Add a wires-per-terminal limit, with an application master switch

Discussion #1158: a real terminal takes one or two wires (four with
double ferrules), a folio report is a virtual point that carries one, but
QElectroTech lets any number of wires be connected anywhere.

Project properties > General gets a "Conducteurs par borne" group:
- the most wires a terminal may take (0, the default, is no limit);
- "one wire per folio report".
When set, a new wire that would go past the limit is refused: the
terminal shows red while dragging, as for any refused link, and a tooltip
says why on release. Auto-connect on placing, moving or multi-pasting a
symbol skips full terminals, and qet.addConductor() refuses with its own
message. Wires already drawn, opening, pasting and undo are never
refused.

Settings > General gets a master switch, on by default, that turns every
rule of this feature off in every project; the project group is greyed
with a note while it is off.

The rules live in wiringrules.cpp. The project setting is saved as
<wiring_rules .../> only when a rule is on, so a project that never used
it saves exactly as before; an older version drops it, which means "off".

tst_wiringrules covers the rules, the save round trip and, through the
real binary, that a wire past the limit is refused unless the master
switch is off (checked to fail with the limit check removed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
This commit is contained in:
ispyisail
2026-10-03 21:42:12 +13:00
parent 6144da0f91
commit 20e134f2bd
15 changed files with 615 additions and 3 deletions
@@ -26,6 +26,7 @@
#include "../qet.h"
#include "../qeticons.h"
#include "../qetproject.h"
#include "../wiringrules.h"
#include "../borderpropertieswidget.h"
#include "../conductorpropertieswidget.h"
#include "../diagramcontextwidget.h"
@@ -176,6 +177,14 @@ void ProjectMainConfigPage::applyProjectConf()
modified_project = true;
}
WiringRules::Settings wiring_rules = m_project -> wiringRules();
wiring_rules.max_wires = max_wires_sb_ -> value();
wiring_rules.one_wire_per_report = one_wire_per_report_cb_ -> isChecked();
if (m_project -> wiringRules() != wiring_rules) {
m_project -> setWiringRules(wiring_rules);
modified_project = true;
}
if (modified_project) {
m_project -> setModified(true);
}
@@ -225,6 +234,25 @@ void ProjectMainConfigPage::initWidgets()
wire_hops_cb_ -> setToolTip(tr("Dessine un petit arc là où deux conducteurs se croisent sans être reliés. "
"Seul le dessin change : aucun élément n'est ajouté et aucun conducteur n'est coupé.",
"tooltip"));
//How many wires a terminal may take (discussion #1158)
wiring_rules_gb_ = new QGroupBox(tr("Conducteurs par borne", "group box title"));
max_wires_label_ = new QLabel(tr("Nombre maximal de conducteurs par borne :", "label when configuring"));
max_wires_sb_ = new QSpinBox();
max_wires_sb_ -> setRange(0, 99);
max_wires_sb_ -> setSpecialValueText(tr("Sans limite", "wires per terminal"));
max_wires_sb_ -> setToolTip(tr("Un nouveau conducteur qui dépasserait ce nombre sur une borne est refusé. "
"Les conducteurs déjà dessinés ne sont pas modifiés. "
"4 correspond à deux embouts doubles, un de chaque côté de la vis.",
"tooltip"));
one_wire_per_report_cb_ = new QCheckBox(tr("Un seul conducteur par renvoi de folio", "checkbox label"));
one_wire_per_report_cb_ -> setToolTip(tr("Un renvoi de folio est un point virtuel : il ne reçoit qu'un conducteur, "
"celui qui continue sur l'autre folio.",
"tooltip"));
wiring_rules_off_label_ = new QLabel(tr("Ces règles sont désactivées pour tous les projets "
"(Configurer QElectroTech > Général).",
"informative label"));
wiring_rules_off_label_ -> setWordWrap(true);
}
/**
@@ -257,6 +285,17 @@ void ProjectMainConfigPage::initLayout()
wire_hops_layout0 -> addWidget(wire_hops_cb_);
wire_hops_layout0 -> addStretch();
main_layout0 -> addLayout(wire_hops_layout0);
main_layout0 -> addSpacing(10);
QVBoxLayout *wiring_rules_layout = new QVBoxLayout(wiring_rules_gb_);
QHBoxLayout *max_wires_layout = new QHBoxLayout();
max_wires_layout -> addWidget(max_wires_label_);
max_wires_layout -> addWidget(max_wires_sb_);
max_wires_layout -> addStretch();
wiring_rules_layout -> addLayout(max_wires_layout);
wiring_rules_layout -> addWidget(one_wire_per_report_cb_);
wiring_rules_layout -> addWidget(wiring_rules_off_label_);
main_layout0 -> addWidget(wiring_rules_gb_);
setLayout(main_layout0);
this -> setMinimumWidth(680);
@@ -280,6 +319,17 @@ void ProjectMainConfigPage::readValuesFromProject()
const int wire_hops_index = wire_hops_cb_ -> findData(WireHops::toString(m_project -> wireHops()));
wire_hops_cb_ -> setCurrentIndex(qMax(0, wire_hops_index));
const WiringRules::Settings wiring_rules = m_project -> wiringRules();
max_wires_sb_ -> setValue(wiring_rules.max_wires);
one_wire_per_report_cb_ -> setChecked(wiring_rules.one_wire_per_report);
//The master switch wins over every project: say so rather than
//let the user set a rule that does nothing.
const bool master = WiringRules::masterEnabled();
wiring_rules_off_label_ -> setVisible(!master);
max_wires_label_ -> setEnabled(master);
max_wires_sb_ -> setEnabled(master);
one_wire_per_report_cb_ -> setEnabled(master);
}
/**
@@ -307,6 +357,7 @@ void ProjectMainConfigPage::adjustReadOnly()
usage_enabled_cb_ -> setDisabled(is_read_only);
usage_reset_pb_ -> setDisabled(is_read_only);
wire_hops_cb_ -> setDisabled(is_read_only);
wiring_rules_gb_ -> setDisabled(is_read_only);
}
//######################################################################################//