diff --git a/sources/dataBase/projectdatabase.cpp b/sources/dataBase/projectdatabase.cpp index 06811c9db..2346c6368 100644 --- a/sources/dataBase/projectdatabase.cpp +++ b/sources/dataBase/projectdatabase.cpp @@ -3954,6 +3954,39 @@ QStringList projectDataBase::linkRowMismatches() return mismatches; } +/** + @brief projectDataBase::savedLinks + The links @p element saves, read from its link rows, in their order + (insertLinkRows()): what Element::toXml() writes (QETZ-DB-FIRST stage + 2a). A partner the project no longer has, on a removed folio, has no + row, so it is not saved. + @return false if the database does not hold @p element (not placed in + this project): the caller then takes the links from the symbol itself +*/ +bool projectDataBase::savedLinks(const Element *element, QList> *links) +{ + if (!element || !m_placed_elements.contains(element->uuid(), const_cast(element))) { + return false; + } + flushLinks(); + if (m_saved_links_query.lastQuery().isEmpty()) { + m_saved_links_query = QSqlQuery(m_data_base); + m_saved_links_query.prepare(QStringLiteral("SELECT linked_uuid, group_index FROM link " + "WHERE element_uuid = :uuid ORDER BY ord, rowid")); + } + m_saved_links_query.bindValue(QStringLiteral(":uuid"), element->uuid().toString()); + if (!m_saved_links_query.exec()) { + qDebug() << "projectDataBase::savedLinks error : " << m_saved_links_query.lastError(); + return false; + } + links->clear(); + while (m_saved_links_query.next()) { + links->append({QUuid(m_saved_links_query.value(0).toString()), + m_saved_links_query.value(1).isNull() ? -1 : m_saved_links_query.value(1).toInt()}); + } + return true; +} + /** @brief projectDataBase::waitingSymbols @return the symbols on folios not built yet (lazy folios): partners a diff --git a/sources/dataBase/projectdatabase.h b/sources/dataBase/projectdatabase.h index a34d52536..6c1e6b195 100644 --- a/sources/dataBase/projectdatabase.h +++ b/sources/dataBase/projectdatabase.h @@ -155,6 +155,7 @@ class projectDataBase : public QObject void conductorUuidChanged(Conductor *conductor, const QUuid &old_uuid); QStringList conductorPropertiesMismatches() const; QStringList linkRowMismatches(); + bool savedLinks(const Element *element, QList> *links); //Fill the three stores from a project's document before its //folios are built, then, once @@ -330,6 +331,7 @@ class projectDataBase : public QObject //written: a link is made first and its contact group set after, //so the rows are written when next read, as drawing items are. QList> m_dirty_link_elements; + QSqlQuery m_saved_links_query; //Elements moved since their folio cell was last written: a move //sends xChanged and yChanged for every step of a drag, so the //cell is written when next read, as drawing items are. diff --git a/sources/qetgraphicsitem/element.cpp b/sources/qetgraphicsitem/element.cpp index f2d61692f..459f7b593 100644 --- a/sources/qetgraphicsitem/element.cpp +++ b/sources/qetgraphicsitem/element.cpp @@ -1436,12 +1436,19 @@ QDomElement Element::toXml( QDomElement inputs = document.createElement(QStringLiteral("inputs")); element.appendChild(inputs); - //if this element is linked to other elements, - //save the uuid of each other elements (linksToSave()) + //if this element is linked to other elements, save the uuid of each: + //as the project database's link rows list them (QETZ-DB-FIRST stage + //2a), or, for a symbol the database does not hold, as it lists them + //itself (linksToSave()) QList> links; - for (const SavedLink &link : linksToSave(diagram() && diagram()->project() - ? diagram()->project()->waitingLinkTargets() : QSet())) - links.append({link.uuid, link.group_index}); + projectDataBase *database = diagram() && diagram()->project() + ? diagram()->project()->dataBase() : nullptr; + if (!database || !database->savedLinks(this, &links)) { + links.clear(); + for (const SavedLink &link : linksToSave(diagram() && diagram()->project() + ? diagram()->project()->waitingLinkTargets() : QSet())) + links.append({link.uuid, link.group_index}); + } if (!links.isEmpty()) { QDomElement links_uuids = document.createElement(QStringLiteral("links_uuids")); for (const auto &link : std::as_const(links)) { diff --git a/tests/qttest/tst_resaveunchanged.cpp b/tests/qttest/tst_resaveunchanged.cpp index 5fdeff080..2613e8699 100644 --- a/tests/qttest/tst_resaveunchanged.cpp +++ b/tests/qttest/tst_resaveunchanged.cpp @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include @@ -481,6 +482,46 @@ private slots: QVERIFY2(out.contains(QStringLiteral("link rows: 0 differ")), qPrintable(out.right(600))); } + // Removing a folio with every folio built saves no link to its symbols: + // the save takes each symbol's links from the project database, which + // drops a partner the project no longer has (F113; lazy folios already + // saved it so). affuteuse_250h, folio 3 removed: its coil KA1 is linked + // to contacts on other folios. + void removedFolioLeavesNoLinkToIt() + { +#ifndef QET_HAS_SCRIPTING + QSKIP("needs --run: this QElectroTech is built without Qt Qml"); +#endif + const QString saved = m_dir.filePath(QStringLiteral("removed%1.qet").arg(m_run)); + const QString js = m_dir.filePath(QStringLiteral("removed%1.js").arg(m_run)); + QFile script(js); + QVERIFY(script.open(QIODevice::WriteOnly)); + script.write(QStringLiteral( + "qet.log('REMOVED ' + qet.removeFolio(3));\n" + "qet.save('%1');\n").arg(saved).toUtf8()); + script.close(); + const QString out = runChecked({QStringLiteral("--run"), js, + QStringLiteral(QET_EXAMPLES_DIR "/affuteuse_250h.qet")}, true); + QVERIFY2(out.contains(QStringLiteral("REMOVED true")), qPrintable(out.right(600))); + + QFile file(saved); + QVERIFY(file.open(QIODevice::ReadOnly)); + const QString text = QString::fromUtf8(file.readAll()); + //Placed symbols carry a uuid; symbol definitions in the + //collection do not + QSet symbols; + auto it = QRegularExpression(QStringLiteral("]*\\buuid=\"(\\{[0-9a-f-]+\\})\"")).globalMatch(text); + while (it.hasNext()) symbols.insert(it.next().captured(1)); + QList links; + auto lt = QRegularExpression(QStringLiteral("]*\\buuid=\"(\\{[0-9a-f-]+\\})\"")).globalMatch(text); + while (lt.hasNext()) links << lt.next().captured(1); + QVERIFY(!symbols.isEmpty() && !links.isEmpty()); + int dangling = 0; + for (const QString &link : std::as_const(links)) + if (!symbols.contains(link)) ++dangling; + QCOMPARE(dangling, 0); + } + // The stores filled from the document before the folios are built // hold what building them gives, for a // project saved by this version (every symbol, folio and wire carries