Symbol editor: give old terminals their derived uuid, not a random one

A symbol file whose terminals have no uuid got a random one for each
terminal when opened in the element editor (PartTerminal's constructor),
written on save. Every copy of the same old symbol therefore ended up
with different terminal uuids, none of them the one a project gives the
same terminals on opening (TerminalUuids::fillMissing()).

ElementScene::loadContent() now reads a copy of the definition filled by
TerminalUuids::fillMissingInDefinition(), the same rule as a project,
including the next occurrence for the second of two terminals at one
point. Terminals that have a uuid keep it; a paste still renews them all
(PastePartsCommand).

Checked in the editor: 6es7_212-1ae40-0xb0__p3.elmt (no terminal uuids
in the collection) and tm3saf5r_layout.elmt with its uuids stripped (two
terminals at one point), opened, nudged back and forth, saved: this build
writes exactly the derived values (computed independently in Python),
the previous one random ones. Select all, copy, paste, save: 6 terminals,
6 distinct uuids.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-09-29 09:37:57 +13:00
parent 90084ecf40
commit 2815dbaf47
4 changed files with 60 additions and 8 deletions
+28 -8
View File
@@ -28,12 +28,12 @@ namespace {
@return the <terminal> of the definition held by the embedded
collection element @p collection_element
*/
QList<QDomElement> terminalsOf(const QDomElement &collection_element)
//The <terminal> of the <definition> @p definition
QList<QDomElement> definitionTerminals(const QDomElement &definition)
{
QList<QDomElement> terminals;
const QDomElement description = collection_element
.firstChildElement(QStringLiteral("definition"))
.firstChildElement(QStringLiteral("description"));
const QDomElement description =
definition.firstChildElement(QStringLiteral("description"));
for (QDomElement t = description.firstChildElement(QStringLiteral("terminal"));
!t.isNull();
t = t.nextSiblingElement(QStringLiteral("terminal"))) {
@@ -42,6 +42,12 @@ QList<QDomElement> terminalsOf(const QDomElement &collection_element)
return terminals;
}
QList<QDomElement> terminalsOf(const QDomElement &collection_element)
{
return definitionTerminals(
collection_element.firstChildElement(QStringLiteral("definition")));
}
//Place and orientation, "10" and "10.0" being the same place
QString terminalPlace(const QDomElement &terminal)
{
@@ -61,10 +67,10 @@ int orientationOf(const QDomElement &terminal)
return 0;
}
//The <terminal> of one element definition get a uuid where missing
int fillDefinition(const QDomElement &collection_element)
//The <terminal> of one <definition> get a uuid where missing
int fillDefinition(const QDomElement &definition)
{
const QList<QDomElement> terminals = terminalsOf(collection_element);
const QList<QDomElement> terminals = definitionTerminals(definition);
QSet<QUuid> taken;
for (const QDomElement &t : terminals) {
const QUuid uuid(t.attribute(QStringLiteral("uuid")));
@@ -105,7 +111,8 @@ int fillDirectory(const QDomElement &directory)
if (child.tagName() == QLatin1String("category")) {
filled += fillDirectory(child);
} else if (child.tagName() == QLatin1String("element")) {
filled += fillDefinition(child);
filled += fillDefinition(
child.firstChildElement(QStringLiteral("definition")));
}
}
return filled;
@@ -252,3 +259,16 @@ int TerminalUuids::fillMissing(const QDomElement &collection_root)
{
return fillDirectory(collection_root);
}
/**
@brief TerminalUuids::fillMissingInDefinition
fillMissing() for a single element definition (@p definition is its
<definition>), as the element editor reads it from a file: an old
symbol opened and saved there gets the uuids a project gives the same
terminals, not random ones that would make every copy of it differ.
@return the number of terminals given a uuid
*/
int TerminalUuids::fillMissingInDefinition(const QDomElement &definition)
{
return fillDefinition(definition);
}
@@ -26,6 +26,7 @@ namespace TerminalUuids
{
QUuid derived(qreal x, qreal y, int orientation, int occurrence = 0);
int fillMissing(const QDomElement &collection_root);
int fillMissingInDefinition(const QDomElement &definition);
void keep(const QDomElement &old_element, QDomElement &new_element);
void keepInDirectory(const QDomElement &old_directory,
QDomElement &new_directory);
+8
View File
@@ -17,6 +17,7 @@
*/
#include "elementscene.h"
#include "../ElementsCollection/terminaluuids.h"
#include "../NameList/ui/namelistdialog.h"
#include "../NameList/ui/namelistwidget.h"
#include "../QPropertyUndoCommand/qpropertyundocommand.h"
@@ -1196,6 +1197,13 @@ ElementContent ElementScene::loadContent(const QDomDocument &xml_document)
if (root.tagName() != "definition" || root.attribute("type") != "element")
return(loaded_parts);
//Terminals saved without a uuid get the one a project gives them
//(see TerminalUuids::fillMissing()), not a random one: the same old
//symbol then has the same terminal uuids wherever it is copied.
//On a copy, the document is the caller's. A paste renews them all.
root = root.cloneNode(true).toElement();
TerminalUuids::fillMissingInDefinition(root);
//Load the graphic description of the element
for (QDomNode node = root.firstChild() ; !node.isNull() ; node = node.nextSibling())
{
+23
View File
@@ -300,6 +300,29 @@ private slots:
QCOMPARE(TerminalUuids::fillMissing(root), 0);
}
// The element editor fills a definition read from a file with exactly
// what a project fills the same definition with.
void fillMissingInDefinitionMatchesProject()
{
QDomDocument doc;
const QString own = QStringLiteral("{0f5d4b0c-2f7e-4a55-9a51-8c3a3e1c2d11}");
QDomElement in_project = symbol(doc, {{"0", "10", "s"},
{"0", "10", "s"},
{"5", "0", "e", own},
{"-3", "0", "w"}});
QDomElement root = doc.createElement(QStringLiteral("collection"));
root.appendChild(in_project);
QDomElement definition = in_project.firstChildElement(QStringLiteral("definition"))
.cloneNode(true).toElement();
QCOMPARE(TerminalUuids::fillMissingInDefinition(definition), 3);
QCOMPARE(TerminalUuids::fillMissing(root), 3);
QCOMPARE(uuids(definition), uuids(in_project));
QCOMPARE(uuids(definition).at(2), own);
QCOMPARE(uuids(definition).at(1), TerminalUuids::derived(0, 10, 2, 1).toString());
QCOMPARE(TerminalUuids::fillMissingInDefinition(definition), 0);
}
// An example whose symbols have no terminal uuids and whose wires are
// all in the numbered form: once saved, every terminal has a uuid,
// every wire names its ends by uuid, nothing is lost, and saving again