From b7f516d485b0bb0fab3e9f98de9d5d691a58be8b Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Wed, 16 Sep 2026 12:09:05 +0200 Subject: [PATCH] Clean up stale data when duplicating diagram pages When duplicating a diagram page, copied slave elements retain stale data from the source: labels, descriptions, link references, and PLC master information (type, address, function, comment, cross-ref, timer values) remain in the copies. Fix by adding clearPendingLinks() to Element to prevent copies from linking back to source elements via stale UUIDs, and by cleaning up copied element data after fromXml(): - Slaves always lose their label, formula, comment, location, and PLC master data, since their text comes from a master element which is not available on the copy. The displayed text on slave elements is cleared directly via setPlainText(), but UserText items (free text typed by the user) are preserved. - For PLC slaves, setElementInformations() is called without m_block_alignment so that elementInfoChanged() can run finishAlignment() to correctly adjust text positions for the cleared content. - Non-slave elements respect the existing erase-label-on-copy preference, same as PasteDiagramCommand::redo(). - Conductor labels are also reset when erase-label-on-copy is active, matching PasteDiagramCommand::redo() (issue #413). - Text alignment is preserved by wrapping setElementInformations() with m_block_alignment for non-slave elements, same as Element::fromXml(). --- sources/elementspanelwidget.cpp | 100 ++++++++++++++++++++++++++++++ sources/qetgraphicsitem/element.h | 5 ++ 2 files changed, 105 insertions(+) diff --git a/sources/elementspanelwidget.cpp b/sources/elementspanelwidget.cpp index 018064da5..4aa61b7b1 100644 --- a/sources/elementspanelwidget.cpp +++ b/sources/elementspanelwidget.cpp @@ -28,6 +28,8 @@ #include #include #include "qetgraphicsitem/element.h" +#include "qetgraphicsitem/dynamicelementtextitem.h" +#include "qetinformation.h" /* When the ENABLE_PANEL_WIDGET_DND_CHECKS flag is set, the panel @@ -668,6 +670,10 @@ void ElementsPanelWidget::duplicateDiagram() new_diagram->fromXml(diagram_elmt, QPointF(0, 0), false, nullptr); + QSettings settings; + bool erase_labels = settings.value( + "diagramcommands/erase-label-on-copy", true).toBool(); + for (QGraphicsItem *item : new_diagram->items()) { if (Element *elmt = dynamic_cast(item)) { // The XML round-trip kept the source elements' uuids. Give the @@ -688,6 +694,90 @@ void ElementsPanelWidget::duplicateDiagram() { new_diagram->restoreText(elmt); } + + // Clear pending links so copies don't link back to + // the source elements via stale UUIDs. + elmt->clearPendingLinks(); + + // Clean up copied element data: + // 1. Slaves always lose label/formula/comment/location + // and PLC master data — their text comes from a + // master element not available on the copy. + // 2. Non-slaves: honour "erase-label-on-copy". + + DiagramContext dc = elmt->elementInformations(); + bool changed = false; + + // Slaves always lose their label/BMK and PLC data — + // their text comes from the master, which is not + // available on the copied element. + if (elmt->linkType() == Element::Slave) { + dc.addValue("formula", ""); + dc.addValue("label", ""); + dc.addValue("comment", ""); + dc.addValue("location", ""); + changed = true; + + for (const QString &key : { + QETInformation::ELMT_PLC_TYPE, + QETInformation::ELMT_PLC_ADDRESS, + QETInformation::ELMT_PLC_FUNCTION, + QETInformation::ELMT_PLC_COMMENT, + QETInformation::ELMT_PLC_CROSSREF, + QETInformation::ELMT_PLC_TC, + QETInformation::ELMT_PLC_T1, + QETInformation::ELMT_PLC_T2, + QETInformation::ELMT_PLC_T3, + QETInformation::ELMT_PLC_T4, + QStringLiteral("xref")}) { + if (dc.contains(key)) { + dc.remove(key); + changed = true; + } + } + } + // Non-slaves: honour the "erase-label-on-copy" preference. + else if (erase_labels) { + dc.addValue("formula", ""); + dc.addValue("label", ""); + dc.addValue("comment", ""); + dc.addValue("location", ""); + changed = true; + } + + if (changed) { + const bool is_slave = (elmt->linkType() == Element::Slave); + + // Block alignment during setElementInformations + // for non-slaves, same as Element::fromXml() (line 890-896). + // For PLC slaves, don't block — elementInfoChanged() + // fires (elementUseForInfo returns self) and needs + // finishAlignment() to adjust positions for the + // new (empty) text. + if (!is_slave) { + for (auto deti : elmt->dynamicTextItems()) + deti->m_block_alignment = true; + } + elmt->setElementInformations(dc); + for (auto deti : elmt->dynamicTextItems()) + deti->m_block_alignment = false; + + // For non-PLC slaves, elementInfoChanged() doesn't + // fire (no linked master), so the text items keep + // their original text. Clear them directly. + // For PLC slaves, elementInfoChanged() already + // cleared the text above. Skip UserText items + // (free text typed by the user) in both cases. + if (is_slave) { + for (auto deti : elmt->dynamicTextItems()) { + if (deti->textFrom() != DynamicElementTextItem::UserText) { + deti->m_block_alignment = true; + deti->setPlainText(QString()); + deti->m_block_alignment = false; + } + } + } + } } else if (Conductor *cond = dynamic_cast(item)) { // Same reasoning for conductors: conductor.uuid is the PRIMARY @@ -695,6 +785,16 @@ void ElementsPanelWidget::duplicateDiagram() // so a duplicated uuid fails and the wire silently disappears // from the wiring list and the per-element wire count. cond->newUuid(); + + // Reset conductor labels when "erase-label-on-copy" is + // active, matching PasteDiagramCommand::redo() — + // "erase on copy" means erase, not "replace with the + // project's default new-conductor text" (see issue #413). + if (erase_labels) { + ConductorProperties cp = cond->properties(); + cp.text = ""; + cond->setProperties(cp); + } } } } diff --git a/sources/qetgraphicsitem/element.h b/sources/qetgraphicsitem/element.h index 9e94d2c4e..9a5b46c2c 100644 --- a/sources/qetgraphicsitem/element.h +++ b/sources/qetgraphicsitem/element.h @@ -119,6 +119,11 @@ class Element : public QetGraphicsItem QList conductors() const; QList> AlignedFreeTerminals() const; + void clearPendingLinks() { + tmp_uuids_link.clear(); + m_group_index_map.clear(); + } + //METHODS related to information DiagramContext elementInformations()const {return m_data.m_informations;}