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().
This commit is contained in:
Kellermorph
2026-09-16 12:09:05 +02:00
parent 451f8c3296
commit b7f516d485
2 changed files with 105 additions and 0 deletions
+100
View File
@@ -28,6 +28,8 @@
#include <QFileInfo>
#include <QMessageBox>
#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<Element *>(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<Conductor *>(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);
}
}
}
}