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
@@ -27,6 +27,7 @@
#include "../../utils/qetutils.h"
#include "../../qetmessagebox.h"
#include "../../textgrid.h"
#include "../../wiringrules.h"
#include "../../editor/terminalnamecheck.h"
#include "../../ElementsCollection/qetlabelsfile.h"
#include "../prefixconfigurationdialog.h"
@@ -113,6 +114,7 @@ GeneralConfigurationPage::GeneralConfigurationPage(QWidget *parent) :
ui->m_use_windows_mode_rb->setChecked(true);
ui->m_zoom_out_beyond_folio->setChecked(settings.value("diagrameditor/zoom-out-beyond-of-folio", false).toBool());
ui->m_conductor_properties_panel->setChecked(settings.value("diagrameditor/conductor_properties_panel", false).toBool());
ui->m_wiring_rules_cb->setChecked(WiringRules::masterEnabled());
ui->m_use_gesture_trackpad->setChecked(settings.value("diagramview/gestures", false).toBool());
ui->m_save_label_paste->setChecked(settings.value("diagramcommands/erase-label-on-copy", true).toBool());
ui->m_enable_scripting->setChecked(QetSettings::scriptingEnabled());
@@ -334,6 +336,7 @@ void GeneralConfigurationPage::applyConf()
settings.setValue("diagrameditor/highlight-integrated-elements", ui->m_highlight_integrated_elements->isChecked());
settings.setValue("diagrameditor/zoom-out-beyond-of-folio", ui->m_zoom_out_beyond_folio->isChecked());
settings.setValue("diagrameditor/conductor_properties_panel", ui->m_conductor_properties_panel->isChecked());
WiringRules::setMasterEnabled(ui->m_wiring_rules_cb->isChecked());
settings.setValue("diagrameditor/autosave-interval", ui->m_autosave_sb->value());
settings.setValue("diagrameditor/grid_display_startup", ui->grid_startup_cb->isChecked());
@@ -70,6 +70,19 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="m_wiring_rules_cb">
<property name="toolTip">
<string>Décoché, aucune règle de conducteurs par borne n'est appliquée, quels que soient les réglages des projets.</string>
</property>
<property name="text">
<string>Appliquer les règles de conducteurs par borne des projets</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
@@ -1297,6 +1310,7 @@ Vous pouvez spécifier ici la valeur par défaut de ce champ pour les éléments
<tabstop>m_use_gesture_trackpad</tabstop>
<tabstop>m_zoom_out_beyond_folio</tabstop>
<tabstop>m_conductor_properties_panel</tabstop>
<tabstop>m_wiring_rules_cb</tabstop>
<tabstop>m_use_windows_mode_rb</tabstop>
<tabstop>m_use_tab_mode_rb</tabstop>
<tabstop>m_save_label_paste</tabstop>
@@ -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);
}
//######################################################################################//
@@ -24,6 +24,8 @@ class QComboBox;
class QLineEdit;
class QCheckBox;
class QPushButton;
class QSpinBox;
class QGroupBox;
class QETProject;
class BorderPropertiesWidget;
class ConductorPropertiesWidget;
@@ -128,6 +130,11 @@ class ProjectMainConfigPage : public ProjectConfigPage {
QPushButton *usage_reset_pb_;
QLabel *wire_hops_label_;
QComboBox *wire_hops_cb_;
QGroupBox *wiring_rules_gb_;
QLabel *max_wires_label_;
QSpinBox *max_wires_sb_;
QCheckBox *one_wire_per_report_cb_;
QLabel *wiring_rules_off_label_;
};
class ProjectAutoNumConfigPage : public ProjectConfigPage {