Give symbols saved without a uuid the same one on every load

A symbol saved without a uuid got a random one from Element::fromXml()
on every load, and the next save wrote it out: two loads of the same file
gave the same symbol two identities, and anything pointing at it by uuid
(a script, a comparison of two versions, a wire's identity) could not
follow it from one session to the next.

When a folio is loaded, such a symbol now gets a UUID v5 derived from
what it is and where it sits: its type, its position on the folio and its
orientation. Never the folio's index, so inserting or moving a folio does
not change it. Identical symbols stacked on one spot, or a copied folio,
are told apart by a counter kept per project (QETProject::derivedUuid()),
in load order among those symbols alone. A paste still renews uuids.

Symbols that have a uuid in the file keep it. All 24 example projects
already have one for every symbol, so they are unchanged; with the
symbols' uuids stripped, each saves byte-for-byte the same twice (master:
different every time).

tst_derivedsymboluuid runs --resave on a fixture with its uuids stripped:
same uuids on every load, same after a folio is inserted in front, saved
uuids kept, stacked copies differ. The first two fail without this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-09-28 20:13:31 +13:00
parent 5ca047a05d
commit 0721b42e21
6 changed files with 237 additions and 0 deletions
+17
View File
@@ -1676,6 +1676,23 @@ bool Diagram::fromXml(QDomElement &document,
delete nvel_elmt;
qDebug() << QStringLiteral("Diagram::fromXml() : Le chargement des parametres d'un element a echoue");
} else {
//A symbol saved without a uuid got a random one from
//Element::fromXml(): a different identity on every load,
//written out on the next save. Derive it instead from what
//the symbol is and where it sits on its folio -- never from
//the folio's index, so inserting or moving a folio does not
//change it. Only for a folio being loaded: a paste renews
//uuids anyway.
if (consider_informations && m_project
&& QUuid(element_xml.attribute(QStringLiteral("uuid"))).isNull()) {
nvel_elmt->setUuid(m_project->derivedUuid(
QStringLiteral("element"),
QStringList{type_id,
element_xml.attribute(QStringLiteral("x")),
element_xml.attribute(QStringLiteral("y")),
element_xml.attribute(QStringLiteral("orientation"))}
.join(QLatin1Char('\n'))));
}
ItemGroups::setGroup(nvel_elmt, ItemGroups::read(element_xml));
added_elements << nvel_elmt;
}
+1
View File
@@ -245,6 +245,7 @@ class Element : public QetGraphicsItem
QString linkTypeToString() const;
void newUuid() {m_uuid = QUuid::createUuid();} //create new uuid for this element
void setUuid(const QUuid &uuid) {m_uuid = uuid;}
protected:
void drawAxes(QPainter *, const QStyleOptionGraphicsItem *);
+21
View File
@@ -275,6 +275,27 @@ QUuid QETProject::uuid() const
return m_uuid;
}
/**
@brief QETProject::derivedUuid
A uuid for an item of this project that was saved without one, the same
on every load of the same file.
@p key describes the item by what it is, never by its place in the file
or its folio's index: inserting or moving a folio must not change it.
Items with the same @p kind and @p key anywhere in the project (a copied
folio, two identical symbols stacked on one spot) are told apart by a
counter, in load order among those items alone.
@return a UUID v5, which cannot collide with the v4 uuids given to new
items
*/
QUuid QETProject::derivedUuid(const QString &kind, const QString &key)
{
static const QUuid derived_ns(QStringLiteral("{7d1e9c3a-5b2f-4e8a-9c61-2f4b8d0e6a17}"));
const QString full = kind + QLatin1Char('\n') + key;
const int n = m_derived_uuid_keys[full]++;
return QUuid::createUuidV5(derived_ns,
n ? full + QLatin1Char('\n') + QString::number(n) : full);
}
/**
@brief QETProject::init
*/
+2
View File
@@ -107,6 +107,7 @@ class QETProject : public QObject
ProjectPropertiesHandler& projectPropertiesHandler();
projectDataBase *dataBase();
QUuid uuid() const;
QUuid derivedUuid(const QString &kind, const QString &key);
ProjectState state() const;
QList<Diagram *> diagrams() const;
int folioIndex(const Diagram *) const;
@@ -365,6 +366,7 @@ class QETProject : public QObject
QFuture<bool> m_backup_future;
KAutoSaveFile m_backup_file;
QUuid m_uuid = QUuid::createUuid();
QHash<QString, int> m_derived_uuid_keys;
projectDataBase m_data_base;
QVector<TerminalStrip *> m_terminal_strip_vector;