diff --git a/lang/qet_de.ts b/lang/qet_de.ts index 747912959..f63cd5473 100644 --- a/lang/qet_de.ts +++ b/lang/qet_de.ts @@ -6942,6 +6942,36 @@ Texte und Zahlen sind ebenso erlaubt. Use gestures touchpad Gesten vom Touchpad aktivieren + + + Display of the terminal labelling + Darstellung der Klemmenbeschriftung + + + + A terminal's label (X1:1) is drawn as a whole, the way it has always been. + Die BMK einer Klemme (X1:1) wird ganz gezeichnet, wie bisher. + + + + Show the label and the terminal number in one field + BMK (Label) und Anschlussnummer in einem Feld anzeigen + + + + The label (X1) is drawn in one field and the terminal number (1) in another, which a terminal symbol may place where it wants -- and hide when it must. A symbol without such a field shows the two, one under the other. + Die BMK (X1) wird in einem Feld und die Anschlussnummer (1) in einem weiteren Feld gezeichnet, das ein Klemmensymbol platzieren kann, wo es will -- und ausblenden, wenn es muss. Ein Symbol ohne ein solches Feld zeigt beides untereinander. + + + + Split the label and the terminal number + BMK und Anschlussnummer auftrennen + + + + Only the drawing changes: what is written in the element, the numbering, the sorting and the exports keep the whole label. + Es ändert sich nur die Darstellung: Was im Bauteil steht, die Nummerierung, die Sortierung und die Exporte behalten das ganze Label. + Use sheet numbers instead of their position in the project @@ -16677,6 +16707,11 @@ Möchten Sie sie ersetzen? Label Betriebsmittelkennzeichen (BMK) + + + Terminal number + Anschlussnummer + PLC Table diff --git a/sources/qetgraphicsitem/dynamicelementtextitem.cpp b/sources/qetgraphicsitem/dynamicelementtextitem.cpp index e07d72294..ffb1535dd 100644 --- a/sources/qetgraphicsitem/dynamicelementtextitem.cpp +++ b/sources/qetgraphicsitem/dynamicelementtextitem.cpp @@ -46,6 +46,20 @@ #include #include +namespace { + /** + A composite text which uses the terminal number follows the label + as closely as one which uses %{label}: both are drawn from the + label of the element, and from the formula that label is made of. + */ + bool usesLabelVariables(const QString &composite_text) + { + return composite_text.contains(QLatin1String("%{label}")) + || composite_text.contains( + QStringLiteral("%{") + QETInformation::ELMT_TERMINAL_NUMBER + QLatin1String("}")); + } +} + void DynamicElementTextItem::keyPressEvent(QKeyEvent *event) { if (!event->matches(QKeySequence::Paste) || m_text_from != UserText || @@ -401,8 +415,10 @@ Element *DynamicElementTextItem::elementUseForInfo() const */ void DynamicElementTextItem::refreshLabelConnection() { - if ((m_text_from == ElementInfo && m_info_name == "label") || - (m_text_from == CompositeText && m_composite_text.contains("%{label}"))) + if ((m_text_from == ElementInfo && + (m_info_name == QLatin1String("label") + || m_info_name == QETInformation::ELMT_TERMINAL_NUMBER)) || + (m_text_from == CompositeText && usesLabelVariables(m_composite_text))) { if((m_parent_element.data()->linkType() & Element::AllReport) || m_parent_element.data()->linkType() == Element::ConductorDefinition) @@ -450,26 +466,34 @@ void DynamicElementTextItem::setTextFrom(DynamicElementTextItem::TextFrom text_f } else if (m_text_from == ElementInfo && elementUseForInfo()) { - if(m_info_name == "label") + if(m_info_name == QLatin1String("label") + || m_info_name == QETInformation::ELMT_TERMINAL_NUMBER) { setupFormulaConnection(); updateLabel(); } else - setPlainText(elementUseForInfo()->elementInformations().value(m_info_name).toString()); + setPlainText(elementUseForInfo()->infoForDisplay(m_info_name)); if(old_text_from == UserText) connect(elementUseForInfo(), &Element::elementInfoChange, this, &DynamicElementTextItem::elementInfoChanged); } else if (m_text_from == CompositeText && elementUseForInfo()) { - if(m_composite_text.contains("%{label}")) + if(usesLabelVariables(m_composite_text)) { setupFormulaConnection(); updateLabel(); } else - setPlainText(autonum::AssignVariables::replaceVariable(m_composite_text, elementUseForInfo()->elementInformations())); + { + //The label and the terminal number are drawn the way the + //settings say, every other information as it is stored + DiagramContext dc = elementUseForInfo()->elementInformations(); + dc.addValue(QETInformation::ELMT_LABEL, elementUseForInfo()->labelForDisplay()); + dc.addValue(QETInformation::ELMT_TERMINAL_NUMBER, elementUseForInfo()->terminalNumberForDisplay()); + setPlainText(autonum::AssignVariables::replaceVariable(m_composite_text, dc)); + } if(old_text_from == UserText) connect(elementUseForInfo(), &Element::elementInfoChange, this, &DynamicElementTextItem::elementInfoChanged); @@ -478,7 +502,16 @@ void DynamicElementTextItem::setTextFrom(DynamicElementTextItem::TextFrom text_f if(m_parent_element.data()->linkType() == Element::Master || m_parent_element.data()->linkType() == Element::Slave) updateXref(); - + + //A field which becomes, or stops being, the field for the + //terminal number changes what the label of the symbol leaves + //to it (@sa Element::labelForDisplay) + if (m_info_name == QETInformation::ELMT_TERMINAL_NUMBER + && (m_text_from == ElementInfo || old_text_from == ElementInfo) + && parentElement()) { + parentElement()->refreshLabelFields(); + } + emit textFromChanged(m_text_from); } @@ -515,7 +548,8 @@ void DynamicElementTextItem::setInfoName(const QString &info_name) m_info_name = info_name; - if(old_info_name == "label") + if(old_info_name == QLatin1String("label") + || old_info_name == QETInformation::ELMT_TERMINAL_NUMBER) { clearFormulaConnection(); updateXref(); @@ -538,14 +572,25 @@ void DynamicElementTextItem::setInfoName(const QString &info_name) conductorPropertiesChanged(); } } - else if (m_info_name == "label" && elementUseForInfo()) + else if ((m_info_name == QLatin1String("label") + || m_info_name == QETInformation::ELMT_TERMINAL_NUMBER) + && elementUseForInfo()) { setupFormulaConnection(); updateLabel(); updateXref(); } else if(elementUseForInfo() && m_text_from == DynamicElementTextItem::ElementInfo) { - setPlainText(elementUseForInfo()->elementInformations().value(info_name).toString()); + setPlainText(elementUseForInfo()->infoForDisplay(info_name)); + } + + //A field which becomes, or stops being, the field for the + //terminal number changes what the label of the symbol leaves + //to it (@sa Element::labelForDisplay) + if (parentElement() + && (info_name == QETInformation::ELMT_TERMINAL_NUMBER + || old_info_name == QETInformation::ELMT_TERMINAL_NUMBER)) { + parentElement()->refreshLabelFields(); } emit infoNameChanged(info_name); @@ -1174,6 +1219,12 @@ void DynamicElementTextItem::elementInfoChanged() Element *element = elementUseForInfo(); if(element) { dc = element->elementInformations(); + //The label and the terminal number are drawn the way the + //settings say (@sa QetSettings::terminalLabelSplit): a + //composite text which uses them must see the same values the + //fields bound to them show + dc.addValue(QETInformation::ELMT_LABEL, element->labelForDisplay()); + dc.addValue(QETInformation::ELMT_TERMINAL_NUMBER, element->terminalNumberForDisplay()); } QString final_text; @@ -1183,12 +1234,13 @@ void DynamicElementTextItem::elementInfoChanged() { //If the info is the label, then we must make some connection //if the label is created from a formula - if(m_info_name == "label") + if(m_info_name == QLatin1String("label") + || m_info_name == QETInformation::ELMT_TERMINAL_NUMBER) { setupFormulaConnection(); if (element) { - final_text = element->actualLabel(); + final_text = element->infoForDisplay(m_info_name); } } else { @@ -1199,7 +1251,7 @@ void DynamicElementTextItem::elementInfoChanged() { //If the composite have the label variable, we must make some //connection if the label is created from a formula - if (m_composite_text.contains("%{label}")) + if (usesLabelVariables(m_composite_text)) setupFormulaConnection(); //A report's %{label} is where its partner is, not its own @@ -1367,8 +1419,10 @@ void DynamicElementTextItem::removeConnectionForReportFormula(const QString &for */ void DynamicElementTextItem::setupFormulaConnection() { - if ((m_text_from == ElementInfo && m_info_name == "label") || - (m_text_from == CompositeText && m_composite_text.contains("%{label}"))) + if ((m_text_from == ElementInfo && + (m_info_name == QLatin1String("label") + || m_info_name == QETInformation::ELMT_TERMINAL_NUMBER)) || + (m_text_from == CompositeText && usesLabelVariables(m_composite_text))) { clearFormulaConnection(); @@ -1458,8 +1512,10 @@ void DynamicElementTextItem::updateReportText() */ void DynamicElementTextItem::updateLabel() { - if ((m_text_from == ElementInfo && m_info_name == "label") || - (m_text_from == CompositeText && m_composite_text.contains("%{label}"))) + if ((m_text_from == ElementInfo && + (m_info_name == QLatin1String("label") + || m_info_name == QETInformation::ELMT_TERMINAL_NUMBER)) || + (m_text_from == CompositeText && usesLabelVariables(m_composite_text))) { DiagramContext dc; Element *element = elementUseForInfo(); @@ -1469,19 +1525,21 @@ void DynamicElementTextItem::updateLabel() if(m_text_from == ElementInfo && element) { - QString new_label = element->actualLabel(); + QString new_label = element->infoForDisplay(m_info_name); if (toPlainText() != new_label) { setPlainText(new_label); } } else if (m_text_from == CompositeText) { - // Use actualLabel() to ensure %{label} reflects the current - // resolved label (e.g. after a folio/page-number change). + // Use the label as it must be drawn, so %{label} and + // %{terminal_number} reflect the current resolved label (e.g. + // after a folio/page-number change). // A contact not linked to a coil has no element to read from // (bugtracker #345): %{label} then shows empty, as it did // before actualLabel() was used here. if (element) { - dc.addValue(QStringLiteral("label"), element->actualLabel()); + dc.addValue(QETInformation::ELMT_LABEL, element->labelForDisplay()); + dc.addValue(QETInformation::ELMT_TERMINAL_NUMBER, element->terminalNumberForDisplay()); } setPlainText(autonum::AssignVariables::replaceVariable(m_composite_text, dc)); } diff --git a/sources/qetgraphicsitem/element.cpp b/sources/qetgraphicsitem/element.cpp index 8c4676e79..4c22353d4 100644 --- a/sources/qetgraphicsitem/element.cpp +++ b/sources/qetgraphicsitem/element.cpp @@ -33,6 +33,7 @@ #include "../properties/terminaldata.h" #include "../properties/xrefproperties.h" #include "../qetinformation.h" +#include "../utils/qetsettings.h" #include "../qetgraphicsitem/conductor.h" #include "../qetgraphicsitem/terminal.h" #include "../ui/elementpropertieswidget.h" @@ -1689,6 +1690,13 @@ void Element::addDynamicTextItem(DynamicElementTextItem *deti) m_dynamic_text_list.append(deti); + //A field of its own for the terminal number is what the label + //leaves the number to: the fields which draw the label follow + //when such a field lands on the symbol + if (deti->infoName() == QETInformation::ELMT_TERMINAL_NUMBER) { + refreshLabelFields(); + } + //On a mirrored element, the mirror of the text depends on the size //and the rotation of the text, see keepReadable() connect(deti->document()->documentLayout(), @@ -1724,6 +1732,9 @@ void Element::removeDynamicTextItem(DynamicElementTextItem *deti) m_dynamic_text_list.removeOne(deti); deti->setParentItem(nullptr); forget(); + if (deti->infoName() == QETInformation::ELMT_TERMINAL_NUMBER) { + refreshLabelFields(); + } emit textRemoved(deti); return; } @@ -1736,12 +1747,36 @@ void Element::removeDynamicTextItem(DynamicElementTextItem *deti) m_dynamic_text_list.removeOne(deti); deti->setParentItem(nullptr); forget(); + if (deti->infoName() == QETInformation::ELMT_TERMINAL_NUMBER) { + refreshLabelFields(); + } emit textRemoved(deti); return; } } } +/** + @brief Element::refreshLabelFields + The fields which draw the label draw it again: what they show of the + label has changed under them (a field for the terminal number added + or removed, @sa Element::hasTerminalNumberText), or the terminal + labelling setting was switched (@sa QetSettings::terminalLabelSplit). + Only the fields which draw a label are refreshed, every other field + follows its own information. +*/ +void Element::refreshLabelFields() +{ + for (DynamicElementTextItem *text : m_dynamic_text_list) { + text->refreshLabelConnection(); + } + for (ElementTextItemGroup *group : m_texts_group) { + for (DynamicElementTextItem *text : group->texts()) { + text->refreshLabelConnection(); + } + } +} + /** @brief Element::dynamicTextItems @return all dynamic text items of this element directly child of this element. @@ -2721,6 +2756,105 @@ QString Element::actualLabel() } } +/** + @brief Element::labelForDisplay + @return the label as the drawing must show it, the settings of the + moment being read (@sa QetSettings::terminalLabelSplit): the whole + label ("X1:1") while the labelling is shown in one field; the label + alone ("X1") when it is shown split and this symbol has a text field + of its own for the terminal number; and the two one under the other + when it is split without such a field -- so a symbol drawn before + the setting existed still shows all of its label, only shorter. +*/ +QString Element::labelForDisplay() +{ + const QString label = actualLabel(); + if (!QetSettings::terminalLabelSplit()) { + return label; + } + + const int colon = label.lastIndexOf(QLatin1Char(':')); + if (colon < 0) { + return label; + } + + if (hasTerminalNumberText()) { + return label.left(colon); + } + + return label.left(colon) + QLatin1Char('\n') + label.mid(colon + 1); +} + +/** + @brief Element::terminalNumberForDisplay + @return the part of the label behind its last colon ("1" in "X1:1"), + or an empty string while the labelling is shown in one field: the + number is already there with the label, showing it twice would only + be noise. @sa labelForDisplay +*/ +QString Element::terminalNumberForDisplay() +{ + if (!QetSettings::terminalLabelSplit()) { + return QString(); + } + + const QString label = actualLabel(); + const int colon = label.lastIndexOf(QLatin1Char(':')); + if (colon < 0) { + return QString(); + } + + return label.mid(colon + 1); +} + +/** + @brief Element::hasTerminalNumberText + @return whether this symbol has a text field bound to the terminal + number, the field labelForDisplay() leaves the number to. +*/ +bool Element::hasTerminalNumberText() const +{ + auto is_number_field = [](const DynamicElementTextItem *text) { + return text + && text->textFrom() == DynamicElementTextItem::ElementInfo + && text->infoName() == QETInformation::ELMT_TERMINAL_NUMBER; + }; + + for (const DynamicElementTextItem *text : dynamicTextItems()) { + if (is_number_field(text)) { + return true; + } + } + //The field may just as well live in a text group of the symbol + for (const ElementTextItemGroup *group : m_texts_group) { + for (const DynamicElementTextItem *text : group->texts()) { + if (is_number_field(text)) { + return true; + } + } + } + + return false; +} + +/** + @brief Element::infoForDisplay + @return what a text field bound to @p info_name must show: the label + and the terminal number follow how the labelling is drawn, every + other information is the stored one. @sa labelForDisplay +*/ +QString Element::infoForDisplay(const QString &info_name) +{ + if (info_name == QETInformation::ELMT_LABEL) { + return labelForDisplay(); + } + if (info_name == QETInformation::ELMT_TERMINAL_NUMBER) { + return terminalNumberForDisplay(); + } + + return m_data.m_informations.value(info_name).toString(); +} + /** @brief Element::name @return the human name of this element diff --git a/sources/qetgraphicsitem/element.h b/sources/qetgraphicsitem/element.h index 29c29bb1f..65dc5ce6f 100644 --- a/sources/qetgraphicsitem/element.h +++ b/sources/qetgraphicsitem/element.h @@ -165,6 +165,24 @@ class Element : public QetGraphicsItem bool isFreezeLabel() const {return m_freeze_label;} void freezeNewAddedElement(); QString actualLabel(); + /** + What the drawing shows of the label and of the terminal + number, the terminal labelling setting of the moment being + read (@sa QetSettings::terminalLabelSplit). The label itself + is never changed by any of them. + */ + QString labelForDisplay(); + QString terminalNumberForDisplay(); + QString infoForDisplay(const QString &info_name); + bool hasTerminalNumberText() const; + /** + The fields which draw the label draw it again: called when + what they show of the label has changed under them, a + field for the terminal number added or removed, or the + terminal labelling setting switched (@sa + QetSettings::terminalLabelSplit). + */ + void refreshLabelFields(); QString name() const override; ElementsLocation location() const; diff --git a/sources/qetinformation.cpp b/sources/qetinformation.cpp index 80d7db2bd..36498c9c4 100644 --- a/sources/qetinformation.cpp +++ b/sources/qetinformation.cpp @@ -142,6 +142,7 @@ QStringList QETInformation::elementInfoKeys() { QStringList list = { ELMT_FORMULA, ELMT_LABEL, + ELMT_TERMINAL_NUMBER, ELMT_PLANT, ELMT_LOCATION, ELMT_COMMENT, @@ -311,6 +312,7 @@ QString QETInformation::translatedInfoKey(const QString &info) else if (info == PROJECT_SAVED_FILE_PATH) return QObject::tr("Saved file path"); else if (info == ELMT_FORMULA) return QObject::tr("Label formula"); else if (info == ELMT_LABEL) return QObject::tr("Label"); + else if (info == ELMT_TERMINAL_NUMBER) return QObject::tr("Terminal number"); else if (info == ELMT_COMMENT) return QObject::tr("Annotation"); else if (info == ELMT_FUNCTION) return QObject::tr("Function"); else if (info == ELMT_DESCRIPTION) return QObject::tr("Textual description"); diff --git a/sources/qetinformation.h b/sources/qetinformation.h index 26dfb78f2..7da77677c 100644 --- a/sources/qetinformation.h +++ b/sources/qetinformation.h @@ -39,6 +39,10 @@ namespace QETInformation { /** Default information related to element **/ static QString ELMT_LABEL = "label"; + //The part of the label behind its last colon ("1" in "X1:1"), + //shown in a text field of its own when the terminal labelling + //is drawn split (@sa QetSettings::terminalLabelSplit) + static QString ELMT_TERMINAL_NUMBER = "terminal_number"; static QString ELMT_FORMULA = "formula"; /** Uuid of the project's element numbering scheme the element's diff --git a/sources/ui/configpage/generalconfigurationpage.cpp b/sources/ui/configpage/generalconfigurationpage.cpp index 6928e8d2b..645dec4dc 100644 --- a/sources/ui/configpage/generalconfigurationpage.cpp +++ b/sources/ui/configpage/generalconfigurationpage.cpp @@ -44,6 +44,7 @@ #include #include "../../diagram.h" +#include "../../qetgraphicsitem/element.h" #include "../../qetproject.h" namespace { @@ -161,6 +162,11 @@ GeneralConfigurationPage::GeneralConfigurationPage(QWidget *parent) : enable(ui->m_wiring_rules_cb->isChecked()); connect(ui->m_wiring_rules_cb, &QCheckBox::toggled, this, enable); } + if (QetSettings::terminalLabelSplit()) { + ui->m_terminal_label_split_rb->setChecked(true); + } else { + ui->m_terminal_label_combined_rb->setChecked(true); + } 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_autonumber_pasted->setChecked(settings.value("diagramcommands/autonumber-pasted-elements", true).toBool()); @@ -415,6 +421,19 @@ void GeneralConfigurationPage::applyConf() if (WiringRules::masterEnabled() && WiringRules::turnsRuleOn(before, rules)) { WiringRulesWarning::show(this); } + } + //The labelling setting is read where the label is drawn, so the + //drawings already open are told to draw it again when it changes + const bool label_split = ui->m_terminal_label_split_rb->isChecked(); + if (QetSettings::terminalLabelSplit() != label_split) { + QetSettings::setTerminalLabelSplit(label_split); + for (QETProject *project : QETApp::registeredProjects()) { + for (Diagram *diagram : project->diagrams()) { + for (Element *element : diagram->elements()) { + element->refreshLabelFields(); + } + } + } } settings.setValue("diagrameditor/autosave-interval", ui->m_autosave_sb->value()); settings.setValue("projectlock/enabled", ui->m_project_lock->isChecked()); diff --git a/sources/ui/configpage/generalconfigurationpage.ui b/sources/ui/configpage/generalconfigurationpage.ui index 5a91496c9..40a87f188 100644 --- a/sources/ui/configpage/generalconfigurationpage.ui +++ b/sources/ui/configpage/generalconfigurationpage.ui @@ -140,6 +140,48 @@ + + + + Display of the terminal labelling + + + + + + A terminal's label (X1:1) is drawn as a whole, the way it has always been. + + + Show the label and the terminal number in one field + + + true + + + + + + + The label (X1) is drawn in one field and the terminal number (1) in another, which a terminal symbol may place where it wants -- and hide when it must. A symbol without such a field shows the two, one under the other. + + + Split the label and the terminal number + + + + + + + Only the drawing changes: what is written in the element, the numbering, the sorting and the exports keep the whole label. + + + true + + + + + + diff --git a/sources/utils/qetsettings.cpp b/sources/utils/qetsettings.cpp index 632d30b52..35ffe8559 100644 --- a/sources/utils/qetsettings.cpp +++ b/sources/utils/qetsettings.cpp @@ -290,4 +290,28 @@ namespace QetSettings QSettings settings; settings.setValue(QStringLiteral("diagrameditor/sheet_background_recent"), colors); } + + /** + * @brief terminalLabelSplit + * @return whether a terminal's label is drawn split, the label and + * the terminal number each in their own text field, instead of the + * whole label ("X1:1") in one field. Off unless the user turned it + * on, so an existing drawing keeps its look. @sa setTerminalLabelSplit + */ + bool terminalLabelSplit() + { + QSettings settings; + return settings.value(QStringLiteral("element/terminal_label_split"), false).toBool(); + } + + /** + * @brief setTerminalLabelSplit + * Store whether a terminal's label is drawn split. @sa terminalLabelSplit + * @param split + */ + void setTerminalLabelSplit(bool split) + { + QSettings settings; + settings.setValue(QStringLiteral("element/terminal_label_split"), split); + } } diff --git a/sources/utils/qetsettings.h b/sources/utils/qetsettings.h index f291caf93..5e4e42c25 100644 --- a/sources/utils/qetsettings.h +++ b/sources/utils/qetsettings.h @@ -76,6 +76,20 @@ namespace QetSettings QStringList sheetBackgroundRecentColors(); void setSheetBackgroundRecentColors(const QStringList &colors); + + /** + @brief How a terminal's own label is drawn. + + The label is stored as it is written ("X1:1"). With the split on, + the drawing shows the label ("X1") and the terminal number ("1") + as two parts, so a terminal symbol can keep the number in its own + text field and place it where it wants. Only the drawing reacts: + what the element stores, the numbering, the sorting in the + terminal strip editor and every export keep the whole label. + Off by default, so a drawing keeps the look it was made with. + */ + bool terminalLabelSplit(); + void setTerminalLabelSplit(bool split); } #endif // QETSETTINGS_H diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index 4e8d6c930..c4cc2fadc 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -323,6 +323,13 @@ qet_add_app_test(tst_reportfoliotitle tst_reportfoliotitle.cpp) # what was typed in, and nothing stale is written into the element. qet_add_app_test(tst_terminalcableauto tst_terminalcableauto.cpp) +# A terminal's label is drawn whole in one field, or split into its label +# and its terminal number: in their own fields when the symbol carries a +# field for the number, one under the other when it does not. Only the +# drawing changes; the stored label, the numbering and the exports keep +# the whole label. +qet_add_app_test(tst_terminallabeldisplay tst_terminallabeldisplay.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_terminallabeldisplay.cpp b/tests/qttest/tst_terminallabeldisplay.cpp new file mode 100644 index 000000000..b3f591ec2 --- /dev/null +++ b/tests/qttest/tst_terminallabeldisplay.cpp @@ -0,0 +1,221 @@ +// 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 "qetgraphicsitem/dynamicelementtextitem.h" +#include "qetgraphicsitem/element.h" +#include "qetgraphicsitem/terminal.h" +#include "qetinformation.h" +#include "qetproject.h" +#include "utils/qetsettings.h" + +/** + How a terminal's own label is drawn, the setting of the moment being + read (@sa QetSettings::terminalLabelSplit). + + The label stays whole wherever it is used as data: what the element + stores, the numbering, the sorting in the terminal strip editor and + the exports are never touched by the setting. Only the drawing reacts: + with the labelling split, the label ("X1") and the terminal number + ("1") are two parts -- in their own fields when the symbol has a + field for the number, one under the other when it has not. + + fixtures/every_feature.qet has the strip X1 whose terminals are + plain terminal symbols: a field tagged "label", no field for the + number, and -- as every element of the fixture -- a label the test + gives them before it looks at the drawing. +*/ +class tst_terminallabeldisplay : public QObject +{ + Q_OBJECT + + //A terminal element of the fixture's strip X1, given @p label + static Element *terminalWithLabel(QETProject &project, const QString &label) + { + const QVector strips = project.terminalStrip(); + if (strips.isEmpty()) return nullptr; + for (const QSharedPointer &real_t + : strips.first()->realTerminals()) + { + Element *element = real_t ? real_t->element() : nullptr; + if (!element || element->terminals().isEmpty()) continue; + + DiagramContext dc = element->elementInformations(); + dc.addValue(QStringLiteral("label"), label); + element->setElementInformations(dc); + if (element->actualLabel() == label) return element; + } + return nullptr; + } + + //The text field of @p element bound to @p info_name, the field the + //symbol carries for it + static DynamicElementTextItem *fieldBoundTo(Element *element, const QString &info_name) + { + for (DynamicElementTextItem *text : element->dynamicTextItems()) + if (text->infoName() == info_name) return text; + return nullptr; + } + + //A text field as a symbol made for the split would carry one: bound + //to the terminal number, and nothing else + static DynamicElementTextItem *addNumberField(Element *element) + { + DynamicElementTextItem *number = new DynamicElementTextItem(element); + element->addDynamicTextItem(number); + number->setInfoName(QETInformation::ELMT_TERMINAL_NUMBER); + number->setTextFrom(DynamicElementTextItem::ElementInfo); + return number; + } + +private slots: + void cleanup() + { + //Whatever a test left behind, the next one (and the rest of + //the suite) starts from the shipped default + QetSettings::setTerminalLabelSplit(false); + } + + //The shipped default: one field with the whole label, the way it has + //always been drawn + void oneFieldKeepsTheWholeLabel() + { + QetSettings::setTerminalLabelSplit(false); + + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + Element *element = terminalWithLabel(project, QStringLiteral("X1:1")); + QVERIFY2(element, "the fixture has a terminal of the strip X1"); + + QCOMPARE(element->actualLabel(), QStringLiteral("X1:1")); + QCOMPARE(element->labelForDisplay(), QStringLiteral("X1:1")); + QVERIFY(element->terminalNumberForDisplay().isEmpty()); + QVERIFY(element->infoForDisplay(QETInformation::ELMT_TERMINAL_NUMBER).isEmpty()); + QVERIFY(!element->hasTerminalNumberText()); + + //The field the symbol carries shows the whole label + DynamicElementTextItem *label = fieldBoundTo(element, QETInformation::ELMT_LABEL); + QVERIFY2(label, "the terminal symbol has a field for its label"); + QCOMPARE(label->toPlainText(), QStringLiteral("X1:1")); + } + + //Split, and the symbol has no field for the number: the number stays + //with the label, on its own line, so that switching the setting on + //can never make a number vanish + void splitWithoutNumberFieldShowsTwoLines() + { + QetSettings::setTerminalLabelSplit(true); + + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + Element *element = terminalWithLabel(project, QStringLiteral("X1:1")); + QVERIFY(element); + + QCOMPARE(element->labelForDisplay(), QStringLiteral("X1\n1")); + QCOMPARE(element->terminalNumberForDisplay(), QStringLiteral("1")); + QCOMPARE(element->infoForDisplay(QETInformation::ELMT_TERMINAL_NUMBER), + QStringLiteral("1")); + + DynamicElementTextItem *label = fieldBoundTo(element, QETInformation::ELMT_LABEL); + QVERIFY(label); + QCOMPARE(label->toPlainText(), QStringLiteral("X1\n1")); + + //A text which uses the label as a variable follows the same + //rule as the field bound to the label itself + DynamicElementTextItem *composite = new DynamicElementTextItem(element); + composite->setCompositeText(QStringLiteral("%{label}")); + composite->setTextFrom(DynamicElementTextItem::CompositeText); + QCOMPARE(composite->toPlainText(), QStringLiteral("X1\n1")); + } + + //Split, and the symbol has a field of its own for the number: each + //part in its field, the label no longer carrying the number + void splitWithNumberFieldShowsEachInItsOwnField() + { + QetSettings::setTerminalLabelSplit(true); + + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + Element *element = terminalWithLabel(project, QStringLiteral("X1:1")); + QVERIFY(element); + + DynamicElementTextItem *number = addNumberField(element); + QVERIFY(element->hasTerminalNumberText()); + QCOMPARE(number->toPlainText(), QStringLiteral("1")); + + QCOMPARE(element->labelForDisplay(), QStringLiteral("X1")); + DynamicElementTextItem *label = fieldBoundTo(element, QETInformation::ELMT_LABEL); + QVERIFY(label); + QCOMPARE(label->toPlainText(), QStringLiteral("X1")); + } + + //A label with no colon -- every label which is not a terminal's, and + //a terminal named by hand -- is drawn as it is stored, whatever the + //setting says + void splitLeavesLabelsWithoutColonAlone() + { + QetSettings::setTerminalLabelSplit(true); + + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + Element *element = terminalWithLabel(project, QStringLiteral("S7")); + QVERIFY(element); + + QCOMPARE(element->labelForDisplay(), QStringLiteral("S7")); + QVERIFY(element->terminalNumberForDisplay().isEmpty()); + + DynamicElementTextItem *label = fieldBoundTo(element, QETInformation::ELMT_LABEL); + QVERIFY(label); + QCOMPARE(label->toPlainText(), QStringLiteral("S7")); + } + + //The setting is read where it is drawn: the settings page tells the + //drawings already open to draw it again, and the whole label comes + //back without anything to rewrite first + void switchingBackGivesTheWholeLabel() + { + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + QetSettings::setTerminalLabelSplit(true); + Element *element = terminalWithLabel(project, QStringLiteral("X1:1")); + QVERIFY(element); + DynamicElementTextItem *label = fieldBoundTo(element, QETInformation::ELMT_LABEL); + QVERIFY(label); + QCOMPARE(label->toPlainText(), QStringLiteral("X1\n1")); + + QetSettings::setTerminalLabelSplit(false); + element->refreshLabelFields(); + QCOMPARE(label->toPlainText(), QStringLiteral("X1:1")); + QCOMPARE(element->actualLabel(), QStringLiteral("X1:1")); + } +}; + +QET_APP_TEST_MAIN(tst_terminallabeldisplay) +#include "tst_terminallabeldisplay.moc"