From 9e37fa2ca7e0fbf1c615644312a134f0baafe047 Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Mon, 19 Oct 2026 18:40:56 +0200 Subject: [PATCH 1/3] Terminal strip editor: cable cells worked out from the project cables The Cable and Color / wire number columns now show what the cables of the project say (designation and core colour or number), worked out from the conductors wired to the terminal, instead of only the values typed in by hand (patch by Laurent); Values typed in by hand are still shown, and the cells still editable, when no cable of the project is wired to the terminal; A value worked out from a cable is never written back into the element, so a cable renamed or removed cannot leave a stale value behind. --- sources/TerminalStrip/realterminal.cpp | 82 ++++++++++++++++++- sources/TerminalStrip/realterminal.h | 1 + sources/TerminalStrip/ui/modelTerminalData.h | 2 + .../TerminalStrip/ui/terminalstripeditor.cpp | 9 +- .../TerminalStrip/ui/terminalstripmodel.cpp | 6 +- 5 files changed, 95 insertions(+), 5 deletions(-) diff --git a/sources/TerminalStrip/realterminal.cpp b/sources/TerminalStrip/realterminal.cpp index 3ec7ad462..95647961f 100644 --- a/sources/TerminalStrip/realterminal.cpp +++ b/sources/TerminalStrip/realterminal.cpp @@ -23,6 +23,50 @@ #include "terminalcatalog.h" #include "../qetgraphicsitem/conductor.h" #include "../qetinformation.h" +#include "../qetgraphicsitem/terminal.h" +#include "../diagram.h" +#include "../cable/cable.h" +#include "../cable/cablemanager.h" + +namespace +{ + /** + * The cable cores wired to one terminal element, worked out from + * the cables of the project (PR #1438). A core knows its + * conductor, and CableManager::refreshLabels() writes the cable + * and the core into the properties of that conductor, so reading + * the conductors of the element is enough. + * Each pair is (cable designation, core colour or number). + */ + QList> wiredCores(Element *element) + { + QList> cores; + if (!element || !element->diagram()) { + return cores; + } + const auto project = element->diagram()->project(); + for (const auto terminal : element->terminals()) + { + for (const auto conductor : terminal->conductors()) + { + const auto properties = conductor->ownProperties(); + const auto cable = CableManager::cableByUuid(project, properties.m_cable_uuid); + if (!cable || cable->designation().isEmpty()) { + continue; + } + auto wire = cable->colorOfCore(properties.m_cable_slot); + if (wire.isEmpty() && properties.m_cable_slot >= 0) { + wire = QString::number(properties.m_cable_slot + 1); + } + const QPair core(cable->designation(), wire); + if (!cores.contains(core)) { + cores.append(core); + } + } + } + return cores; + } +} /** * @brief RealTerminal @@ -291,18 +335,52 @@ QString RealTerminal::Xref() const * @return The name of the cable (hose) connected to this terminal. * The value is stored in the informations of the element. */ -QString RealTerminal::cable() const { +QString RealTerminal::cable() const +{ + const auto cores = wiredCores(m_element.data()); + if (!cores.isEmpty()) + { + QStringList cables; + for (const auto &core : cores) { + if (!cables.contains(core.first)) { + cables.append(core.first); + } + } + return cables.join(QStringLiteral(", ")); + } + if (hasData()) { return terminalData().m_informations.value(cableInfoKey()).toString(); } return QString(); } +/** + * @brief RealTerminal::cableIsAutomatic + * @return true when the cable and the core of this terminal come from + * a cable of the project wired to it, false when they are the values + * typed in by the user in the terminal strip editor. + */ +bool RealTerminal::cableIsAutomatic() const { + return !wiredCores(m_element.data()).isEmpty(); +} + /** * @brief RealTerminal::cableWire * @return The color / number of the wire of the cable, connected to this terminal. */ -QString RealTerminal::cableWire() const { +QString RealTerminal::cableWire() const +{ + const auto cores = wiredCores(m_element.data()); + if (!cores.isEmpty()) + { + QStringList wires; + for (const auto &core : cores) { + wires.append(core.second); + } + return wires.join(QStringLiteral(", ")); + } + if (hasData()) { return terminalData().m_informations.value(cableWireInfoKey()).toString(); } diff --git a/sources/TerminalStrip/realterminal.h b/sources/TerminalStrip/realterminal.h index bf8d39bbd..a5c89fd3d 100644 --- a/sources/TerminalStrip/realterminal.h +++ b/sources/TerminalStrip/realterminal.h @@ -73,6 +73,7 @@ class RealTerminal QString Xref() const; QString cable() const; QString cableWire() const; + bool cableIsAutomatic() const; bool isShield() const; QColor color() const; QColor stripeColor() const; diff --git a/sources/TerminalStrip/ui/modelTerminalData.h b/sources/TerminalStrip/ui/modelTerminalData.h index 2921b33af..e947d4dc5 100644 --- a/sources/TerminalStrip/ui/modelTerminalData.h +++ b/sources/TerminalStrip/ui/modelTerminalData.h @@ -35,6 +35,7 @@ struct modelRealTerminalData mrtd.Xref_ = real_t->Xref(); mrtd.cable_ = real_t->cable(); mrtd.cable_wire = real_t->cableWire(); + mrtd.cable_auto_ = real_t->cableIsAutomatic(); mrtd.conductor_ = real_t->conductor(); mrtd.led_ = real_t->isLed(); mrtd.shield_ = real_t->isShield(); @@ -60,6 +61,7 @@ struct modelRealTerminalData QString Xref_; QString cable_; QString cable_wire; + bool cable_auto_ = false; //True when cable_ and cable_wire come from the cables of the project QString conductor_; bool led_ = false; bool shield_ = false; diff --git a/sources/TerminalStrip/ui/terminalstripeditor.cpp b/sources/TerminalStrip/ui/terminalstripeditor.cpp index 3d748fc69..56016fb0b 100644 --- a/sources/TerminalStrip/ui/terminalstripeditor.cpp +++ b/sources/TerminalStrip/ui/terminalstripeditor.cpp @@ -245,8 +245,13 @@ void TerminalStripEditor::apply() current_data.m_informations.addValue(key, value); } }; - set_info(RealTerminal::cableInfoKey(), data_.cable_); - set_info(RealTerminal::cableWireInfoKey(), data_.cable_wire); + //A cable worked out from the cables of the project is + //never written in the element: it would stay there, + //out of date, once the cable is renamed or removed + if (!data_.cable_auto_) { + set_info(RealTerminal::cableInfoKey(), data_.cable_); + set_info(RealTerminal::cableWireInfoKey(), data_.cable_wire); + } set_info(RealTerminal::shieldInfoKey(), data_.shield_ ? QStringLiteral("true") : QString()); //The color and the section are stored with the dot as decimal separator, whatever the locale set_info(RealTerminal::colorInfoKey(), TerminalColor::toSpec(data_.color_, data_.stripe_color_)); diff --git a/sources/TerminalStrip/ui/terminalstripmodel.cpp b/sources/TerminalStrip/ui/terminalstripmodel.cpp index 4dfdb51e4..6ecadcd61 100644 --- a/sources/TerminalStrip/ui/terminalstripmodel.cpp +++ b/sources/TerminalStrip/ui/terminalstripmodel.cpp @@ -497,8 +497,12 @@ Qt::ItemFlags TerminalStripModel::flags(const QModelIndex &index) const Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; auto c = index.column(); + //The cable cells are only edited by hand when no cable + //of the project is wired to the terminal + if ((c == CABLE_CELL || c == CABLE_WIRE_CELL) + && !dataAtRow(index.row()).cable_auto_) + flags = flags | Qt::ItemIsEditable; if (c == LABEL_CELL || c == TYPE_CELL || c == FUNCTION_CELL - || c == CABLE_CELL || c == CABLE_WIRE_CELL || c == COLOR_CELL || c == SECTION_CELL || c == WIDTH_CELL) flags = flags | Qt::ItemIsEditable; if (c == LED_CELL || c == SHIELD_CELL || c == SEPARATOR_CELL) { From 94a929c8c00ff68dbef84f36848352bd80357d44 Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Mon, 19 Oct 2026 18:49:36 +0200 Subject: [PATCH 2/3] Add tst_terminalcableauto: the terminal strip cable cells from a cable Three cases against fixtures/every_feature.qet (strip X1, wired terminals, no cable): a wired terminal shows cable and core, the cells go read only and nothing is written into the element; a core without a colour is shown as its number; a value typed in by hand stays when no cable is wired and comes back unchanged once the cable is gone. --- tests/qttest/CMakeLists.txt | 5 + tests/qttest/tst_terminalcableauto.cpp | 201 +++++++++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 tests/qttest/tst_terminalcableauto.cpp diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index 644cf4725..ca8b577ec 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -290,6 +290,11 @@ qet_add_app_test(tst_assistantinfopaths tst_assistantinfopaths.cpp) # %T in a report or cross reference formula is the target folio's title. qet_add_app_test(tst_reportfoliotitle tst_reportfoliotitle.cpp) +# The cable cells of a terminal strip are worked out from the cables of +# the project: wired shows the cable and its core read only, unwired keeps +# what was typed in, and nothing stale is written into the element. +qet_add_app_test(tst_terminalcableauto tst_terminalcableauto.cpp) + # Escape during a picture's resize, rotate or pivot drag puts it back, keeps # it selected and records no undo step (#1414); the key goes to the view. qet_add_app_test(tst_imagehandleescape tst_imagehandleescape.cpp) diff --git a/tests/qttest/tst_terminalcableauto.cpp b/tests/qttest/tst_terminalcableauto.cpp new file mode 100644 index 000000000..c82a4c8cd --- /dev/null +++ b/tests/qttest/tst_terminalcableauto.cpp @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#include "qetapptest.h" +#include "TerminalStrip/realterminal.h" +#include "TerminalStrip/terminalstrip.h" +#include "TerminalStrip/ui/terminalstripmodel.h" +#include "cable/cable.h" +#include "qetgraphicsitem/conductor.h" +#include "qetgraphicsitem/element.h" +#include "qetgraphicsitem/terminal.h" +#include "qetproject.h" + +/** + The Cable and Color / wire number columns of the terminal strip editor + are worked out from the cables of the project: a terminal with a cable + wired to it shows that cable and its core, the cell is read only, and + nothing is written into the element -- so a cable renamed or removed + cannot leave a stale value behind. A terminal no cable is wired to + keeps the value typed in by hand, cell and all. + + fixtures/every_feature.qet has the strip X1 whose two terminals are + wired to conductors on the folio, and no cable at all. +*/ +class tst_terminalcableauto : public QObject +{ + Q_OBJECT + + // The wired terminal of strip X1: its element carries a conductor. + static QSharedPointer wiredTerminal(QETProject &project) + { + const QVector strips = project.terminalStrip(); + if (strips.isEmpty()) return {}; + for (const QSharedPointer &real_t + : strips.first()->realTerminals()) + { + Element *element = real_t ? real_t->element() : nullptr; + if (!element) continue; + for (Terminal *terminal : element->terminals()) + if (terminal && !terminal->conductors().isEmpty()) + return real_t; + } + return {}; + } + + static Conductor *conductorOf(const QSharedPointer &real_t) + { + Element *element = real_t->element(); + if (!element) return nullptr; + for (Terminal *terminal : element->terminals()) { + if (!terminal) continue; + const QList conductors = terminal->conductors(); + if (!conductors.isEmpty()) return conductors.first(); + } + return nullptr; + } + + // The cable the create dialog hands over: owned by the project, four + // cores, core 1 (blue) wired to the terminal's conductor. + static Cable *wireCable(QETProject &project, + const QSharedPointer &real_t, + int core = 1) + { + Cable *cable = project.newCable(); + cable->setDesignation(QStringLiteral("15W1")); + cable->setCoreCount(4); + cable->setCoreColors({QStringLiteral("bn"), QStringLiteral("bl"), + QStringLiteral("gn/ye"), QStringLiteral("gy")}); + CableCore assignment; + assignment.core = core; + assignment.conductor = conductorOf(real_t)->uuid(); + cable->setCore(assignment); + return cable; + } + +private slots: + void wiredCableIsWorkedOutAndReadsOnly() + { + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QVERIFY2(!fixture.isEmpty(), "fixture project not found"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + const auto real_t = wiredTerminal(project); + QVERIFY2(!real_t.isNull(), "the fixture has a wired terminal"); + Element *element = real_t->element(); + + //Without a cable, the cells show nothing and may be typed in + QVERIFY(!real_t->cableIsAutomatic()); + TerminalStripModel model_before(project.terminalStrip().first()); + QVERIFY(model_before.rowCount() > 0); + QVERIFY(model_before.flags(model_before.index(0, TerminalStripModel::Cable)) + & Qt::ItemIsEditable); + + wireCable(project, real_t); + + //The terminal itself reads the cable and its core + QCOMPARE(real_t->cable(), QStringLiteral("15W1")); + QVERIFY(real_t->cableIsAutomatic()); + QCOMPARE(real_t->cableWire(), QStringLiteral("bl")); + + //The automatic value is never written into the element: nothing + //stays behind once the cable is renamed or removed + QVERIFY(element); + QVERIFY(element->elementData().m_informations + .value(RealTerminal::cableInfoKey()).toString().isEmpty()); + + //The model shows both cells and keeps them out of the edits + TerminalStripModel model(project.terminalStrip().first()); + QCOMPARE(model.rowCount(), model_before.rowCount()); + const QModelIndex cable_cell = model.index(0, TerminalStripModel::Cable); + const QModelIndex wire_cell = model.index(0, TerminalStripModel::CableWire); + QCOMPARE(model.data(cable_cell).toString(), QStringLiteral("15W1")); + QCOMPARE(model.data(wire_cell).toString(), QStringLiteral("bl")); + QVERIFY(!(model.flags(cable_cell) & Qt::ItemIsEditable)); + QVERIFY(!(model.flags(wire_cell) & Qt::ItemIsEditable)); + //The other cells go on being editable + QVERIFY(model.flags(model.index(0, TerminalStripModel::Label)) + & Qt::ItemIsEditable); + } + + void coreWithoutColorIsShownAsItsNumber() + { + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + const auto real_t = wiredTerminal(project); + QVERIFY(!real_t.isNull()); + + Cable *cable = project.newCable(); + cable->setDesignation(QStringLiteral("15W1")); + cable->setCoreCount(4); + //A type file which gives no colours: the number of the core is shown + CableCore assignment; + assignment.core = 1; + assignment.conductor = conductorOf(real_t)->uuid(); + cable->setCore(assignment); + + QCOMPARE(real_t->cable(), QStringLiteral("15W1")); + QCOMPARE(real_t->cableWire(), QStringLiteral("2")); + QVERIFY(real_t->cableIsAutomatic()); + } + + void typedValueStaysWhenNoCableIsThere() + { + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + const auto real_t = wiredTerminal(project); + QVERIFY(!real_t.isNull()); + Element *element = real_t->element(); + QVERIFY(element); + + //A cable designation typed in by hand in the editor + ElementData typed = element->elementData(); + typed.m_informations.addValue(RealTerminal::cableInfoKey(), + QStringLiteral("K9")); + element->setElementData(typed); + QCOMPARE(real_t->cable(), QStringLiteral("K9")); + QVERIFY(!real_t->cableIsAutomatic()); + + //Wired to a cable: the cable is shown, the typed value is set aside + Cable *cable = wireCable(project, real_t); + QCOMPARE(real_t->cable(), QStringLiteral("15W1")); + QVERIFY(real_t->cableIsAutomatic()); + QCOMPARE(real_t->cableWire(), QStringLiteral("bl")); + + //Cable removed: the typed value is back, no stale 15W1 anywhere + cable->removeCore(1); + QCOMPARE(real_t->cable(), QStringLiteral("K9")); + QVERIFY(!real_t->cableIsAutomatic()); + QVERIFY(element->elementData().m_informations + .value(RealTerminal::cableInfoKey()) + .toString() == QStringLiteral("K9")); + + TerminalStripModel model(project.terminalStrip().first()); + QVERIFY(model.flags(model.index(0, TerminalStripModel::Cable)) + & Qt::ItemIsEditable); + } +}; + +QET_APP_TEST_MAIN(tst_terminalcableauto) + +#include "tst_terminalcableauto.moc" From bab2707fff07a6e09840504b0a66e1bdb5f82b13 Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Mon, 19 Oct 2026 19:09:50 +0200 Subject: [PATCH 3/3] Terminal strip editor: the cable cells follow the cables The window stays open while the drawing is wired, so the table takes up what the cables say on its own: each cable of the project is watched, its change reloads the table after CableManager has worked the labels out again, and a cable taken away empties the cells right away. A reload never costs the user his own work: cell edits not applied yet are kept, and a cell being typed in is left alone. A cell worked out from a cable refuses to be written through setData(), so the apply button, the paste and the deletion of texts leave it alone too. --- .../TerminalStrip/ui/terminalstripeditor.cpp | 84 +++++++++++++++++ .../TerminalStrip/ui/terminalstripeditor.h | 7 ++ .../TerminalStrip/ui/terminalstripmodel.cpp | 8 ++ tests/qttest/tst_terminalcableauto.cpp | 90 ++++++++++++++++--- 4 files changed, 179 insertions(+), 10 deletions(-) diff --git a/sources/TerminalStrip/ui/terminalstripeditor.cpp b/sources/TerminalStrip/ui/terminalstripeditor.cpp index 56016fb0b..c5ea3f760 100644 --- a/sources/TerminalStrip/ui/terminalstripeditor.cpp +++ b/sources/TerminalStrip/ui/terminalstripeditor.cpp @@ -35,6 +35,7 @@ #include "../terminalcatalog.h" #include "../realterminal.h" #include "../../qetinformation.h" +#include "../../cable/cable.h" #include #include @@ -95,6 +96,10 @@ TerminalStripEditor::TerminalStripEditor(QETProject *project, QWidget *parent) : } } }); + + //The cable cells are worked out from the cables of the project, + //so the table follows them while the user wires the drawing + watchCableChanges(); } /** @@ -107,9 +112,88 @@ TerminalStripEditor::~TerminalStripEditor() { void TerminalStripEditor::setProject(QETProject *project) { m_project = project; + watchCableChanges(); setCurrentStrip(nullptr); } +/** + * @brief TerminalStripEditor::watchCableChanges + * Follow the cables of the project: the cable cells are worked out from + * them, so a cable renamed, given cores or taken away changes what the + * cells say, and the window stays open while the user wires the drawing. + * The project the editor followed before is let go first, since the + * window may be shown on another project. + */ +void TerminalStripEditor::watchCableChanges() +{ + if (m_watched_project) { + //Only the connections made here go to the project + m_watched_project->disconnect(nullptr, this); + for (Cable *cable : m_watched_project->cables()) { + cable->disconnect(nullptr, this); + } + } + m_watched_project = m_project; + if (!m_project) { + return; + } + + connect(m_project, &QETProject::cableAdded, this, [this](Cable *cable) { + watchCable(cable); + }); + connect(m_project, &QETProject::cableRemoved, this, [this](Cable *cable) { + //The project stopped feeding its labels already, so the + //cells show what is left: the typed in value, or nothing + if (cable) { + cable->disconnect(nullptr, this); + } + reloadOnCableChange(); + }); + for (Cable *cable : m_project->cables()) { + watchCable(cable); + } +} + +/** + * @brief TerminalStripEditor::watchCable + * Reload the table when @a cable changes. The project runs its own + * connection first (made when the cable entered it), so the labels of + * the conductors are worked out again before this reload reads them. + * @param cable + */ +void TerminalStripEditor::watchCable(Cable *cable) +{ + if (!cable) { + return; + } + cable->disconnect(nullptr, this); + connect(cable, &Cable::changed, this, &TerminalStripEditor::reloadOnCableChange); +} + +/** + * @brief TerminalStripEditor::reloadOnCableChange + * Take up a change of the cables: reload so the cable cells say what + * they now must, never at the price of the user's own work -- cell + * edits not applied yet, or a cell being typed in, are kept and catch + * up with the next reload of any kind. + */ +void TerminalStripEditor::reloadOnCableChange() +{ + if (!m_current_strip || !m_model) { + return; + } + if (!m_model->modifiedmodelRealTerminalData().isEmpty()) { + return; + } + //A cell being typed in keeps its editor: the reload would close + //it under the fingers of the user (the edit itself is picked up + //by the next reload once it has been made) + if (ui->m_table_widget->isAncestorOf(QApplication::focusWidget())) { + return; + } + reload(); +} + /** * @brief TerminalStripEditor::setCurrentStrip * Set the current terminal strip edited to \p strip_ diff --git a/sources/TerminalStrip/ui/terminalstripeditor.h b/sources/TerminalStrip/ui/terminalstripeditor.h index 4a38d0e34..8ba7a878e 100644 --- a/sources/TerminalStrip/ui/terminalstripeditor.h +++ b/sources/TerminalStrip/ui/terminalstripeditor.h @@ -27,6 +27,7 @@ namespace Ui { } class QETProject; +class Cable; class TerminalStrip; /** @@ -58,6 +59,9 @@ class TerminalStripEditor : public QWidget void spanMultiLevelTerminals(); void selectionChanged(); QSize setUpBridgeCellWidth(); + void watchCableChanges(); + void watchCable(Cable *cable); + void reloadOnCableChange(); TerminalStripModel::Column isSingleColumnSelected() const; QPair> singleColumnData() const; @@ -79,6 +83,9 @@ class TerminalStripEditor : public QWidget private: Ui::TerminalStripEditor *ui; QPointer m_project; + //The project whose cables this editor follows, which is not + //always m_project yet when watchCableChanges() runs again + QPointer m_watched_project; QPointer m_current_strip; TerminalStripModel *m_model {nullptr}; }; diff --git a/sources/TerminalStrip/ui/terminalstripmodel.cpp b/sources/TerminalStrip/ui/terminalstripmodel.cpp index 6ecadcd61..6c4274bae 100644 --- a/sources/TerminalStrip/ui/terminalstripmodel.cpp +++ b/sources/TerminalStrip/ui/terminalstripmodel.cpp @@ -302,6 +302,14 @@ bool TerminalStripModel::setData(const QModelIndex &index, const QVariant &value int modified_cell = -1; auto column_ = index.column(); + //A cell worked out from the cables of the project is read only: + //the element never holds that value, so writing one in would only + //show something the next reload takes away again + if ((column_ == CABLE_CELL || column_ == CABLE_WIRE_CELL) + && mrtd.cable_auto_) { + return false; + } + if (column_ == LEVEL_CELL && role == Qt::EditRole) { diff --git a/tests/qttest/tst_terminalcableauto.cpp b/tests/qttest/tst_terminalcableauto.cpp index c82a4c8cd..802068b28 100644 --- a/tests/qttest/tst_terminalcableauto.cpp +++ b/tests/qttest/tst_terminalcableauto.cpp @@ -19,6 +19,7 @@ #include "qetapptest.h" #include "TerminalStrip/realterminal.h" #include "TerminalStrip/terminalstrip.h" +#include "TerminalStrip/ui/terminalstripeditor.h" #include "TerminalStrip/ui/terminalstripmodel.h" #include "cable/cable.h" #include "qetgraphicsitem/conductor.h" @@ -26,6 +27,8 @@ #include "qetgraphicsitem/terminal.h" #include "qetproject.h" +#include + /** The Cable and Color / wire number columns of the terminal strip editor are worked out from the cables of the project: a terminal with a cable @@ -58,32 +61,44 @@ class tst_terminalcableauto : public QObject return {}; } - static Conductor *conductorOf(const QSharedPointer &real_t) + static QList conductorsOf(const QSharedPointer &real_t) { - Element *element = real_t->element(); - if (!element) return nullptr; + QList conductors; + Element *element = real_t ? real_t->element() : nullptr; + if (!element) return conductors; for (Terminal *terminal : element->terminals()) { if (!terminal) continue; - const QList conductors = terminal->conductors(); - if (!conductors.isEmpty()) return conductors.first(); + for (Conductor *conductor : terminal->conductors()) { + if (conductor && !conductors.contains(conductor)) + conductors.append(conductor); + } } - return nullptr; + return conductors; + } + + static Conductor *conductorOf(const QSharedPointer &real_t) + { + const QList conductors = conductorsOf(real_t); + return conductors.isEmpty() ? nullptr : conductors.first(); } // The cable the create dialog hands over: owned by the project, four - // cores, core 1 (blue) wired to the terminal's conductor. + // cores, one core wired to the terminal's conductor (or to @p on). static Cable *wireCable(QETProject &project, const QSharedPointer &real_t, - int core = 1) + int core = 1, + const QString &designation = QStringLiteral("15W1"), + Conductor *on = nullptr) { + if (!on) on = conductorOf(real_t); Cable *cable = project.newCable(); - cable->setDesignation(QStringLiteral("15W1")); + cable->setDesignation(designation); cable->setCoreCount(4); cable->setCoreColors({QStringLiteral("bn"), QStringLiteral("bl"), QStringLiteral("gn/ye"), QStringLiteral("gy")}); CableCore assignment; assignment.core = core; - assignment.conductor = conductorOf(real_t)->uuid(); + assignment.conductor = on->uuid(); cable->setCore(assignment); return cable; } @@ -194,6 +209,61 @@ private slots: QVERIFY(model.flags(model.index(0, TerminalStripModel::Cable)) & Qt::ItemIsEditable); } + + // The window stays open while the user wires the drawing: the cells + // follow a cable wired, renamed or taken away without any reload by + // hand. + void openEditorFollowsCableChanges() + { + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + const auto real_t = wiredTerminal(project); + QVERIFY(!real_t.isNull()); + + TerminalStripEditor editor(&project); + editor.setCurrentStrip(project.terminalStrip().first()); + const QAbstractItemView *table = editor.findChild( + QStringLiteral("m_table_widget")); + QVERIFY2(table && table->model(), "the editor has its table"); + const QAbstractItemModel *model = table->model(); + const QModelIndex cable_cell = model->index(0, TerminalStripModel::Cable); + QVERIFY(model->data(cable_cell).toString().isEmpty()); + + //A cable wired to the terminal fills the cell in + Cable *cable = wireCable(project, real_t); + QCOMPARE(model->data(cable_cell).toString(), QStringLiteral("15W1")); + + //Renaming it follows at once, the drawing is not stale behind + cable->setDesignation(QStringLiteral("16W1")); + QCOMPARE(model->data(cable_cell).toString(), QStringLiteral("16W1")); + + //Taking it away empties the cell: nothing stale stays behind + QVERIFY(project.removeCable(cable)); + QVERIFY(model->data(cable_cell).toString().isEmpty()); + } + + // A terminal wired to two cables shows them both, comma separated. + void twoCablesOnOneTerminalAreBothShown() + { + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + const auto real_t = wiredTerminal(project); + QVERIFY(!real_t.isNull()); + const QList conductors = conductorsOf(real_t); + QVERIFY2(conductors.size() >= 2, + "the fixture terminal carries two wires"); + + wireCable(project, real_t, 1, QStringLiteral("15W1"), conductors.at(0)); + wireCable(project, real_t, 1, QStringLiteral("16W1"), conductors.at(1)); + + QCOMPARE(real_t->cable(), QStringLiteral("15W1, 16W1")); + QCOMPARE(real_t->cableWire(), QStringLiteral("bl, bl")); + QVERIFY(real_t->cableIsAutomatic()); + } }; QET_APP_TEST_MAIN(tst_terminalcableauto)