Merge pull request #1584 from Kellermorph/feature/terminal-label-split
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m38s

Terminal labelling: option to draw label and number apart, and a Show name box for terminals
This commit is contained in:
Laurent Trinques
2026-10-23 13:41:48 +02:00
committed by GitHub
16 changed files with 1036 additions and 26 deletions
@@ -548,6 +548,12 @@ void ElementQueryWidget::setUpItems()
{
if (key == "formula" || key == "exclude_from_bom")
continue;
// Terminal number is read-only and not a column of the nomenclature
// query, so offering it as an export/query column would break the
// export (query fails with "no such column"). It stays available as a
// text variable (%{terminal_number}) in the element editor.
if (key == QETInformation::ELMT_TERMINAL_NUMBER)
continue;
auto item = new QListWidgetItem(QETInformation::translatedInfoKey(key), ui->m_var_list);
item->setData(Qt::UserRole, key);
@@ -46,6 +46,20 @@
#include <QTextCursor>
#include <QTextDocumentFragment>
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,11 @@ 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}")))
refreshLabelShown();
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)
@@ -418,6 +435,30 @@ void DynamicElementTextItem::refreshLabelConnection()
}
}
/**
@brief DynamicElementTextItem::refreshLabelShown
Whether this field is drawn at all: a field bound to the label of
an element whose "Show name" box is unticked (@sa Element::labelShown)
does not stay on the sheet as an empty frame to select -- it is
hidden, and comes back the moment the box is ticked again. Both ways
run through the same signals which refresh the text, so the field
can never get stuck on one side. Every other field is drawn as
always, and a composite text keeps drawing what is not the label.
*/
void DynamicElementTextItem::refreshLabelShown()
{
const bool shown = !(m_text_from == ElementInfo
&& m_info_name == QETInformation::ELMT_LABEL)
|| !m_parent_element
|| m_parent_element.data()->labelShown();
if (!shown && isSelected()) {
setSelected(false);
}
setVisible(shown);
refreshResizeHandlesVisibility();
}
/**
@brief DynamicElementTextItem::textFrom
@return what the final text is created from.
@@ -450,26 +491,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 +527,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 +573,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,16 +597,32 @@ 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();
}
//A field which becomes, or stops being, the field for the
//label of its element is shown or hidden with the "Show name"
//box (@sa refreshLabelShown)
refreshLabelShown();
emit infoNameChanged(info_name);
}
@@ -956,7 +1031,11 @@ bool DynamicElementTextItem::sceneEventFilter(QGraphicsItem *watched, QEvent *ev
*/
void DynamicElementTextItem::refreshResizeHandlesVisibility()
{
const bool handles_wanted = isSelected() || (m_parent_element && m_parent_element->isSelected());
//A hidden text has no handles, not even while its element is
//selected: refreshLabelShown() puts them back when the text
//is shown again
const bool handles_wanted = isVisible()
&& (isSelected() || (m_parent_element && m_parent_element->isSelected()));
if (handles_wanted && !m_left_resize_handle)
addResizeHandles();
else if (!handles_wanted && m_left_resize_handle)
@@ -1157,6 +1236,11 @@ void DynamicElementTextItem::handlerMouseReleaseEvent(QetGraphicsHandlerItem *ha
void DynamicElementTextItem::elementInfoChanged()
{
//The "Show name" box of the selection properties was possibly
//ticked or unticked: the field of a hidden name leaves the
//sheet and comes back with the box (@sa refreshLabelShown)
refreshLabelShown();
//A folio report's label shows where its partner is
//(updateReportText()); the label in its information is only where
//the partner was when last saved, and changing any other
@@ -1174,6 +1258,12 @@ void DynamicElementTextItem::elementInfoChanged()
Element *element = elementUseForInfo();
if(element) {
dc = element->elementInformations();
//The label and the terminal number are drawn the way the
//project says (@sa QETProject::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 +1273,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 +1290,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 +1458,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 +1551,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 +1564,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));
}
@@ -97,6 +97,7 @@ class DynamicElementTextItem : public DiagramTextItem
ElementTextItemGroup *parentGroup() const;
Element *elementUseForInfo() const;
void refreshLabelConnection();
void refreshLabelShown();
DynamicElementTextItem::TextFrom textFrom() const;
void setTextFrom (DynamicElementTextItem::TextFrom text_from);
+189
View File
@@ -1689,6 +1689,17 @@ void Element::addDynamicTextItem(DynamicElementTextItem *deti)
m_dynamic_text_list.append(deti);
//An element whose name must not be drawn does not keep the
//field of its label on the sheet either (@sa Element::labelShown)
deti->refreshLabelShown();
//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 +1735,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 +1750,37 @@ 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 of the project was switched
(@sa QETProject::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.
@@ -2735,6 +2774,156 @@ QString Element::actualLabel()
}
}
/**
@brief Element::labelForDisplay
@return the label as the drawing must show it, the terminal labelling
of the project being read (@sa QETProject::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. Only an element of the terminal type is ever drawn split:
the colon in any other label is part of the label ("-K1:13").
*/
QString Element::labelForDisplay()
{
const QString label = actualLabel();
const Diagram *d = diagram();
const bool split = m_data.m_type == ElementData::Terminal
&& d && d->project() && d->project()->terminalLabelSplit();
//The "Show name" box of the selection properties hides the name
//of this element on the sheet only: the element keeps all of its
//data, and a terminal keeps its number, drawn on its own while
//the labelling is split.
if (!labelShown())
{
if (!split) {
return QString();
}
//The number alone in the label field, but only while the
//symbol has no field of its own for it: the number is
//drawn once, wherever it is drawn at all.
const int hidden_colon = label.lastIndexOf(QLatin1Char(':'));
if (hidden_colon < 0 || hasTerminalNumberText()) {
return QString();
}
return label.mid(hidden_colon + 1);
}
if (!split) {
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 -- and
for every element which is no terminal, whose colon is part of its
label: the number is already there with the label, showing it twice
would only be noise. @sa labelForDisplay
*/
QString Element::terminalNumberForDisplay()
{
const Diagram *d = diagram();
if (m_data.m_type != ElementData::Terminal
|| !d || !d->project() || !d->project()->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::labelShown
@return whether the label of this element must be drawn on the sheet.
The choice belongs to the element itself, set with the "Show name"
box of the selection properties: an element whose name is not shown
keeps all of its data, nothing is only not drawn. An element which
never got the box unticked keeps drawing its name, which is why a
missing information means true.
*/
bool Element::labelShown() const
{
//Only a terminal may have its name undrawn: an information
//which says otherwise, from whatever source, is not obeyed.
if (m_data.m_type != ElementData::Terminal) {
return true;
}
if (!m_data.m_informations.contains(QStringLiteral("label_shown"))) {
return true;
}
return QET::infoFlagIsTrue(
m_data.m_informations.value(QStringLiteral("label_shown")).toString());
}
/**
@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
+19
View File
@@ -166,6 +166,25 @@ 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 of the project being
read (@sa QETProject::terminalLabelSplit). The label itself
is never changed by any of them.
*/
QString labelForDisplay();
QString terminalNumberForDisplay();
QString infoForDisplay(const QString &info_name);
bool hasTerminalNumberText() const;
bool labelShown() 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 of the project switched (@sa
QETProject::terminalLabelSplit).
*/
void refreshLabelFields();
QString name() const override;
ElementsLocation location() const;
+2
View File
@@ -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");
+4
View File
@@ -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 QETProject::terminalLabelSplit)
static QString ELMT_TERMINAL_NUMBER = "terminal_number";
static QString ELMT_FORMULA = "formula";
/**
Uuid of the project's element numbering scheme the element's
+67
View File
@@ -2316,6 +2316,39 @@ void QETProject::setUprightSymbolTexts(bool upright)
}
}
/**
@brief QETProject::terminalLabelSplit
@return whether the terminals of this project draw their label split,
the label ("X1") and the terminal number ("1") each in their own text
field, instead of the whole label ("X1:1") in one field. Off unless
the project says otherwise, so a project saved without it draws as it
always did. This is a setting of the project, not of the application:
the same file draws the same way on every machine which opens it.
@sa setTerminalLabelSplit
*/
bool QETProject::terminalLabelSplit() const {
return m_terminal_label_split;
}
/**
@brief QETProject::setTerminalLabelSplit
Set whether the terminals of this project draw their label split, and
tell the labels of the drawings already open to draw it again.
@param split
*/
void QETProject::setTerminalLabelSplit(bool split)
{
if (split == m_terminal_label_split) {
return;
}
m_terminal_label_split = split;
for (Diagram *diagram : diagrams()) {
for (Element *element : diagram->elements()) {
element->refreshLabelFields();
}
}
}
/**
@brief QETProject::wiringRules
@return how many wires a terminal of this project may take
@@ -2554,6 +2587,7 @@ QDomDocument QETProject::toXml(QHash<int, QByteArray> *kept_folios, QStringList
writeUsageXml(project_root);
writeWireHopsXml(project_root);
writeSymbolTextsXml(project_root);
writeTerminalLabelSplitXml(project_root);
writeWiringRulesXml(project_root);
// Properties for news diagrams
@@ -3525,6 +3559,7 @@ void QETProject::readProjectXml(QDomDocument &xml_project)
readUsageXml(xml_project);
readWireHopsXml(xml_project);
readSymbolTextsXml(xml_project);
readTerminalLabelSplitXml(xml_project);
readWiringRulesXml(xml_project);
//Load the default properties for the new diagrams
@@ -3889,6 +3924,21 @@ void QETProject::readSymbolTextsXml(QDomDocument &xml_project)
&& texts.attribute(QStringLiteral("upright")) == QLatin1String("true");
}
/**
@brief QETProject::readTerminalLabelSplitXml
Read the <terminal_labelling> element of the project, if any. A
project without it was saved before the setting existed, or with it
off, and draws the whole label of each terminal in one field.
@param xml_project : the xml description of the project
*/
void QETProject::readTerminalLabelSplitXml(QDomDocument &xml_project)
{
const QDomElement labelling = xml_project.documentElement()
.firstChildElement(QStringLiteral("terminal_labelling"));
m_terminal_label_split = !labelling.isNull()
&& labelling.attribute(QStringLiteral("split")) == QLatin1String("true");
}
/**
@brief QETProject::readWiringRulesXml
Read the <wiring_rules> element of the project, if any.
@@ -4213,6 +4263,23 @@ void QETProject::writeSymbolTextsXml(QDomElement &xml_element)
xml_element.appendChild(texts);
}
/**
@brief QETProject::writeTerminalLabelSplitXml
Export whether the terminals draw their label split, as a
<terminal_labelling> child of \a xml_element. Written only when on,
so a project with it off saves exactly as before.
*/
void QETProject::writeTerminalLabelSplitXml(QDomElement &xml_element)
{
if (!m_terminal_label_split) {
return;
}
QDomElement labelling = xml_element.ownerDocument()
.createElement(QStringLiteral("terminal_labelling"));
labelling.setAttribute(QStringLiteral("split"), QStringLiteral("true"));
xml_element.appendChild(labelling);
}
/**
@brief QETProject::writeWiringRulesXml
Export the project's wiring rules as a <wiring_rules> child of
+8
View File
@@ -325,6 +325,8 @@ class QETProject : public QObject
void setWireHops(WireHops::Mode mode);
bool uprightSymbolTexts() const;
void setUprightSymbolTexts(bool upright);
bool terminalLabelSplit() const;
void setTerminalLabelSplit(bool split);
WiringRules::Settings wiringRules() const;
WiringRules::Settings projectWiringRules() const;
void setWiringRules(const WiringRules::Settings &rules);
@@ -454,6 +456,7 @@ class QETProject : public QObject
void readUsageXml(QDomDocument &xml_project);
void readWireHopsXml(QDomDocument &xml_project);
void readSymbolTextsXml(QDomDocument &xml_project);
void readTerminalLabelSplitXml(QDomDocument &xml_project);
void readWiringRulesXml(QDomDocument &xml_project);
void writeProjectPropertiesXml(QDomElement &);
@@ -463,6 +466,7 @@ class QETProject : public QObject
void writeUsageXml(QDomElement &);
void writeWireHopsXml(QDomElement &);
void writeSymbolTextsXml(QDomElement &);
void writeTerminalLabelSplitXml(QDomElement &);
void writeWiringRulesXml(QDomElement &);
void addDiagram(Diagram *diagram, int pos = -1);
void detachDiagram(Diagram *diagram);
@@ -572,6 +576,10 @@ class QETProject : public QObject
/// Texts drawn in a turned symbol stay horizontal (on for a new
/// project, off for one saved without it), see uprightSymbolTexts()
bool m_upright_symbol_texts = false;
/// Terminals draw their label split: the label ("X1") and the
/// terminal number ("1") each in their own text field (off for
/// a project saved without it), see terminalLabelSplit()
bool m_terminal_label_split = false;
WiringRules::Settings m_wiring_rules;
XmlElementCollection *m_elements_collection = nullptr;
QTimer m_save_backup_timer,
@@ -191,6 +191,11 @@ void ProjectMainConfigPage::applyProjectConf()
modified_project = true;
}
if (m_project -> terminalLabelSplit() != terminal_label_split_rb_ -> isChecked()) {
m_project -> setTerminalLabelSplit(terminal_label_split_rb_ -> isChecked());
modified_project = true;
}
//Following the application's rules stores nothing in the project
WiringRules::Settings wiring_rules;
if (!use_application_rules_cb_ -> isChecked()) {
@@ -266,6 +271,27 @@ void ProjectMainConfigPage::initWidgets()
"text. Uncheck to make them turn with the element, as before.",
"tooltip"));
//How the terminals draw their label: a setting of this project,
//so the same file draws the same way on every machine which
//opens it, like every other choice of how the sheets are drawn
terminal_label_gb_ = new QGroupBox(tr("Display of the terminal labelling", "group box title"));
terminal_label_combined_rb_ = new QRadioButton(
tr("Show the label and the terminal number in one field", "radio button label"));
terminal_label_combined_rb_ -> setToolTip(tr(
"A terminal's label (X1:1) is drawn as a whole, the way it has always been.",
"tooltip"));
terminal_label_split_rb_ = new QRadioButton(tr("Split the label and the terminal number",
"radio button label"));
terminal_label_split_rb_ -> setToolTip(tr(
"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.",
"tooltip"));
terminal_label_help_ = new QLabel(tr("Only the drawing changes: what is written in the element, "
"the numbering, the sorting and the exports keep the whole label.",
"informative label"));
terminal_label_help_ -> setWordWrap(true);
//How many wires a terminal may take (discussion #1158)
wiring_rules_gb_ = new QGroupBox(tr("Conductors per terminal", "group box title"));
use_application_rules_cb_ = new QCheckBox(tr("Use the application settings", "checkbox label"));
@@ -335,6 +361,13 @@ void ProjectMainConfigPage::initLayout()
main_layout0 -> addWidget(upright_symbol_texts_cb_);
main_layout0 -> addSpacing(10);
QVBoxLayout *terminal_label_layout = new QVBoxLayout(terminal_label_gb_);
terminal_label_layout -> addWidget(terminal_label_combined_rb_);
terminal_label_layout -> addWidget(terminal_label_split_rb_);
terminal_label_layout -> addWidget(terminal_label_help_);
main_layout0 -> addWidget(terminal_label_gb_);
main_layout0 -> addSpacing(10);
QVBoxLayout *wiring_rules_layout = new QVBoxLayout(wiring_rules_gb_);
wiring_rules_layout -> addWidget(use_application_rules_cb_);
QHBoxLayout *max_wires_layout = new QHBoxLayout();
@@ -369,6 +402,11 @@ void ProjectMainConfigPage::readValuesFromProject()
const int wire_hops_index = wire_hops_cb_ -> findData(WireHops::toString(m_project -> wireHops()));
wire_hops_cb_ -> setCurrentIndex(qMax(0, wire_hops_index));
upright_symbol_texts_cb_ -> setChecked(m_project -> uprightSymbolTexts());
if (m_project -> terminalLabelSplit()) {
terminal_label_split_rb_ -> setChecked(true);
} else {
terminal_label_combined_rb_ -> setChecked(true);
}
//The rules that apply: the project's own or the application's
const WiringRules::Settings wiring_rules = m_project -> wiringRules();
@@ -425,6 +463,7 @@ void ProjectMainConfigPage::adjustReadOnly()
usage_reset_pb_ -> setDisabled(is_read_only);
wire_hops_cb_ -> setDisabled(is_read_only);
upright_symbol_texts_cb_ -> setDisabled(is_read_only);
terminal_label_gb_ -> setDisabled(is_read_only);
wiring_rules_gb_ -> setDisabled(is_read_only);
}
@@ -26,6 +26,7 @@ class QLabel;
class QComboBox;
class QLineEdit;
class QCheckBox;
class QRadioButton;
class QPushButton;
class QSpinBox;
class QGroupBox;
@@ -140,6 +141,10 @@ class ProjectMainConfigPage : public ProjectConfigPage {
QLabel *wire_hops_label_;
QComboBox *wire_hops_cb_;
QCheckBox *upright_symbol_texts_cb_;
QGroupBox *terminal_label_gb_;
QRadioButton *terminal_label_combined_rb_;
QRadioButton *terminal_label_split_rb_;
QLabel *terminal_label_help_;
QGroupBox *wiring_rules_gb_;
QCheckBox *use_application_rules_cb_;
QLabel *max_wires_label_;
+58 -1
View File
@@ -262,6 +262,9 @@ void ElementInfoWidget::enableLiveEdit()
if (m_freeze_cb) {
connect(m_freeze_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
}
if (m_label_shown_cb) {
connect(m_label_shown_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
}
if (m_number_cb) {
connect(m_number_cb, QOverload<int>::of(&QComboBox::activated), this, &ElementInfoWidget::apply);
}
@@ -283,6 +286,9 @@ void ElementInfoWidget::disableLiveEdit()
if (m_freeze_cb) {
disconnect(m_freeze_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
}
if (m_label_shown_cb) {
disconnect(m_label_shown_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
}
if (m_number_cb) {
disconnect(m_number_cb, QOverload<int>::of(&QComboBox::activated), this, &ElementInfoWidget::apply);
}
@@ -307,6 +313,13 @@ void ElementInfoWidget::buildInterface()
//line that currentInfo() then fills with "true"/"false".
keys.removeAll(QStringLiteral("exclude_from_bom"));
//The terminal number is not a property of the element: it is
//derived from its label, so an edit row here would store a value
//nothing reads -- and the nomenclature query has no such column,
//which is why the export does not offer it either. The number
//stays a text variable (%{terminal_number}) of the element editor.
keys.removeAll(QETInformation::ELMT_TERMINAL_NUMBER);
for (auto str : keys)
{
ElementInfoPartWidget *eipw = new ElementInfoPartWidget(str, QETInformation::translatedInfoKey(str), this);
@@ -318,6 +331,7 @@ void ElementInfoWidget::buildInterface()
setupSchemeRow();
setupNumberRow();
setupFreezeRow();
setupLabelShownRow();
m_add_custom_property_btn = new QPushButton(tr("Add a custom property"), this);
connect(m_add_custom_property_btn, &QPushButton::clicked, this, [this]() { addCustomProperty(); });
@@ -367,7 +381,8 @@ QStringList ElementInfoWidget::predefinedKeys() const
keys << QETInformation::ELMT_FORMULA_ID
<< QStringLiteral("auto_num_locked")
<< QStringLiteral("potential_isolating")
<< QStringLiteral("exclude_from_bom");
<< QStringLiteral("exclude_from_bom")
<< QStringLiteral("label_shown");
return keys;
}
@@ -672,6 +687,9 @@ void ElementInfoWidget::updateUi()
m_freeze_cb->setChecked(m_element->isFreezeLabel());
updateFreezeRow();
}
if (m_label_shown_cb) {
m_label_shown_cb->setChecked(m_element->labelShown());
}
refreshNumberRow();
updateSuggestions();
@@ -759,6 +777,13 @@ DiagramContext ElementInfoWidget::currentInfo() const
info_.addValue(QStringLiteral("exclude_from_bom"), m_exclude_from_bom_cb->isChecked() ? QStringLiteral("true") : QStringLiteral("false"));
}
// The name is drawn unless the element says otherwise: only a name
// which is not drawn needs an information, so that elements which
// draw it keep their files exactly as they are.
if (m_label_shown_cb && !m_label_shown_cb->isChecked()) {
info_.addValue(QStringLiteral("label_shown"), QStringLiteral("false"));
}
//The element keeps following its numbering scheme as long as its
//formula is left as it is; a formula edited by hand is its own.
const DiagramContext &elmt_info = m_element->elementInformations();
@@ -846,6 +871,38 @@ void ElementInfoWidget::updateFreezeRow()
updateNumberRow();
}
/**
@brief ElementInfoWidget::setupLabelShownRow
Add under the freeze box a check box which decides whether the name
of this element is drawn on the sheet: the choice belongs to the
placed element, not to the symbol, so everyone can decide for
himself.
*/
void ElementInfoWidget::setupLabelShownRow()
{
//The box means something for a terminal only, so it is drawn
//for a terminal only - the same rule as the function separation
//box just below it.
if (!m_element || m_element->elementData().m_type != ElementData::Terminal) {
return;
}
m_label_shown_cb = new QCheckBox(tr("Show name"), this);
m_label_shown_cb->setObjectName(QStringLiteral("m_label_shown_cb"));
m_label_shown_cb->setToolTip(tr("Untick to not draw the name of this element on the sheet: "
"its data is kept, and a terminal keeps its number."));
int row = ui->scroll_vlayout->indexOf(m_freeze_cb);
if (row < 0) {
ElementInfoPartWidget *label = infoPartWidgetForKey(QETInformation::ELMT_LABEL);
row = label ? ui->scroll_vlayout->indexOf(label) : -1;
}
if (row >= 0) {
ui->scroll_vlayout->insertWidget(row + 1, m_label_shown_cb);
} else {
ui->scroll_vlayout->addWidget(m_label_shown_cb);
}
}
/**
@brief ElementInfoWidget::setupNumberRow
Add under the numbering a list of the numbers the element may be given
+2
View File
@@ -76,6 +76,7 @@ class ElementInfoWidget : public AbstractElementPropertiesEditorWidget
void updateSuggestions();
void setupSchemeRow();
void setupFreezeRow();
void setupLabelShownRow();
void setupNumberRow();
void refreshNumberRow();
void updateNumberRow();
@@ -103,6 +104,7 @@ class ElementInfoWidget : public AbstractElementPropertiesEditorWidget
QCheckBox *m_exclude_from_bom_cb = nullptr;
QComboBox *m_scheme_cb = nullptr;
QCheckBox *m_freeze_cb = nullptr;
QCheckBox *m_label_shown_cb = nullptr;
QWidget *m_number_row = nullptr;
QComboBox *m_number_cb = nullptr;
int m_number_current = -1; ///< the number the element has, -1 if unknown
+22 -2
View File
@@ -75,6 +75,15 @@ quint64 s_last_session = 0;
const QString EXCLUDE_FROM_BOM = QStringLiteral("exclude_from_bom");
const QString AUTO_NUM_LOCKED = QStringLiteral("auto_num_locked");
const QString POTENTIAL_ISOLATING = QStringLiteral("potential_isolating");
//The name is drawn unless the element says otherwise (@sa
//Element::labelShown): an element which never got the box unticked
//has no information for it, and counts as "shown".
const QString LABEL_SHOWN = QStringLiteral("label_shown");
bool flagDefaultIsOn(const QString &key)
{
return key == LABEL_SHOWN;
}
QString flagText(bool on)
{
@@ -449,7 +458,8 @@ void MultiElementInfoWidget::buildInterface()
const QList<QPair<QString, QString>> flags {
{EXCLUDE_FROM_BOM, tr("Exclude from the bill of materials")},
{POTENTIAL_ISOLATING, tr("Function separation")},
{AUTO_NUM_LOCKED, tr("Exclude from automatic numbering")}
{AUTO_NUM_LOCKED, tr("Exclude from automatic numbering")},
{LABEL_SHOWN, tr("Show name")}
};
for (const auto &pair : flags)
{
@@ -486,6 +496,9 @@ void MultiElementInfoWidget::buildInterface()
QStringList keys = QETInformation::elementInfoKeys();
//It has its own check box above, see ElementInfoWidget::buildInterface
keys.removeAll(EXCLUDE_FROM_BOM);
//Not an editable property but derived from the label, see
//ElementInfoWidget::buildInterface
keys.removeAll(QETInformation::ELMT_TERMINAL_NUMBER);
int line = 0;
for (const QString &key : std::as_const(keys))
@@ -588,12 +601,19 @@ void MultiElementInfoWidget::fillRows()
for (Flag *flag : std::as_const(m_flags))
{
//"Show name" means something for a terminal only, so it is
//shown for terminals only - the same rule as the two flags
//before it.
flag->check->setVisible(flag->key == EXCLUDE_FROM_BOM || all_terminals);
flag->restore->setVisible(flag->check->isVisible());
int on = 0;
for (Element *element : list) {
if (QET::infoFlagIsTrue(m_snapshot.value(element).value(flag->key).toString())) {
const QVariant value = m_snapshot.value(element).value(flag->key);
const bool is_on = value.isValid()
? QET::infoFlagIsTrue(value.toString())
: flagDefaultIsOn(flag->key);
if (is_on) {
++on;
}
}
+7
View File
@@ -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)
+487
View File
@@ -0,0 +1,487 @@
// 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 <http://www.gnu.org/licenses/>.
*/
#include "qetapptest.h"
#include "TerminalStrip/realterminal.h"
#include "TerminalStrip/terminalstrip.h"
#include "dataBase/ui/elementquerywidget.h"
#include "diagram.h"
#include "ui/elementinfopartwidget.h"
#include "ui/elementinfowidget.h"
#include "ui/multielementinfowidget.h"
#include <QLabel>
#include <QListWidget>
#include "properties/elementdata.h"
#include "qetgraphicsitem/dynamicelementtextitem.h"
#include "qetgraphicsitem/element.h"
#include "qetgraphicsitem/terminal.h"
#include "qetinformation.h"
#include "qetproject.h"
/**
How a terminal's own label is drawn, the terminal labelling of the
project being read (@sa QETProject::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<TerminalStrip *> strips = project.terminalStrip();
if (strips.isEmpty()) return nullptr;
for (const QSharedPointer<RealTerminal> &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;
}
//The "Show name" box of the selection properties, unticked: the
//element then stores the information "label_shown" = "false", the
//only thing it stores for it
static void hideName(Element *element)
{
DiagramContext dc = element->elementInformations();
dc.addValue(QStringLiteral("label_shown"), QStringLiteral("false"));
element->setElementInformations(dc);
}
//Any element of the fixture which is not a terminal and carries a
//name to draw
static Element *aNonTerminal(QETProject &project)
{
for (Diagram *diagram : project.diagrams())
for (Element *element : diagram->elements())
if (element->elementData().m_type != ElementData::Terminal
&& !element->actualLabel().isEmpty())
return element;
return nullptr;
}
//A non-terminal element of the fixture whose label field draws the
//label it is given, colon and all: a terminal's own kind of drawing
//is what the split changes, so the test needs an element which is
//none of the things drawing a label of their own -- a report (its
//label is where its partner is), a slave (its label follows its
//master) or a numbered symbol (a formula composes its label)
static Element *nonTerminalWithLabel(QETProject &project, const QString &label)
{
for (Diagram *diagram : project.diagrams())
for (Element *element : diagram->elements())
{
if (element->elementData().m_type == ElementData::Terminal) {
continue;
}
if (element->linkType() & (Element::Slave | Element::AllReport)) {
continue;
}
DiagramContext dc = element->elementInformations();
if (!dc.value(QStringLiteral("formula")).toString().isEmpty()) {
continue;
}
dc.addValue(QStringLiteral("label"), label);
element->setElementInformations(dc);
if (element->actualLabel() != label) {
continue;
}
//Only an element whose own label field draws the
//label it stores: everything else draws a text of
//its own, which this test would not understand
DynamicElementTextItem *field = fieldBoundTo(element,
QETInformation::ELMT_LABEL);
if (!field || field->toPlainText() != label) {
continue;
}
return element;
}
return nullptr;
}
private slots:
//The shipped default: one field with the whole label, the way it has
//always been drawn
void oneFieldKeepsTheWholeLabel()
{
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()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
project.setTerminalLabelSplit(true);
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()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
project.setTerminalLabelSplit(true);
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()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
project.setTerminalLabelSplit(true);
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);
project.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"));
//The setter itself tells the labels of the drawings already
//open to draw again: nothing else to ask for
project.setTerminalLabelSplit(false);
QCOMPARE(label->toPlainText(), QStringLiteral("X1:1"));
QCOMPARE(element->actualLabel(), QStringLiteral("X1:1"));
}
//A terminal whose name must not be drawn draws nothing of it, but
//keeps everything: the stored label is still there, and so is every
//use of it as data -- only the drawing changes
void hiddenNameKeepsEverythingStored()
{
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);
hideName(element);
QVERIFY(!element->labelShown());
QCOMPARE(element->actualLabel(), QStringLiteral("X1:1"));
QCOMPARE(element->labelForDisplay(), QString());
DynamicElementTextItem *label = fieldBoundTo(element, QETInformation::ELMT_LABEL);
QVERIFY(label);
QCOMPARE(label->toPlainText(), QString());
}
//Hidden, and the labelling is split: the terminal keeps its number,
//drawn on its own line while the symbol has no field for it, so
//hiding the name can never hide the number
void hiddenNameKeepsTheNumberWithSplit()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
project.setTerminalLabelSplit(true);
Element *element = terminalWithLabel(project, QStringLiteral("X1:1"));
QVERIFY(element);
hideName(element);
QCOMPARE(element->labelForDisplay(), QStringLiteral("1"));
QCOMPARE(element->terminalNumberForDisplay(), QStringLiteral("1"));
QCOMPARE(element->actualLabel(), QStringLiteral("X1:1"));
DynamicElementTextItem *label = fieldBoundTo(element, QETInformation::ELMT_LABEL);
QVERIFY(label);
QCOMPARE(label->toPlainText(), QStringLiteral("1"));
}
//Hidden, split, and the symbol has a field of its own for the
//number: the number is drawn in its own field, the label field
//draws nothing -- the number is never drawn twice
void hiddenNameLeavesTheNumberToItsOwnField()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
project.setTerminalLabelSplit(true);
Element *element = terminalWithLabel(project, QStringLiteral("X1:1"));
QVERIFY(element);
DynamicElementTextItem *number = addNumberField(element);
hideName(element);
QCOMPARE(element->labelForDisplay(), QString());
DynamicElementTextItem *label = fieldBoundTo(element, QETInformation::ELMT_LABEL);
QVERIFY(label);
QCOMPARE(label->toPlainText(), QString());
QCOMPARE(number->toPlainText(), QStringLiteral("1"));
}
//Only a terminal may have its name undrawn: another element which
//carries the same information still draws its name, whatever the
//file says
void nonTerminalAlwaysDrawsItsName()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
Element *element = aNonTerminal(project);
QVERIFY2(element, "the fixture has an element which is no terminal");
DiagramContext dc = element->elementInformations();
dc.addValue(QStringLiteral("label_shown"), QStringLiteral("false"));
element->setElementInformations(dc);
//Whatever the file says, a non-terminal draws its name:
//the information is not obeyed, and the drawn name is the
//stored one, composed as it always is
QVERIFY(element->labelShown());
QVERIFY(!element->actualLabel().isEmpty());
QCOMPARE(element->labelForDisplay(), element->actualLabel());
}
//The labelling is drawn split for an element of the terminal type
//only: the colon in the label of any other element ("-K1:13") is
//part of the label, not a split waiting to happen
void splitLeavesNonTerminalsWithColonAlone()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
project.setTerminalLabelSplit(true);
Element *element = nonTerminalWithLabel(project, QStringLiteral("-K1:13"));
QVERIFY2(element,
"the fixture has an element which is no terminal and draws the label it is given");
QCOMPARE(element->actualLabel(), QStringLiteral("-K1:13"));
QCOMPARE(element->labelForDisplay(), QStringLiteral("-K1:13"));
QVERIFY(element->terminalNumberForDisplay().isEmpty());
DynamicElementTextItem *label = fieldBoundTo(element, QETInformation::ELMT_LABEL);
QVERIFY2(label, "the symbol has a field for its label");
QCOMPARE(label->toPlainText(), QStringLiteral("-K1:13"));
}
//The field of a hidden name does not stay on the sheet as an empty
//frame to select: it leaves the sheet with the box and comes back
//with it, both ways through the same signal
void hiddenNameHidesTheLabelField()
{
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 *label = fieldBoundTo(element, QETInformation::ELMT_LABEL);
QVERIFY(label);
QVERIFY(label->isVisible());
hideName(element);
QVERIFY(!label->isVisible());
//The box ticked again: the element drops the information,
//and the field is drawn as it was before
DiagramContext dc = element->elementInformations();
dc.remove(QStringLiteral("label_shown"));
element->setElementInformations(dc);
QVERIFY(label->isVisible());
QCOMPARE(label->toPlainText(), QStringLiteral("X1:1"));
}
//The nomenclature/CSV export builds its columns from the element info
//keys, but its query has no terminal_number column: offering the key
//there made the export fail. The key itself stays an info key, so the
//Bauteileditor keeps offering %{terminal_number} as a text variable
void csvExportDoesNotOfferTerminalNumber()
{
ElementQueryWidget widget;
QListWidget *offer = widget.findChild<QListWidget *>(QStringLiteral("m_var_list"));
QVERIFY2(offer, "the widget which lists the exportable columns");
QStringList offered;
for (int row = 0; row < offer->count(); ++row)
offered.append(offer->item(row)->text());
const QString terminal_number = QETInformation::translatedInfoKey(
QETInformation::ELMT_TERMINAL_NUMBER);
QVERIFY2(!offered.contains(terminal_number),
qPrintable(QStringLiteral("The export must not offer \"%1\"")
.arg(terminal_number)));
//The other columns are still offered...
QVERIFY(offered.contains(
QETInformation::translatedInfoKey(QETInformation::ELMT_LABEL)));
//...and the variable is still a text variable
QVERIFY(QETInformation::elementInfoKeys().contains(
QETInformation::ELMT_TERMINAL_NUMBER));
QCOMPARE(QETInformation::elementInfoToVar(QETInformation::ELMT_TERMINAL_NUMBER),
QStringLiteral("%{terminal_number}"));
}
//The properties of a terminal have no row for its number: the number
//is derived from the label, so a row there would offer an edit which
//stores a value nothing reads -- the export has no column for it, and
//the drawing takes the number from the label
void propertiesHaveNoRowForTheTerminalNumber()
{
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);
//The properties of one element...
ElementInfoWidget one(element);
QStringList one_rows;
const QList<ElementInfoPartWidget *> parts = one.findChildren<ElementInfoPartWidget *>();
for (const ElementInfoPartWidget *part : parts)
one_rows << part->key();
QVERIFY2(one_rows.contains(QETInformation::ELMT_LABEL),
"The other properties are still offered");
QVERIFY2(!one_rows.contains(QETInformation::ELMT_TERMINAL_NUMBER),
"The terminal number is no property of the element");
//...and the properties of several elements at once
MultiElementInfoWidget many({element});
QStringList rows;
const QList<QLabel *> labels = many.findChildren<QLabel *>();
for (const QLabel *label : labels)
rows << label->text();
QVERIFY2(rows.contains(QETInformation::translatedInfoKey(QETInformation::ELMT_LABEL)),
"The other properties are still offered");
QVERIFY2(!rows.contains(
QETInformation::translatedInfoKey(QETInformation::ELMT_TERMINAL_NUMBER)),
"The terminal number is no property of the element");
}
};
QET_APP_TEST_MAIN(tst_terminallabeldisplay)
#include "tst_terminallabeldisplay.moc"