From e7fd820db6660b1bcac4c6850a87d765e9acb600 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Mon, 12 Oct 2026 08:46:31 +1300 Subject: [PATCH] Add, move or remove a folio without building the others (stage D5) Adding, moving or removing a folio rewrote every folio's position in the project database through diagrams(), which builds every folio: 3 s on a 50-folio project opened lazily. A folio not built has its position and title block already, so folios() is enough. The list of tables a table can continue builds only the folios holding a table (from the index of a .qetz), and the wiring list's count of wires left out looks at built folios only: reading a wire drops it unless both its ends are found. tst_lazyfolios: a folio added, moved and removed builds no other, with the folios' rows as when every folio is built (fails with diagrams(): 47 built); the tables listed as when built, building the 12 folios that hold one (fails when every folio is built). ctest 96/96 eager and lazy; 342 corpus projects save the same in all four arms. Co-Authored-By: Claude Opus 5.5 --- sources/dataBase/projectdatabase.cpp | 9 +++-- sources/elementprovider.cpp | 9 ++++- sources/qetproject.cpp | 21 +++++++++++ sources/qetproject.h | 1 + tests/qttest/lazy_folios_probe.cpp | 55 ++++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 4 deletions(-) diff --git a/sources/dataBase/projectdatabase.cpp b/sources/dataBase/projectdatabase.cpp index 80c5d1ec5..007bad900 100644 --- a/sources/dataBase/projectdatabase.cpp +++ b/sources/dataBase/projectdatabase.cpp @@ -1536,8 +1536,10 @@ int projectDataBase::excludedConductorCount() const return 0; } + //A folio not built (QET_LAZY_FOLIOS) has none: reading a wire + //drops it unless both its ends are found (Conductor::fromXml()) int count = 0; - for (auto *diagram : m_project->diagrams()) + for (auto *diagram : m_project->builtFolios()) { const auto conductor_list = diagram->conductors(); for (auto *conductor : conductor_list) @@ -1722,11 +1724,12 @@ void projectDataBase::flushElementPositions() /** @brief projectDataBase::updateFolioPositions Write every folio's position and folio number again, after a folio was - added, removed or moved. + added, removed or moved. A folio not built yet (QET_LAZY_FOLIOS) has + its position and title block already: it is not built for this. */ void projectDataBase::updateFolioPositions() { - for (auto diagram : m_project->diagrams()) + for (auto diagram : m_project->folios()) { m_diagram_order_changed.bindValue(":pos", m_project->folioIndex(diagram)+1); m_diagram_order_changed.bindValue(":uuid", diagram->uuid().toString()); diff --git a/sources/elementprovider.cpp b/sources/elementprovider.cpp index a493fd7fe..174151811 100644 --- a/sources/elementprovider.cpp +++ b/sources/elementprovider.cpp @@ -199,7 +199,14 @@ QVector ElementProvider::table( } } - for (auto d : allFolios()) { + //Only the folios holding a table are built for it + QList folios = m_diagram_list; + if (m_project) { + m_project->buildFoliosHoldingTables(); + folios = m_project->builtFolios(); + folios.removeOne(m_excluded); + } + for (auto d : std::as_const(folios)) { for (auto item_ : d->items()) { if(item_->type() == QetGraphicsTableItem::Type) diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp index a63de72a7..34e46e67d 100644 --- a/sources/qetproject.cpp +++ b/sources/qetproject.cpp @@ -2978,6 +2978,27 @@ bool QETProject::usesElement(const ElementsLocation &location) const return(unbuiltFolioElements().contains(location)); } +/** + @brief QETProject::buildFoliosHoldingTables + Build the folios not built yet (QET_LAZY_FOLIOS) that hold a drawn + table, found from the index the project was opened from when it has + one: what a list of the project's tables needs, and no more +*/ +void QETProject::buildFoliosHoldingTables() +{ + QList to_build; + for (Diagram *d : std::as_const(m_diagrams_list)) + if (!d->isLoaded() && unbuiltFolioHasTables(d)) + to_build << d; + if (to_build.isEmpty()) { + return; + } + if (qEnvironmentVariableIntValue("QET_LAZY_FOLIOS_TRACE") == 1) { + qWarning().noquote() << "lazy folios:" << to_build.size() << "folios built for their tables"; + } + buildFolios(to_build); +} + /** @brief QETProject::unbuiltFolioHasTables @return true if @p folio, not built yet (QET_LAZY_FOLIOS), has a drawn diff --git a/sources/qetproject.h b/sources/qetproject.h index 61f9729d7..509a532a8 100644 --- a/sources/qetproject.h +++ b/sources/qetproject.h @@ -135,6 +135,7 @@ class QETProject : public QObject void buildPartnerSymbols(const QList &folios, const QSet &partners); void buildFoliosHolding(ElementData::Types kinds, bool free_only); void buildFoliosHoldingSymbols(const QSet &symbols); + void buildFoliosHoldingTables(); QSet symbolsOnUnbuiltFolios() const; /// While saving: the symbols on folios not built a built symbol /// may be saved linked to (Element::toXml()) diff --git a/tests/qttest/lazy_folios_probe.cpp b/tests/qttest/lazy_folios_probe.cpp index 24d71f743..2947e6b37 100644 --- a/tests/qttest/lazy_folios_probe.cpp +++ b/tests/qttest/lazy_folios_probe.cpp @@ -725,6 +725,61 @@ int main(int argc, char **argv) check(asked.folioPartsRead() == built, "asking a .qetz reads no file of a folio not built"); } + //A folio added, moved and removed: no other folio built, the + //folios' rows as when every folio is built (D5) + { + auto shuffle = [](QETProject &project) { + Diagram *added = project.addNewDiagram(); + project.diagramOrderChanged(1, 0); + project.removeDiagram(added); + }; + auto folioRows = [](QETProject &project) { + QStringList rows; + for (const QString &row : tables(project)) + if (row.startsWith("diagram")) rows << row; + return rows; + }; + QETProject shuffled(zipped); + const int unbuilt = shuffled.unloadedFolioCount(); + shuffle(shuffled); + std::printf(".qetz folio added, moved, removed: %d folios built\n", + unbuilt - shuffled.unloadedFolioCount()); + check(shuffled.unloadedFolioCount() == unbuilt, "adding, moving and removing a folio builds no other"); + qunsetenv("QET_LAZY_FOLIOS"); + QETProject usual_shuffled(zipped); + qputenv("QET_LAZY_FOLIOS", "1"); + shuffle(usual_shuffled); + check(folioRows(shuffled) == folioRows(usual_shuffled), "folios' rows after a move as when built"); + } + + //The tables a table can continue (its properties editor) and + //the wiring list's count of wires left out: only the folios + //holding a table built (D5) + { + QETProject listed(zipped); + const int unbuilt = listed.unloadedFolioCount(); + auto tableUuids = [](const QVector &list) { + QStringList uuids; + for (QetGraphicsTableItem *table : list) uuids << table->uuid().toString(); + uuids.sort(); + return uuids; + }; + const QStringList lazy_tables = tableUuids(ElementProvider(&listed).table(nullptr, nullptr)); + check(!lazy_tables.isEmpty(), "the project has tables"); + check(lazy_tables == tableUuids(ElementProvider(&eager).table(nullptr, nullptr)), + "the project's tables listed as when built"); + check(listed.dataBase()->excludedConductorCount() == eager.dataBase()->excludedConductorCount(), + "wires left out of the wiring list counted as when built"); + int with_tables = 0; + for (Diagram *folio : folios) { + for (QGraphicsItem *item : folio->items()) + if (item->type() == QetGraphicsTableItem::Type) { ++with_tables; break; } + } + std::printf(".qetz tables listed: %d folios built, %d hold a table\n", + unbuilt - listed.unloadedFolioCount(), with_tables); + check(listed.unloadedFolioCount() > 0, "listing the tables does not build every folio"); + } + //Saved as a .qetz without building the folios not touched //(D4): their parts and rows copied as they were QETProject untouched(zipped);