diff --git a/sources/TerminalStrip/realterminal.cpp b/sources/TerminalStrip/realterminal.cpp index f4354bc75..8b86dd31f 100644 --- a/sources/TerminalStrip/realterminal.cpp +++ b/sources/TerminalStrip/realterminal.cpp @@ -407,6 +407,57 @@ bool RealTerminal::isShield() const { /** * @brief RealTerminal::cableTarget + * Where the cable of this terminal goes at its far end: the lines to + * write in the box under the cable. What the project itself says when + * a cable lands on a piece (see automaticCableTarget()), and the text + * typed in by hand when none does -- the Target cell of the terminal + * strip editor is read only in the first case, so a target the drawing + * cannot work out is still worth naming by hand. A text typed in is + * written the way the fields are shown in the column, + * "Installation / Location / Label", and gives one line per field. + * @return the lines to write in the box under the cable + */ +QVector RealTerminal::cableTarget() const +{ + const auto automatic_{automaticCableTarget()}; + if (!automatic_.isEmpty()) { + return automatic_; + } + + if (hasData()) + { + const auto typed_{terminalData().m_informations + .value(cableTargetInfoKey()).toString()}; + if (!typed_.isEmpty()) + { + //Written the way the fields are shown in the column, + //"Installation / Location / Label": the box draws one line + //per field, without the separators, like it does for a + //target worked out from the project + QVector lines_; + for (const QString &field_ + : typed_.split(QStringLiteral(" / "), Qt::SkipEmptyParts)) { + lines_ << field_; + } + return lines_; + } + } + return QVector(); +} + +/** + * @brief RealTerminal::cableTargetIsAutomatic + * @return true when the target of this terminal is worked out from the + * project -- one of its cables lands on a piece worth naming -- and + * false when only a text typed in by hand is there to show. + */ +bool RealTerminal::cableTargetIsAutomatic() const +{ + return !automaticCableTarget().isEmpty(); +} + +/** + * @brief RealTerminal::automaticCableTarget * Where the cable of this terminal goes at its far end: the * Installation, Location and Label (BMK) of the first piece the * wires of the cable land on, past the strip's own terminals, in @@ -420,7 +471,7 @@ bool RealTerminal::isShield() const { * its name among the cables of the project. * @return the lines to write in the box under the cable */ -QVector RealTerminal::cableTarget() const +QVector RealTerminal::automaticCableTarget() const { if (m_spare || m_element.isNull()) { return QVector(); diff --git a/sources/TerminalStrip/realterminal.h b/sources/TerminalStrip/realterminal.h index d0c4dcb76..61f44295a 100644 --- a/sources/TerminalStrip/realterminal.h +++ b/sources/TerminalStrip/realterminal.h @@ -55,6 +55,11 @@ class RealTerminal void setPhysicalTerminal(const QSharedPointer &phy_t); + ///Where the wires of a cable land, worked out from the project: + ///empty when no cable lands on a piece worth naming (cableTarget() + ///then falls back on the text typed in by hand). + QVector automaticCableTarget() const; + public: ~RealTerminal(); static QSharedPointer createSpare(const ElementData &data, const QUuid &uuid = QUuid()); @@ -76,6 +81,9 @@ class RealTerminal bool cableIsAutomatic() const; bool isShield() const; QVector cableTarget() const; + ///True when cableTarget() is worked out from the project (read only), + ///false when it is the text typed in by hand (which may be edited) + bool cableTargetIsAutomatic() const; QColor color() const; QColor stripeColor() const; qreal section() const; @@ -86,6 +94,8 @@ class RealTerminal //Keys used to store the cable data in the element informations static QString cableInfoKey() { return QStringLiteral("terminal_cable"); } static QString cableWireInfoKey() { return QStringLiteral("terminal_cable_wire"); } + //Target typed in by hand, only read when the cables give none + static QString cableTargetInfoKey() { return QStringLiteral("terminal_cable_target"); } static QString shieldInfoKey() { return QStringLiteral("terminal_shield"); } //Keys used to store the color and the section (mm2) of the terminal static QString colorInfoKey() { return QStringLiteral("terminal_color"); } diff --git a/sources/TerminalStrip/ui/modelTerminalData.h b/sources/TerminalStrip/ui/modelTerminalData.h index e947d4dc5..0e8778832 100644 --- a/sources/TerminalStrip/ui/modelTerminalData.h +++ b/sources/TerminalStrip/ui/modelTerminalData.h @@ -20,11 +20,30 @@ #include #include +#include #include "../../qetgraphicsitem/element.h" #include "../realterminal.h" struct modelRealTerminalData { + ///The Installation, Location and Label (BMK) of the piece the cable + ///lands on, joined in one line : what the strip draws in the box + ///under its cable (RealTerminal::cableTarget), in the same order. + ///A target typed in by hand is one single line, and nothing is + ///shown when the cable lands on nothing worth naming and no text + ///was ever written in. + static QString targetText(const QVector &target_) + { + QStringList fields_; + for (const QString &field_ : target_) + { + if (!field_.isEmpty()) { + fields_ << field_; + } + } + return fields_.join(QStringLiteral(" / ")); + } + static modelRealTerminalData data(const QSharedPointer &real_t) { modelRealTerminalData mrtd; @@ -35,6 +54,8 @@ struct modelRealTerminalData mrtd.Xref_ = real_t->Xref(); mrtd.cable_ = real_t->cable(); mrtd.cable_wire = real_t->cableWire(); + mrtd.cable_target_ = targetText(real_t->cableTarget()); + mrtd.cable_target_auto_ = real_t->cableTargetIsAutomatic(); mrtd.cable_auto_ = real_t->cableIsAutomatic(); mrtd.conductor_ = real_t->conductor(); mrtd.led_ = real_t->isLed(); @@ -61,6 +82,11 @@ struct modelRealTerminalData QString Xref_; QString cable_; QString cable_wire; + //Where the cable lands, past this strip : Installation / Location / Label + QString cable_target_; + //True when cable_target_ comes from the project (read only), + //false when it is empty or was typed in by hand (editable) + bool cable_target_auto_ = false; bool cable_auto_ = false; //True when cable_ and cable_wire come from the cables of the project QString conductor_; bool led_ = false; diff --git a/sources/TerminalStrip/ui/terminalstripeditor.cpp b/sources/TerminalStrip/ui/terminalstripeditor.cpp index c5ea3f760..9d17884e9 100644 --- a/sources/TerminalStrip/ui/terminalstripeditor.cpp +++ b/sources/TerminalStrip/ui/terminalstripeditor.cpp @@ -249,6 +249,10 @@ void TerminalStripEditor::setCurrentStrip(TerminalStrip *strip_) { m_model = new TerminalStripModel{strip_, this}; ui->m_table_widget->setModel(m_model); + //The Target column is worked out from the drawing: it takes + //the width of its own text, never a cut off Installation + ui->m_table_widget->horizontalHeader()->setSectionResizeMode( + TerminalStripModel::CableTarget, QHeaderView::ResizeToContents); m_model->buildBridgePixmap(setUpBridgeCellWidth()); connect(ui->m_table_widget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &TerminalStripEditor::selectionChanged); } @@ -335,6 +339,12 @@ void TerminalStripEditor::apply() if (!data_.cable_auto_) { set_info(RealTerminal::cableInfoKey(), data_.cable_); set_info(RealTerminal::cableWireInfoKey(), data_.cable_wire); + } + //The same for the target: a target worked out from the + //project is never written in the element, only the one + //typed in by hand is stored + if (!data_.cable_target_auto_) { + set_info(RealTerminal::cableTargetInfoKey(), data_.cable_target_); } 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 @@ -790,7 +800,8 @@ void TerminalStripEditor::on_m_led_cb_activated(int index) namespace { /** * @return true if the cell at @a index contain a free text - * (label, cable, wire of the cable, color, section or width), so a text can be pasted in it. + * (label, cable, wire of the cable, target of the cable, color, section + * or width), so a text can be pasted in it. */ bool cellAcceptText(const QModelIndex &index) { @@ -798,6 +809,7 @@ bool cellAcceptText(const QModelIndex &index) return column_ == TerminalStripModel::Label || column_ == TerminalStripModel::Cable || column_ == TerminalStripModel::CableWire + || column_ == TerminalStripModel::CableTarget || column_ == TerminalStripModel::Color || column_ == TerminalStripModel::Section || column_ == TerminalStripModel::Width; diff --git a/sources/TerminalStrip/ui/terminalstripmodel.cpp b/sources/TerminalStrip/ui/terminalstripmodel.cpp index 6c4274bae..b15be27a9 100644 --- a/sources/TerminalStrip/ui/terminalstripmodel.cpp +++ b/sources/TerminalStrip/ui/terminalstripmodel.cpp @@ -48,16 +48,17 @@ const int CONDUCTOR_CELL = 7; const int XREF_CELL = 8; const int CABLE_CELL = 9; const int CABLE_WIRE_CELL = 10; -const int TYPE_CELL = 11; -const int FUNCTION_CELL = 12; -const int LED_CELL = 13; -const int SHIELD_CELL = 14; -const int COLOR_CELL = 15; -const int SECTION_CELL = 16; -const int WIDTH_CELL = 17; -const int SEPARATOR_CELL = 18; +const int CABLE_TARGET_CELL = 11; +const int TYPE_CELL = 12; +const int FUNCTION_CELL = 13; +const int LED_CELL = 14; +const int SHIELD_CELL = 15; +const int COLOR_CELL = 16; +const int SECTION_CELL = 17; +const int WIDTH_CELL = 18; +const int SEPARATOR_CELL = 19; -const int COLUMN_COUNT = 19; +const int COLUMN_COUNT = 20; static QVector UNMODIFIED_CELL_VECTOR(COLUMN_COUNT, false); @@ -132,14 +133,15 @@ TerminalStripModel::Column TerminalStripModel::columnTypeForIndex(const QModelIn case 8 : return XRef; case 9 : return Cable; case 10 : return CableWire; - case 11 : return Type; - case 12 : return Function; - case 13 : return Led; - case 14 : return Shield; - case 15 : return Color; - case 16 : return Section; - case 17 : return Width; - case 18 : return Separator; + case 11 : return CableTarget; + case 12 : return Type; + case 13 : return Function; + case 14 : return Led; + case 15 : return Shield; + case 16 : return Color; + case 17 : return Section; + case 18 : return Width; + case 19 : return Separator; default : return Invalid; } } @@ -215,6 +217,7 @@ QVariant TerminalStripModel::data(const QModelIndex &index, int role) const case XREF_CELL : return mrtd.spare_ ? tr("Spare") : mrtd.Xref_; case CABLE_CELL : return mrtd.cable_; case CABLE_WIRE_CELL : return mrtd.cable_wire; + case CABLE_TARGET_CELL : return mrtd.cable_target_; case TYPE_CELL : return ElementData::translatedTerminalType(mrtd.type_); case FUNCTION_CELL : return ElementData::translatedTerminalFunction(mrtd.function_); case COLOR_CELL : return TerminalColor::displayName(mrtd.color_, mrtd.stripe_color_); @@ -235,6 +238,7 @@ QVariant TerminalStripModel::data(const QModelIndex &index, int role) const case LABEL_CELL : return mrtd.label_; case CABLE_CELL : return mrtd.cable_; case CABLE_WIRE_CELL : return mrtd.cable_wire; + case CABLE_TARGET_CELL : return mrtd.cable_target_; case COLOR_CELL : return TerminalColor::toSpec(mrtd.color_, mrtd.stripe_color_); case SECTION_CELL : return mrtd.section_ > 0 ? QLocale().toString(mrtd.section_, 'g', 6) : QString(); case WIDTH_CELL : return mrtd.width_mm_ > 0 ? QLocale().toString(mrtd.width_mm_, 'g', 6) : QString(); @@ -309,6 +313,10 @@ bool TerminalStripModel::setData(const QModelIndex &index, const QVariant &value && mrtd.cable_auto_) { return false; } + if (column_ == CABLE_TARGET_CELL + && mrtd.cable_target_auto_) { + return false; + } if (column_ == LEVEL_CELL && role == Qt::EditRole) @@ -361,6 +369,14 @@ bool TerminalStripModel::setData(const QModelIndex &index, const QVariant &value modified_ = true; modified_cell = CABLE_WIRE_CELL; } + else if (column_ == CABLE_TARGET_CELL && + role == Qt::EditRole && + mrtd.cable_target_ != value.toString()) + { + mrtd.cable_target_ = value.toString(); + modified_ = true; + modified_cell = CABLE_TARGET_CELL; + } else if (column_ == SHIELD_CELL) { mrtd.shield_ = value.toBool(); @@ -484,6 +500,7 @@ QVariant TerminalStripModel::headerData(int section, Qt::Orientation orientation case XREF_CELL: return tr("Cross-reference"); case CABLE_CELL: return tr("Cable"); case CABLE_WIRE_CELL: return tr("Color / wire number of the cable"); + case CABLE_TARGET_CELL: return tr("Target"); case TYPE_CELL: return tr("Type"); case FUNCTION_CELL : return tr("Function"); case LED_CELL: return tr("led"); @@ -496,6 +513,13 @@ QVariant TerminalStripModel::headerData(int section, Qt::Orientation orientation } } } + else if (role == Qt::ToolTipRole && + orientation == Qt::Horizontal && + section == CABLE_TARGET_CELL) + { + //Same three fields as the box drawn under the cable + return tr("Installation, Location and Label (BMK) of the piece the cable lands on, past this strip"); + } return QVariant(); } @@ -510,6 +534,12 @@ Qt::ItemFlags TerminalStripModel::flags(const QModelIndex &index) const if ((c == CABLE_CELL || c == CABLE_WIRE_CELL) && !dataAtRow(index.row()).cable_auto_) flags = flags | Qt::ItemIsEditable; + //The target the project works out is read only: only the drawing + //decides it. No target to show, or one typed in by hand, may be + //written -- the same rule as the cable cells. + if (c == CABLE_TARGET_CELL + && !dataAtRow(index.row()).cable_target_auto_) + flags = flags | Qt::ItemIsEditable; if (c == LABEL_CELL || c == TYPE_CELL || c == FUNCTION_CELL || c == COLOR_CELL || c == SECTION_CELL || c == WIDTH_CELL) flags = flags | Qt::ItemIsEditable; diff --git a/sources/TerminalStrip/ui/terminalstripmodel.h b/sources/TerminalStrip/ui/terminalstripmodel.h index 7a95f227a..ac0fc9e7f 100644 --- a/sources/TerminalStrip/ui/terminalstripmodel.h +++ b/sources/TerminalStrip/ui/terminalstripmodel.h @@ -58,14 +58,15 @@ class TerminalStripModel : public QAbstractTableModel XRef = 8, Cable = 9, CableWire = 10, - Type = 11, - Function = 12, - Led = 13, - Shield = 14, - Color = 15, - Section = 16, - Width = 17, - Separator = 18, + CableTarget = 11, + Type = 12, + Function = 13, + Led = 14, + Shield = 15, + Color = 16, + Section = 17, + Width = 18, + Separator = 19, Invalid = 99 }; diff --git a/tests/qttest/tst_cabletarget.cpp b/tests/qttest/tst_cabletarget.cpp index f34dd92f8..1504b4761 100644 --- a/tests/qttest/tst_cabletarget.cpp +++ b/tests/qttest/tst_cabletarget.cpp @@ -50,7 +50,10 @@ Also here: the box at the end of a cable is written at the font of the drawing -- the end zone the settings leave is what gives it the - room, so three lines never end up squeezed into a tiny text. + room, so three lines never end up squeezed into a tiny text. A + target typed in by hand is written the way the fields are shown in + the column, "Installation / Location / Label", and the box draws one + line per field, with no slash in it. Both drawing styles are covered: the schematic one and the realistic one (the strip seen from above), which paint the same @@ -174,6 +177,7 @@ private slots: void paintsWithTargetBox(); void wireLabelStaysOutOfTheTerminal(); void targetBoxKeepsTheDrawingFont(); + void typedTargetIsDrawnInItsOwnLines(); void realisticStyleDrawsTheCable(); }; @@ -520,6 +524,94 @@ void tst_cabletarget::realisticStyleDrawsTheCable() QVERIFY(without_bounds.height() < with_bounds.height()); } +/** + A target typed in by hand is written the way the fields are shown in + the column, "Installation / Location / Label": the box under the + cable draws one line per field, with no slash in it. The picture of + a strip whose cable ends on three fields is therefore that much + deeper than the picture of the same strip with one single field -- + two lines of the font of the drawing. +*/ +void tst_cabletarget::typedTargetIsDrawnInItsOwnLines() +{ + //The cable lands on a piece which names nothing, so there is no + //target of the drawing and the box takes what is typed in + QVERIFY(wireCableToTheContact()); + Element *contact_ = element( + QStringLiteral("{fc9cc920-5822-421a-916e-a17100acdcf6}")); + QVERIFY(contact_); + ElementData contact_data_{contact_->elementData()}; + contact_data_.m_informations.addValue(QETInformation::ELMT_PLANT, QString()); + contact_data_.m_informations.addValue(QETInformation::ELMT_LOCATION, QString()); + contact_data_.m_informations.addValue(QETInformation::ELMT_LABEL, QString()); + contact_->setElementData(contact_data_); + + const auto a_ = terminalInStrip( + QStringLiteral("{891b70f2-2822-41c3-9f94-28efb448b5c4}")); + QVERIFY(a_); + QVERIFY(a_->cableTarget().isEmpty()); + Element *terminal_ = a_->element(); + QVERIFY(terminal_); + + auto pattern_ = QSharedPointer{new TerminalStripLayoutPattern}; + const auto strips_ = m_project->terminalStrip(); + QVERIFY(!strips_.isEmpty()); + TerminalStripDrawer::TerminalStripDrawer drawer{ + QSharedPointer{ + new TerminalStripDrawer::TrueTerminalStrip{strips_.first()}}, + pattern_}; + + const QRectF bounds{drawer.boundingRect()}; + QVERIFY(!bounds.isEmpty()); + + const auto render = [&drawer, &bounds]() { + QImage image{bounds.size().toSize() + QSize{4, 4}, + QImage::Format_ARGB32_Premultiplied}; + image.fill(Qt::transparent); + QPainter painter{&image}; + painter.translate(-bounds.topLeft() + QPointF{2, 2}); + drawer.paint(&painter); + painter.end(); + return image; + }; + const auto lowest_row = [](const QImage &image_) { + qreal lowest{-1}; + for (auto y = 0 ; y < image_.height() ; ++y) + { + for (auto x = 0 ; x < image_.width() ; ++x) + { + if (image_.pixel(x, y) != qRgba(0, 0, 0, 0)) { + lowest = y; + break; + } + } + } + return lowest; + }; + const auto type_in = [terminal_](const QString &text_) { + ElementData data_{terminal_->elementData()}; + data_.m_informations.addValue(RealTerminal::cableTargetInfoKey(), text_); + terminal_->setElementData(data_); + }; + + //One field: the box holds one single line + type_in(QStringLiteral("Boiler room")); + const qreal one_line{lowest_row(render())}; + QVERIFY(one_line >= 0); + + //Three fields: the box is two lines deeper, at the font of the + //drawing -- the slashes are only there to separate the fields in + //the column, the box never draws them + type_in(QStringLiteral("Boiler room / Cabinet 3 / -K3")); + QCOMPARE(a_->cableTarget(), (QVector{QStringLiteral("Boiler room"), + QStringLiteral("Cabinet 3"), + QStringLiteral("-K3")})); + const qreal three_lines{lowest_row(render())}; + const QFontMetricsF metrics{pattern_->font()}; + QVERIFY2(three_lines - one_line > 1.5 * metrics.height(), + "three fields must give a box two lines deeper than one field"); +} + int main(int argc, char **argv) { QStandardPaths::setTestModeEnabled(true); diff --git a/tests/qttest/tst_terminalcableauto.cpp b/tests/qttest/tst_terminalcableauto.cpp index 802068b28..56c764590 100644 --- a/tests/qttest/tst_terminalcableauto.cpp +++ b/tests/qttest/tst_terminalcableauto.cpp @@ -17,6 +17,7 @@ along with QElectroTech. If not, see . */ #include "qetapptest.h" +#include "TerminalStrip/physicalterminal.h" #include "TerminalStrip/realterminal.h" #include "TerminalStrip/terminalstrip.h" #include "TerminalStrip/ui/terminalstripeditor.h" @@ -25,6 +26,7 @@ #include "qetgraphicsitem/conductor.h" #include "qetgraphicsitem/element.h" #include "qetgraphicsitem/terminal.h" +#include "qetinformation.h" #include "qetproject.h" #include @@ -37,6 +39,17 @@ cannot leave a stale value behind. A terminal no cable is wired to keeps the value typed in by hand, cell and all. + The Target column follows the same rule: it names where the cable + lands past this strip (Installation, Location and Label), the very + three fields the strip drawing writes in its box. Worked out from a + cable it is read only -- only the drawing decides it -- and with no + cable landing anywhere it is there to be typed in, just like the + Cable cell, the text then stored in the element and kept until a + cable works a target out again. A text typed in is written the way + the fields are shown in the column, "Installation / Location / + Label", so the box draws one line per field and not one long line + needing the slashes. + fixtures/every_feature.qet has the strip X1 whose two terminals are wired to conductors on the folio, and no cable at all. */ @@ -82,6 +95,37 @@ class tst_terminalcableauto : public QObject return conductors.isEmpty() ? nullptr : conductors.first(); } + // The wire which leaves the strip: one of its ends belongs to a piece + // outside the strip, while the wire joining two terminals of this very + // strip never leaves it -- and never has a target to name. + static Conductor *outgoingConductor(const QSharedPointer &real_t) + { + TerminalStrip *strip = real_t ? real_t->parentStrip() : nullptr; + if (!strip) return nullptr; + + QSet own; + for (const QSharedPointer &physical + : strip->physicalTerminal()) + { + if (!physical) continue; + for (const QSharedPointer &real : physical->realTerminals()) + if (real && real->element()) + own.insert(real->element()->uuid()); + } + + for (Conductor *conductor : conductorsOf(real_t)) + { + if (!conductor) continue; + Element *ends[2] = { + conductor->terminal1 ? conductor->terminal1->parentElement() : nullptr, + conductor->terminal2 ? conductor->terminal2->parentElement() : nullptr }; + for (Element *end : ends) + if (end && !own.contains(end->uuid())) + return conductor; + } + return nullptr; + } + // The cable the create dialog hands over: owned by the project, four // cores, one core wired to the terminal's conductor (or to @p on). static Cable *wireCable(QETProject &project, @@ -264,6 +308,224 @@ private slots: QCOMPARE(real_t->cableWire(), QStringLiteral("bl, bl")); QVERIFY(real_t->cableIsAutomatic()); } + + // The Target column names where the cable lands, past this strip: the + // three fields the strip drawing writes in the box under the cable, + // joined in one line. Worked out from a cable it is read only, there + // is nothing to name without one, and the cell empties again once the + // cable goes. + void targetColumnNamesWhereTheCableLands() + { + 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()); + Conductor *outgoing = outgoingConductor(real_t); + QVERIFY2(outgoing, "the fixture terminal has a wire leaving the strip"); + + Element *target = (outgoing->terminal1 + && outgoing->terminal1->parentElement() == real_t->element()) + ? (outgoing->terminal2 ? outgoing->terminal2->parentElement() : nullptr) + : (outgoing->terminal1 ? outgoing->terminal1->parentElement() : nullptr); + QVERIFY(target); + + //The piece the cable lands on carries an Installation, a Location + //and a Label, the way an element of a real drawing does + ElementData named = target->elementData(); + named.m_informations.addValue(QETInformation::ELMT_PLANT, + QStringLiteral("Plant 7")); + named.m_informations.addValue(QETInformation::ELMT_LOCATION, + QStringLiteral("Cabinet 7")); + named.m_informations.addValue(QETInformation::ELMT_LABEL, + QStringLiteral("-S7")); + target->setElementData(named); + + //No cable yet : nothing to name, so the cell is there to be + //filled in by hand -- the very rule of the Cable cell + TerminalStripModel model_before(project.terminalStrip().first()); + const QModelIndex before_cell = model_before.index( + 0, TerminalStripModel::CableTarget); + QVERIFY(model_before.data(before_cell).toString().isEmpty()); + QVERIFY(model_before.flags(before_cell) & Qt::ItemIsEditable); + + Cable *cable = wireCable(project, real_t, 1, QStringLiteral("15W1"), + outgoing); + + //Row zero is the terminal the cable is wired to + TerminalStripModel model(project.terminalStrip().first()); + QCOMPARE(model.data(model.index(0, TerminalStripModel::Cable)) + .toString(), QStringLiteral("15W1")); + + //The three fields, in the order the box draws them + const QModelIndex target_cell = model.index(0, + TerminalStripModel::CableTarget); + QCOMPARE(model.data(target_cell).toString(), + QStringLiteral("Plant 7 / Cabinet 7 / -S7")); + QVERIFY(!(model.flags(target_cell) & Qt::ItemIsEditable)); + QCOMPARE(model.headerData(TerminalStripModel::CableTarget, + Qt::Horizontal).toString(), + QStringLiteral("Target")); + + //The cable taken away : the cell is empty again, nothing stale + //behind, and it takes an edit once more + QVERIFY(cable); + cable->removeCore(1); + TerminalStripModel reloaded(project.terminalStrip().first()); + const QModelIndex reloaded_cell = reloaded.index( + 0, TerminalStripModel::CableTarget); + QVERIFY(reloaded.data(reloaded_cell).toString().isEmpty()); + QVERIFY(reloaded.flags(reloaded_cell) & Qt::ItemIsEditable); + } + + // A target the drawing cannot work out is typed in by hand, exactly + // like a cable designation: the cell takes the edit, Apply stores the + // text in the element, a fresh model shows it again. A cable landing + // on a named piece then gives the read only target of the drawing and + // the text typed in by hand waits, untouched, until that cable goes. + void targetTypedByHandIsStoredAndKept() + { + 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); + Conductor *outgoing = outgoingConductor(real_t); + QVERIFY2(outgoing, "the fixture terminal has a wire leaving the strip"); + + //The editor takes a target typed in by hand : no cable is wired, + //so there is nothing the drawing could name and the cell is open + TerminalStripEditor editor(&project); + editor.setCurrentStrip(project.terminalStrip().first()); + QAbstractItemView *table = editor.findChild( + QStringLiteral("m_table_widget")); + QVERIFY2(table && table->model(), "the editor has its table"); + QAbstractItemModel *model = table->model(); + const QModelIndex cell = model->index(0, TerminalStripModel::CableTarget); + QVERIFY(model->data(cell).toString().isEmpty()); + QVERIFY(model->flags(cell) & Qt::ItemIsEditable); + + const QString typed{QStringLiteral("Boiler room / Cabinet 3 / -K3")}; + QVERIFY(model->setData(cell, typed, Qt::EditRole)); + QCOMPARE(model->data(cell, Qt::EditRole).toString(), typed); + QCOMPARE(model->data(cell).toString(), typed); + + //Apply writes it in the element, and nowhere else + editor.apply(); + QCOMPARE(element->elementData().m_informations + .value(RealTerminal::cableTargetInfoKey()).toString(), typed); + //Three fields, written the way the column shows them: the box + //draws three lines, without the slashes + QCOMPARE(real_t->cableTarget(), + (QVector{QStringLiteral("Boiler room"), + QStringLiteral("Cabinet 3"), + QStringLiteral("-K3")})); + QVERIFY(!real_t->cableTargetIsAutomatic()); + + //A fresh model shows the text and still lets it be edited + TerminalStripModel model_after(project.terminalStrip().first()); + const QModelIndex after_cell = model_after.index( + 0, TerminalStripModel::CableTarget); + QCOMPARE(model_after.data(after_cell).toString(), typed); + QVERIFY(model_after.flags(after_cell) & Qt::ItemIsEditable); + + //The piece the cable lands on is named now : once the cable is + //wired the drawing gives the target of its own, and that one is + //read only while the text typed in by hand stays in the element + Element *target = (outgoing->terminal1 + && outgoing->terminal1->parentElement() == real_t->element()) + ? (outgoing->terminal2 ? outgoing->terminal2->parentElement() : nullptr) + : (outgoing->terminal1 ? outgoing->terminal1->parentElement() : nullptr); + QVERIFY(target); + ElementData named = target->elementData(); + named.m_informations.addValue(QETInformation::ELMT_PLANT, + QStringLiteral("Plant 9")); + named.m_informations.addValue(QETInformation::ELMT_LOCATION, + QStringLiteral("Cabinet 9")); + named.m_informations.addValue(QETInformation::ELMT_LABEL, + QStringLiteral("-S9")); + target->setElementData(named); + + Cable *cable = wireCable(project, real_t, 1, QStringLiteral("15W1"), + outgoing); + QVERIFY(cable); + QVERIFY(real_t->cableTargetIsAutomatic()); + QCOMPARE(real_t->cableTarget(), + (QVector{QStringLiteral("Plant 9"), + QStringLiteral("Cabinet 9"), + QStringLiteral("-S9")})); + QCOMPARE(element->elementData().m_informations + .value(RealTerminal::cableTargetInfoKey()).toString(), typed); + + TerminalStripModel model_cable(project.terminalStrip().first()); + const QModelIndex cable_cell = model_cable.index( + 0, TerminalStripModel::CableTarget); + QCOMPARE(model_cable.data(cable_cell).toString(), + QStringLiteral("Plant 9 / Cabinet 9 / -S9")); + QVERIFY(!(model_cable.flags(cable_cell) & Qt::ItemIsEditable)); + //And the automatic target was never written in the element + QCOMPARE(element->elementData().m_informations + .value(RealTerminal::cableTargetInfoKey()).toString(), typed); + + //The cable taken away : the text typed in by hand is back, cell + //and all, no stale Plant 9 anywhere + cable->removeCore(1); + QVERIFY(!real_t->cableTargetIsAutomatic()); + QCOMPARE(real_t->cableTarget(), + (QVector{QStringLiteral("Boiler room"), + QStringLiteral("Cabinet 3"), + QStringLiteral("-K3")})); + TerminalStripModel model_back(project.terminalStrip().first()); + const QModelIndex back_cell = model_back.index( + 0, TerminalStripModel::CableTarget); + QCOMPARE(model_back.data(back_cell).toString(), typed); + QVERIFY(model_back.flags(back_cell) & Qt::ItemIsEditable); + } + + // A text typed in by hand is written the way the fields are shown in + // the column, "Installation / Location / Label": the box under the + // cable draws one line per field, without the slashes, exactly like a + // target worked out from the project. + void typedTargetGivesOneLinePerField() + { + 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); + + //One field only: one single line in the box + ElementData typed = element->elementData(); + typed.m_informations.addValue(RealTerminal::cableTargetInfoKey(), + QStringLiteral("Boiler room")); + element->setElementData(typed); + QCOMPARE(real_t->cableTarget(), + QVector{QStringLiteral("Boiler room")}); + + //Three fields: three lines, and no slash anywhere in them + typed = element->elementData(); + typed.m_informations.addValue(RealTerminal::cableTargetInfoKey(), + QStringLiteral("Plant 9 / Cabinet 9 / -S9")); + element->setElementData(typed); + QCOMPARE(real_t->cableTarget(), + (QVector{QStringLiteral("Plant 9"), + QStringLiteral("Cabinet 9"), + QStringLiteral("-S9")})); + //A typed text is never a target of the drawing + QVERIFY(!real_t->cableTargetIsAutomatic()); + + //The column shows the text back the way it was written + TerminalStripModel model(project.terminalStrip().first()); + QCOMPARE(model.data(model.index(0, TerminalStripModel::CableTarget)) + .toString(), QStringLiteral("Plant 9 / Cabinet 9 / -S9")); + } }; QET_APP_TEST_MAIN(tst_terminalcableauto)