diff --git a/sources/ElementsCollection/terminaluuids.cpp b/sources/ElementsCollection/terminaluuids.cpp index fe9141f47..c5b7ba098 100644 --- a/sources/ElementsCollection/terminaluuids.cpp +++ b/sources/ElementsCollection/terminaluuids.cpp @@ -28,12 +28,12 @@ namespace { @return the of the definition held by the embedded collection element @p collection_element */ -QList terminalsOf(const QDomElement &collection_element) + //The of the @p definition +QList definitionTerminals(const QDomElement &definition) { QList 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 terminalsOf(const QDomElement &collection_element) return terminals; } +QList 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 of one element definition get a uuid where missing -int fillDefinition(const QDomElement &collection_element) + //The of one get a uuid where missing +int fillDefinition(const QDomElement &definition) { - const QList terminals = terminalsOf(collection_element); + const QList terminals = definitionTerminals(definition); QSet 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 + ), 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); +} diff --git a/sources/ElementsCollection/terminaluuids.h b/sources/ElementsCollection/terminaluuids.h index d5c94f517..f9892b0a6 100644 --- a/sources/ElementsCollection/terminaluuids.h +++ b/sources/ElementsCollection/terminaluuids.h @@ -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); diff --git a/sources/editor/UndoCommand/openelmtcommand.cpp b/sources/editor/UndoCommand/openelmtcommand.cpp index f94ab4c2f..ff28b4adc 100644 --- a/sources/editor/UndoCommand/openelmtcommand.cpp +++ b/sources/editor/UndoCommand/openelmtcommand.cpp @@ -17,6 +17,7 @@ */ #include "openelmtcommand.h" #include "../elementscene.h" +#include "../graphicspart/partterminal.h" #include #include @@ -52,6 +53,15 @@ void OpenElmtCommand::redo() if (m_first_redo) { m_scene->fromXml(m_document, QPointF(), true, &m_graphics_item); + //The terminals are added to an element that may already hold + //the same ones -- importing the same symbol twice, or two old + //symbols whose terminals get the same derived uuid -- so they + //get new uuids, as a paste gives them (PastePartsCommand). + for (QGraphicsItem *qgi : std::as_const(m_graphics_item)) { + if (qgi->type() == PartTerminal::Type) { + static_cast(qgi)->setNewUuid(); + } + } m_scene->qgiManager().manage(m_graphics_item); m_first_redo = false; diff --git a/sources/editor/elementscene.cpp b/sources/editor/elementscene.cpp index 534d435c9..c0105ae83 100644 --- a/sources/editor/elementscene.cpp +++ b/sources/editor/elementscene.cpp @@ -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()) { diff --git a/tests/qttest/tst_terminaluuids.cpp b/tests/qttest/tst_terminaluuids.cpp index b9a7fe1fd..35caba47f 100644 --- a/tests/qttest/tst_terminaluuids.cpp +++ b/tests/qttest/tst_terminaluuids.cpp @@ -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