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 touchpadGesten 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?
LabelBetriebsmittelkennzeichen (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"