mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-19 20:44:16 +02:00
Save a symbol's links from the project database
A symbol's <links_uuids> are now what the project database's link rows list for it, in their order (projectDataBase::savedLinks()), instead of a list the save built itself from the symbol's partners: QETZ-DB-FIRST stage 2a, the save reads links from the database. A symbol the database does not hold (saved outside a project) still lists them itself. The rows leave out a partner the project no longer has, so removing a folio with every folio built no longer saves links to its symbols, as lazy folios already did (the links part of F113). Every other save is unchanged. tst_resaveunchanged removedFolioLeavesNoLinkToIt: affuteuse_250h with every folio built, folio 3 removed: 4 links named symbols no longer in the file, none now. Needs #1522 (each symbol its own uuid) and #1523 (rows in saved order); local only until both are merged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XCdg2Er2vYhB8Vwa5G7fSR
This commit is contained in:
@@ -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<std::pair<QUuid, int>> *links)
|
||||
{
|
||||
if (!element || !m_placed_elements.contains(element->uuid(), const_cast<Element *>(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
|
||||
|
||||
@@ -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<std::pair<QUuid, int>> *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<QPointer<Element>> 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.
|
||||
|
||||
@@ -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<std::pair<QUuid, int>> links;
|
||||
for (const SavedLink &link : linksToSave(diagram() && diagram()->project()
|
||||
? diagram()->project()->waitingLinkTargets() : QSet<QUuid>()))
|
||||
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<QUuid>()))
|
||||
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)) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <QtTest>
|
||||
#include <QSet>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
@@ -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<QString> symbols;
|
||||
auto it = QRegularExpression(QStringLiteral("<element [^>]*\\buuid=\"(\\{[0-9a-f-]+\\})\"")).globalMatch(text);
|
||||
while (it.hasNext()) symbols.insert(it.next().captured(1));
|
||||
QList<QString> links;
|
||||
auto lt = QRegularExpression(QStringLiteral("<link_uuid [^>]*\\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
|
||||
|
||||
Reference in New Issue
Block a user