diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp
index 4a20fd926..e8ce5998d 100644
--- a/sources/qetproject.cpp
+++ b/sources/qetproject.cpp
@@ -1260,16 +1260,27 @@ void QETProject::setWireHops(WireHops::Mode mode)
/**
@brief QETProject::wiringRules
@return how many wires a terminal of this project may take
- (discussion #1158); the default when the project sets no rule.
+ (discussion #1158): the project's own rules when it sets them,
+ otherwise the application's (Settings > General).
*/
WiringRules::Settings QETProject::wiringRules() const {
+ return WiringRules::effective(m_wiring_rules, WiringRules::applicationSettings());
+}
+
+/**
+ @brief QETProject::projectWiringRules
+ @return the rules as the project stores them: Settings::own false when
+ it follows the application's.
+*/
+WiringRules::Settings QETProject::projectWiringRules() const {
return m_wiring_rules;
}
/**
@brief QETProject::setWiringRules
- Set how many wires a terminal of this project may take. Only wires
- drawn from now on are affected: none already drawn is removed.
+ Set the project's own wiring rules, or (Settings::own false) make it
+ follow the application's. Only wires drawn from now on are affected:
+ none already drawn is removed.
@param rules
*/
void QETProject::setWiringRules(const WiringRules::Settings &rules)
diff --git a/sources/qetproject.h b/sources/qetproject.h
index 2f713919e..90fcbd0fb 100644
--- a/sources/qetproject.h
+++ b/sources/qetproject.h
@@ -213,6 +213,7 @@ class QETProject : public QObject
WireHops::Mode wireHops() const;
void setWireHops(WireHops::Mode mode);
WiringRules::Settings wiringRules() const;
+ WiringRules::Settings projectWiringRules() const;
void setWiringRules(const WiringRules::Settings &rules);
void setAutoBreakConductor (bool abc);
void setAutoElement (bool ae);
diff --git a/sources/ui/configpage/generalconfigurationpage.cpp b/sources/ui/configpage/generalconfigurationpage.cpp
index 7a6293b81..aa6e63989 100644
--- a/sources/ui/configpage/generalconfigurationpage.cpp
+++ b/sources/ui/configpage/generalconfigurationpage.cpp
@@ -115,6 +115,19 @@ GeneralConfigurationPage::GeneralConfigurationPage(QWidget *parent) :
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());
+ {
+ //The rules every project follows unless it sets its own (#1158)
+ const WiringRules::Settings rules = WiringRules::applicationSettings();
+ ui->m_wiring_max_wires_sb->setValue(rules.max_wires);
+ ui->m_wiring_one_wire_per_report_cb->setChecked(rules.one_wire_per_report);
+ auto enable = [this](bool on) {
+ ui->m_wiring_max_wires_label->setEnabled(on);
+ ui->m_wiring_max_wires_sb->setEnabled(on);
+ ui->m_wiring_one_wire_per_report_cb->setEnabled(on);
+ };
+ enable(ui->m_wiring_rules_cb->isChecked());
+ connect(ui->m_wiring_rules_cb, &QCheckBox::toggled, this, enable);
+ }
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());
@@ -337,6 +350,12 @@ void GeneralConfigurationPage::applyConf()
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());
+ {
+ WiringRules::Settings rules = WiringRules::applicationSettings();
+ rules.max_wires = ui->m_wiring_max_wires_sb->value();
+ rules.one_wire_per_report = ui->m_wiring_one_wire_per_report_cb->isChecked();
+ WiringRules::setApplicationSettings(rules);
+ }
settings.setValue("diagrameditor/autosave-interval", ui->m_autosave_sb->value());
settings.setValue("diagrameditor/grid_display_startup", ui->grid_startup_cb->isChecked());
diff --git a/sources/ui/configpage/generalconfigurationpage.ui b/sources/ui/configpage/generalconfigurationpage.ui
index adf7e3a21..1120faa20 100644
--- a/sources/ui/configpage/generalconfigurationpage.ui
+++ b/sources/ui/configpage/generalconfigurationpage.ui
@@ -83,6 +83,57 @@
+ -
+
+
+ 20
+
+
-
+
+
+ Nombre maximal de conducteurs par borne :
+
+
+
+ -
+
+
+ Pour tous les projets, sauf ceux qui ont leurs propres réglages. 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.
+
+
+ Sans limite
+
+
+ 99
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+
+ -
+
+
+ 20
+
+
-
+
+
+ Un renvoi de folio est un point virtuel : il ne reçoit qu'un conducteur, celui qui continue sur l'autre folio.
+
+
+ Un seul conducteur par renvoi de folio
+
+
+
+
+
-
@@ -1311,6 +1362,8 @@ Vous pouvez spécifier ici la valeur par défaut de ce champ pour les éléments
m_zoom_out_beyond_folio
m_conductor_properties_panel
m_wiring_rules_cb
+ m_wiring_max_wires_sb
+ m_wiring_one_wire_per_report_cb
m_use_windows_mode_rb
m_use_tab_mode_rb
m_save_label_paste
diff --git a/sources/ui/configpage/projectconfigpages.cpp b/sources/ui/configpage/projectconfigpages.cpp
index d1ac9520d..4531ffcea 100644
--- a/sources/ui/configpage/projectconfigpages.cpp
+++ b/sources/ui/configpage/projectconfigpages.cpp
@@ -177,10 +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) {
+ //Following the application's rules stores nothing in the project
+ WiringRules::Settings wiring_rules;
+ if (!use_application_rules_cb_ -> isChecked()) {
+ wiring_rules.own = true;
+ wiring_rules.max_wires = max_wires_sb_ -> value();
+ wiring_rules.one_wire_per_report = one_wire_per_report_cb_ -> isChecked();
+ }
+ if (m_project -> projectWiringRules() != wiring_rules) {
m_project -> setWiringRules(wiring_rules);
modified_project = true;
}
@@ -237,6 +241,20 @@ void ProjectMainConfigPage::initWidgets()
//How many wires a terminal may take (discussion #1158)
wiring_rules_gb_ = new QGroupBox(tr("Conducteurs par borne", "group box title"));
+ use_application_rules_cb_ = new QCheckBox(tr("Utiliser les réglages de l'application", "checkbox label"));
+ use_application_rules_cb_ -> setToolTip(tr("Les réglages de Configurer QElectroTech > Général s'appliquent. "
+ "Décochez pour donner à ce projet ses propres réglages, enregistrés dans le projet.",
+ "tooltip"));
+ connect(use_application_rules_cb_, &QCheckBox::toggled, this, [this](bool use) {
+ //Show the values that will apply: the application's, or the
+ //application's as a starting point for the project's own
+ if (use) {
+ const WiringRules::Settings application = WiringRules::applicationSettings();
+ max_wires_sb_ -> setValue(application.max_wires);
+ one_wire_per_report_cb_ -> setChecked(application.one_wire_per_report);
+ }
+ updateWiringRulesWidgets();
+ });
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);
@@ -288,6 +306,7 @@ void ProjectMainConfigPage::initLayout()
main_layout0 -> addSpacing(10);
QVBoxLayout *wiring_rules_layout = new QVBoxLayout(wiring_rules_gb_);
+ wiring_rules_layout -> addWidget(use_application_rules_cb_);
QHBoxLayout *max_wires_layout = new QHBoxLayout();
max_wires_layout -> addWidget(max_wires_label_);
max_wires_layout -> addWidget(max_wires_sb_);
@@ -320,16 +339,33 @@ 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));
+ //The rules that apply: the project's own or the application's
const WiringRules::Settings wiring_rules = m_project -> wiringRules();
+ {
+ const QSignalBlocker blocker(use_application_rules_cb_);
+ use_application_rules_cb_ -> setChecked(!wiring_rules.own);
+ }
max_wires_sb_ -> setValue(wiring_rules.max_wires);
one_wire_per_report_cb_ -> setChecked(wiring_rules.one_wire_per_report);
+ updateWiringRulesWidgets();
+}
+
+/**
+ @brief ProjectMainConfigPage::updateWiringRulesWidgets
+ Enable the wiring rule fields only when they can change something: the
+ master switch is on and the project sets its own rules.
+*/
+void ProjectMainConfigPage::updateWiringRulesWidgets()
+{
//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();
+ const bool own = master && !use_application_rules_cb_ -> isChecked();
wiring_rules_off_label_ -> setVisible(!master);
- max_wires_label_ -> setEnabled(master);
- max_wires_sb_ -> setEnabled(master);
- one_wire_per_report_cb_ -> setEnabled(master);
+ use_application_rules_cb_ -> setEnabled(master);
+ max_wires_label_ -> setEnabled(own);
+ max_wires_sb_ -> setEnabled(own);
+ one_wire_per_report_cb_ -> setEnabled(own);
}
/**
diff --git a/sources/ui/configpage/projectconfigpages.h b/sources/ui/configpage/projectconfigpages.h
index 0868815d1..e911b5a43 100644
--- a/sources/ui/configpage/projectconfigpages.h
+++ b/sources/ui/configpage/projectconfigpages.h
@@ -116,6 +116,7 @@ class ProjectMainConfigPage : public ProjectConfigPage {
private slots:
void resetUsageTracker();
+ void updateWiringRulesWidgets();
// attributes
protected:
@@ -131,6 +132,7 @@ class ProjectMainConfigPage : public ProjectConfigPage {
QLabel *wire_hops_label_;
QComboBox *wire_hops_cb_;
QGroupBox *wiring_rules_gb_;
+ QCheckBox *use_application_rules_cb_;
QLabel *max_wires_label_;
QSpinBox *max_wires_sb_;
QCheckBox *one_wire_per_report_cb_;
diff --git a/sources/wiringrules.cpp b/sources/wiringrules.cpp
index 45e347813..16a46918e 100644
--- a/sources/wiringrules.cpp
+++ b/sources/wiringrules.cpp
@@ -30,6 +30,9 @@ namespace {
//The master switch is read on every wire drawn, so it is read from
//the settings once and kept; setMasterEnabled() keeps both in step.
int master_cache = -1;
+ //Same for the application's rules
+ bool application_cached = false;
+ WiringRules::Settings application_cache;
}
/**
@@ -57,11 +60,56 @@ void WiringRules::setMasterEnabled(bool enabled)
master_cache = enabled ? 1 : 0;
}
+/**
+ @brief WiringRules::applicationSettings
+ @return the rules set for every project in Settings > General; a
+ project that sets its own (Settings::own) uses those instead.
+*/
+WiringRules::Settings WiringRules::applicationSettings()
+{
+ if (!application_cached) {
+ const QSettings settings;
+ application_cache = Settings();
+ application_cache.max_wires = qMax(0, settings.value(max_wires_key, 0).toInt());
+ application_cache.one_wire_per_report = settings.value(one_wire_per_report_key, false).toBool();
+ application_cached = true;
+ }
+ return application_cache;
+}
+
+/**
+ @brief WiringRules::setApplicationSettings
+ Save the rules set for every project.
+ @param settings
+*/
+void WiringRules::setApplicationSettings(const Settings &settings)
+{
+ QSettings qsettings;
+ qsettings.setValue(max_wires_key, qMax(0, settings.max_wires));
+ qsettings.setValue(one_wire_per_report_key, settings.one_wire_per_report);
+ application_cache = settings;
+ application_cache.own = false;
+ application_cached = true;
+}
+
+/**
+ @brief WiringRules::effective
+ @return the rules that apply to a project: its own when it sets them,
+ otherwise the application's.
+*/
+WiringRules::Settings WiringRules::effective(const Settings &project, const Settings &application)
+{
+ Settings result = project.own ? project : application;
+ result.own = project.own;
+ return result;
+}
+
/**
@brief WiringRules::fromXml
@param project_root : the root element of a project
@return the rules stored in the child of \a project_root,
- the default (no rule) when there is none.
+ which the project then uses instead of the application's (Settings::own);
+ the default, which follows the application, when there is none.
*/
WiringRules::Settings WiringRules::fromXml(const QDomElement &project_root)
{
@@ -70,6 +118,7 @@ WiringRules::Settings WiringRules::fromXml(const QDomElement &project_root)
if (rules.isNull()) {
return settings;
}
+ settings.own = true;
settings.max_wires = qMax(0, rules.attribute(max_wires_attribute, QStringLiteral("0")).toInt());
settings.one_wire_per_report = rules.attribute(report_attribute) == QLatin1String("true");
return settings;
@@ -77,13 +126,13 @@ WiringRules::Settings WiringRules::fromXml(const QDomElement &project_root)
/**
@brief WiringRules::toXml
- Write \a settings as a child of \a project_root. Nothing
- is written when no rule is on, so a project that never used them saves
- exactly as before.
+ Write \a settings as a child of \a project_root, only
+ when the project sets its own rules: a project that follows the
+ application's saves exactly as before.
*/
void WiringRules::toXml(const Settings &settings, QDomElement &project_root)
{
- if (settings.isDefault()) {
+ if (!settings.own) {
return;
}
QDomElement rules = project_root.ownerDocument().createElement(element_name);
diff --git a/sources/wiringrules.h b/sources/wiringrules.h
index 50842dfe2..b92eaec72 100644
--- a/sources/wiringrules.h
+++ b/sources/wiringrules.h
@@ -32,7 +32,10 @@ class QDomElement;
limit, a new wire that would go past it is refused. Wires already in
the drawing are never touched.
- Every rule is off unless the project turns it on, and the application
+ The rules are set once for the application (Settings > General,
+ applicationSettings()) and apply to every project; a project can set
+ its own instead (Settings::own), saved in the project so it travels
+ with the drawing. Every rule is off by default, and the application
has a master switch (masterEnabled()) that turns all of them off in
every project at once. Kept free of any QGraphicsItem so the rules can
be tested on their own.
@@ -41,10 +44,17 @@ namespace WiringRules
{
///The QSettings key of the master switch
constexpr const char *settings_key = "diagrameditor/wiring_rules_enabled";
+ ///The QSettings keys of the application's rules
+ constexpr const char *max_wires_key = "diagrameditor/wiring_rules_max_wires";
+ constexpr const char *one_wire_per_report_key = "diagrameditor/wiring_rules_one_wire_per_report";
- ///The rules a project chose. The default is "no rule".
+ ///A set of rules: the application's, or a project's. The default is
+ ///"no rule".
struct Settings
{
+ ///For a project: it uses these values rather than the
+ ///application's. Ignored in the application's own settings.
+ bool own = false;
///Most wires a terminal may take, 0 for no limit
int max_wires = 0;
///A folio report takes one wire only
@@ -52,7 +62,8 @@ namespace WiringRules
bool isDefault() const { return *this == Settings(); }
bool operator==(const Settings &other) const {
- return max_wires == other.max_wires
+ return own == other.own
+ && max_wires == other.max_wires
&& one_wire_per_report == other.one_wire_per_report;
}
bool operator!=(const Settings &other) const { return !(*this == other); }
@@ -61,6 +72,10 @@ namespace WiringRules
bool masterEnabled();
void setMasterEnabled(bool enabled);
+ Settings applicationSettings();
+ void setApplicationSettings(const Settings &settings);
+ Settings effective(const Settings &project, const Settings &application);
+
Settings fromXml(const QDomElement &project_root);
void toXml(const Settings &settings, QDomElement &project_root);
diff --git a/tests/qttest/tst_wiringrules.cpp b/tests/qttest/tst_wiringrules.cpp
index 683d3fd10..8c70bf10a 100644
--- a/tests/qttest/tst_wiringrules.cpp
+++ b/tests/qttest/tst_wiringrules.cpp
@@ -23,8 +23,9 @@ class tst_wiringrules : public QObject
int m_run = 0;
// Environment of one sandboxed run; @p master_off writes the master
- // switch off into that run's own settings.
- QProcessEnvironment sandbox(bool master_off = false)
+ // switch off into that run's own settings, @p application_max_wires
+ // the limit every project follows unless it sets its own.
+ QProcessEnvironment sandbox(bool master_off = false, int application_max_wires = 0)
{
const QString home = m_dir.filePath(QStringLiteral("home%1").arg(m_run));
const QString tmp = m_dir.filePath(QStringLiteral("tmp%1").arg(m_run));
@@ -33,10 +34,12 @@ class tst_wiringrules : public QObject
QDir().mkpath(home);
QDir().mkpath(tmp);
QDir().mkpath(settings + QStringLiteral("/QElectroTech"));
- if (master_off) {
+ if (master_off || application_max_wires) {
QFile ini(settings + QStringLiteral("/QElectroTech/QElectroTech.ini"));
if (ini.open(QIODevice::WriteOnly | QIODevice::Text))
- ini.write("[diagrameditor]\nwiring_rules_enabled=false\n");
+ ini.write(QStringLiteral("[diagrameditor]\nwiring_rules_enabled=%1\nwiring_rules_max_wires=%2\n")
+ .arg(master_off ? QStringLiteral("false") : QStringLiteral("true"))
+ .arg(application_max_wires).toUtf8());
}
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
@@ -85,7 +88,8 @@ class tst_wiringrules : public QObject
// Wires a free terminal to a terminal that already has a wire, through
// qet.addConductor(); returns what the script printed after "PROBE ".
- QString addWireToWiredTerminal(const QString &project, bool master_off)
+ QString addWireToWiredTerminal(const QString &project, bool master_off,
+ int application_max_wires = 0)
{
const QString script_path = m_dir.filePath(QStringLiteral("probe%1.js").arg(m_run));
QFile script(script_path);
@@ -109,7 +113,7 @@ class tst_wiringrules : public QObject
script.close();
QProcess proc;
- proc.setProcessEnvironment(sandbox(master_off));
+ proc.setProcessEnvironment(sandbox(master_off, application_max_wires));
proc.start(QStringLiteral(QET_TEST_BINARY_PATH), {QStringLiteral("--run"), script_path, project});
if (!proc.waitForFinished(60000))
return {};
@@ -236,18 +240,52 @@ private slots:
QCOMPARE(order, QList({0, 1, 2, 3, 4}));
}
+ void projectOverridesApplication()
+ {
+ WiringRules::Settings application;
+ application.max_wires = 2;
+ application.one_wire_per_report = true;
+
+ // A project that follows the application gets its rules
+ WiringRules::Settings follows;
+ QCOMPARE(WiringRules::effective(follows, application).max_wires, 2);
+ QVERIFY(WiringRules::effective(follows, application).one_wire_per_report);
+ QVERIFY(!WiringRules::effective(follows, application).own);
+
+ // Its own rules win, "no limit" included
+ WiringRules::Settings own;
+ own.own = true;
+ QCOMPARE(WiringRules::effective(own, application).max_wires, 0);
+ QVERIFY(!WiringRules::effective(own, application).one_wire_per_report);
+ QVERIFY(WiringRules::effective(own, application).own);
+ own.max_wires = 6;
+ QCOMPARE(WiringRules::effective(own, application).max_wires, 6);
+ }
+
void xmlRoundTrip()
{
QDomDocument doc;
QDomElement root = doc.createElement(QStringLiteral("project"));
doc.appendChild(root);
- // No rule: nothing written, and reading nothing gives no rule
- WiringRules::toXml(WiringRules::Settings(), root);
+ // Following the application: nothing written, and reading
+ // nothing follows the application
+ WiringRules::Settings follows;
+ follows.max_wires = 3; // not the project's: not written
+ WiringRules::toXml(follows, root);
QVERIFY(root.firstChildElement().isNull());
QVERIFY(WiringRules::fromXml(root).isDefault());
+ // Its own rules, even "none", are written and read back as its own
+ WiringRules::Settings none;
+ none.own = true;
+ WiringRules::toXml(none, root);
+ QCOMPARE(root.firstChildElement().tagName(), QStringLiteral("wiring_rules"));
+ QVERIFY(WiringRules::fromXml(root) == none);
+ root.removeChild(root.firstChildElement());
+
WiringRules::Settings rules;
+ rules.own = true;
rules.max_wires = 2;
rules.one_wire_per_report = true;
WiringRules::toXml(rules, root);
@@ -269,6 +307,14 @@ private slots:
QVERIFY2(saved.contains(QLatin1String("max_wires_per_terminal=\"2\""))
&& saved.contains(QLatin1String("one_wire_per_report=\"true\"")),
"the setting was lost on save");
+
+ // A project's own "no rule" is kept too: it overrides the application's
+ const QString own_none = fixtureWith(QStringLiteral(""));
+ QVERIFY(!own_none.isEmpty());
+ const QString saved_none = resave(own_none);
+ QVERIFY2(!saved_none.isEmpty(), "--resave failed");
+ QVERIFY2(saved_none.contains(QLatin1String("")),
+ "a project's own rules were lost on save");
}
void wirePastTheLimitIsRefused()
@@ -285,6 +331,16 @@ private slots:
// The master switch off: the project's rule does nothing
QCOMPARE(addWireToWiredTerminal(limited, true), QStringLiteral("true"));
+
+ // The application's limit applies to a project that sets none...
+ const QString plain = QFINDTESTDATA("fixtures/qet_bug_repro_resaved.qet");
+ QCOMPARE(addWireToWiredTerminal(plain, false, 1), QStringLiteral("false"));
+ // ...not to one that sets its own, here "no limit"
+ const QString own_none = fixtureWith(QStringLiteral(""));
+ QVERIFY(!own_none.isEmpty());
+ QCOMPARE(addWireToWiredTerminal(own_none, false, 1), QStringLiteral("true"));
+ // ...and the master switch still turns it off
+ QCOMPARE(addWireToWiredTerminal(plain, true, 1), QStringLiteral("true"));
}
void deletingASymbolChainsTheWires()