diff --git a/sources/container/qetcontainer.cpp b/sources/container/qetcontainer.cpp index 1bfa5f8e6..df69c9e54 100644 --- a/sources/container/qetcontainer.cpp +++ b/sources/container/qetcontainer.cpp @@ -355,7 +355,8 @@ QList QetContainer::split(const QDomDocument &project, const QStr engineering data back from project.sqlite. */ bool QetContainer::readFolio(const QHash &files, const QString &file, - QDomDocument *folio, QString *error) + QDomDocument *folio, QString *error, + QetContainerDb::FolioReader *reader) { if (!files.contains(file)) { setError(error, QStringLiteral("part \"%1\" is missing").arg(file)); @@ -380,9 +381,11 @@ bool QetContainer::readFolio(const QHash &files, const QStr image.insertBefore(folio->createTextNode(QString::fromLatin1(files.value(picture).toBase64())), image.firstChild()); } - return !files.contains(QStringLiteral("project.sqlite")) - || QetContainerDb::restoreFolio(folio->documentElement(), - files.value(QStringLiteral("project.sqlite")), error); + if (!files.contains(QStringLiteral("project.sqlite"))) + return true; + return reader ? reader->restore(folio->documentElement(), error) + : QetContainerDb::restoreFolio(folio->documentElement(), + files.value(QStringLiteral("project.sqlite")), error); } /** diff --git a/sources/container/qetcontainer.h b/sources/container/qetcontainer.h index 16da23a29..814ca2595 100644 --- a/sources/container/qetcontainer.h +++ b/sources/container/qetcontainer.h @@ -18,6 +18,7 @@ #ifndef QETCONTAINER_H #define QETCONTAINER_H +#include "qetcontainerdb.h" #include "qetzip.h" #include @@ -59,8 +60,10 @@ namespace QetContainer QString *error = nullptr, QStringList *folio_files = nullptr); /// A folio part of @p files, which join() left out, with its /// pictures and its engineering data back (stage D2) + /// @p reader, if any, reads the engineering data of @p files bool readFolio(const QHash &files, const QString &file, - QDomDocument *folio, QString *error = nullptr); + QDomDocument *folio, QString *error = nullptr, + QetContainerDb::FolioReader *reader = nullptr); /// Take what QElectroTech computes out of a folio (format 3) void dropComputed(QDomElement diagram, bool terminal_lists); diff --git a/sources/container/qetcontainerdb.cpp b/sources/container/qetcontainerdb.cpp index 7ec4c6e0a..541b1d00f 100644 --- a/sources/container/qetcontainerdb.cpp +++ b/sources/container/qetcontainerdb.cpp @@ -929,6 +929,47 @@ bool QetContainerDb::restoreFolio(QDomElement diagram, const QByteArray &databas return openDatabase(db, database, error) && restoreDiagram(db, diagram, error); } +struct QetContainerDb::FolioReader::Private +{ + QByteArray database; + Database db; + bool opened = false; + QString open_error; +}; + +/** + @brief QetContainerDb::FolioReader::FolioReader + Reads folios' engineering data from @p database, opened the first time + one is read and kept open: a folio built after the project was opened + from a .qetz no longer copies the whole database (stage D5) +*/ +QetContainerDb::FolioReader::FolioReader(const QByteArray &database) : + d(std::make_unique()) +{ + d->database = database; +} + +QetContainerDb::FolioReader::~FolioReader() = default; + +/** + @brief QetContainerDb::FolioReader::restore + restoreFolio() for @p diagram +*/ +bool QetContainerDb::FolioReader::restore(QDomElement diagram, QString *error) +{ + if (!d->opened) { + d->opened = true; + if (!openDatabase(d->db, d->database, &d->open_error) && d->open_error.isEmpty()) + d->open_error = QStringLiteral("the project database cannot be read"); + d->database = QByteArray(); + } + if (!d->open_error.isEmpty()) { + setError(error, d->open_error); + return false; + } + return restoreDiagram(d->db, diagram, error); +} + /** @brief QetContainerDb::foliosByUuid @return true if @p database, a format 3 project.sqlite, names its diff --git a/sources/container/qetcontainerdb.h b/sources/container/qetcontainerdb.h index 8e49eca63..1b616acf2 100644 --- a/sources/container/qetcontainerdb.h +++ b/sources/container/qetcontainerdb.h @@ -21,6 +21,8 @@ #include #include +#include + /** @brief The QetContainerDb namespace The engineering data of a project, moved between its document and the @@ -42,6 +44,17 @@ namespace QetContainerDb const QByteArray &kept_from = QByteArray()); bool restore(QDomElement project, const QByteArray &database, QString *error = nullptr); bool restoreFolio(QDomElement diagram, const QByteArray &database, QString *error = nullptr); + /// restoreFolio() for the folios of one database, opened once + class FolioReader + { + public: + explicit FolioReader(const QByteArray &database); + ~FolioReader(); + bool restore(QDomElement diagram, QString *error = nullptr); + private: + struct Private; + std::unique_ptr d; + }; /// true if @p database names its folios by their uuid (format 3) bool foliosByUuid(const QByteArray &database); } diff --git a/sources/dataBase/projectdatabase.cpp b/sources/dataBase/projectdatabase.cpp index 3ebe41382..f43af8179 100644 --- a/sources/dataBase/projectdatabase.cpp +++ b/sources/dataBase/projectdatabase.cpp @@ -907,11 +907,14 @@ QStringList indexTables(const QSqlDatabase &db) This database written to an SQLite file, for a .qetz to be opened from (stage D2): every table but the drawing items, brought up to date first, with the folios' own data @p folio_headers (in folio - order, each as Diagram::headerXml() writes it) and the stamp of the - schema it was written with. + order, each as Diagram::headerXml() writes it), the drawn tables + @p tables and how they continue one another (which folios a folio's + tables must be built with), and the stamp of the schema it was written + with. @return the file, or nothing if it could not be written */ -QByteArray projectDataBase::indexFile(const QList> &folio_headers) +QByteArray projectDataBase::indexFile(const QList> &folio_headers, + const QList &tables) { if (m_content_changed) { updateDB(); @@ -952,7 +955,9 @@ QByteArray projectDataBase::indexFile(const QList> &fo && query.exec(QStringLiteral("DROP TABLE image")) && query.exec(QStringLiteral("CREATE TABLE index_meta (key TEXT PRIMARY KEY, value TEXT)")) && query.exec(QStringLiteral("CREATE TABLE folio_header (pos INTEGER PRIMARY KEY, " - "uuid TEXT NOT NULL, xml TEXT NOT NULL)")); + "uuid TEXT NOT NULL, xml TEXT NOT NULL)")) + && query.exec(QStringLiteral("CREATE TABLE folio_table (uuid TEXT NOT NULL, " + "folio_uuid TEXT NOT NULL, previous_uuid TEXT)")); file.transaction(); query.prepare(QStringLiteral("INSERT INTO index_meta (key, value) VALUES (?, ?)")); for (const auto &meta : {std::pair{QStringLiteral("schema"), stamp}, @@ -970,6 +975,14 @@ QByteArray projectDataBase::indexFile(const QList> &fo query.addBindValue(header.second); ok = ok && query.exec(); } + query.prepare(QStringLiteral("INSERT INTO folio_table (uuid, folio_uuid, previous_uuid) " + "VALUES (?, ?, ?)")); + for (const FolioTable &table : tables) { + query.addBindValue(table.table.toString()); + query.addBindValue(table.folio.toString()); + query.addBindValue(table.previous.isNull() ? QVariant() : QVariant(table.previous.toString())); + ok = ok && query.exec(); + } ok = file.commit() && ok; ok = query.exec(QStringLiteral("VACUUM")) && ok; file.close(); @@ -1015,12 +1028,15 @@ void projectDataBase::discardFill() when written with this database's schema, its tables plain tables, and nothing in its schema run. @param folio_headers : the folios' own data, in folio order + @param folio_tables : if not null, the drawn tables indexFile() wrote, or + nothing if the index predates them @return false, with @p why set and nothing changed in the tables, if the file cannot be read */ bool projectDataBase::fillFromIndex(const QByteArray &index, QList> *folio_headers, - QString *why) + QString *why, + std::optional> *folio_tables) { auto refuse = [why](const QString &reason) { if (why) *why = reason; @@ -1093,6 +1109,17 @@ bool projectDataBase::fillFromIndex(const QByteArray &index, query.exec(QStringLiteral("SELECT uuid, xml FROM idx.folio_header ORDER BY pos")); while (query.next()) folio_headers->append({QUuid(query.value(0).toString()), query.value(1).toString()}); + if (folio_tables) { + folio_tables->reset(); + if (isTable(QStringLiteral("folio_table"))) { + QList read; + query.exec(QStringLiteral("SELECT uuid, folio_uuid, previous_uuid FROM idx.folio_table")); + while (query.next()) + read.append({QUuid(query.value(0).toString()), QUuid(query.value(1).toString()), + QUuid(query.value(2).toString())}); + *folio_tables = read; + } + } detach(); //The stores, from their tables, as the document would fill them diff --git a/sources/dataBase/projectdatabase.h b/sources/dataBase/projectdatabase.h index 15119a61d..ec0ac2e88 100644 --- a/sources/dataBase/projectdatabase.h +++ b/sources/dataBase/projectdatabase.h @@ -29,6 +29,8 @@ #include #include +#include + #include "../diagramcontext.h" #include "../titleblockproperties.h" #include "../conductorproperties.h" @@ -164,12 +166,16 @@ class projectDataBase : public QObject bool isBuildingFolio() const {return m_building_folios > 0;} bool rowsFromDocument() const {return m_rows_from_document;} QString schemaStamp() const; - QByteArray indexFile(const QList> &folio_headers); + /// A drawn table of a folio and the table it continues + struct FolioTable { QUuid table; QUuid folio; QUuid previous; }; + QByteArray indexFile(const QList> &folio_headers, + const QList &tables = QList()); void discardFill(); /// The tables are right as they are (filled from an index) void markCurrent() {m_content_changed = false;} bool fillFromIndex(const QByteArray &index, QList> *folio_headers, - QString *why = nullptr); + QString *why = nullptr, + std::optional> *folio_tables = nullptr); QString prefillReport() const; QSet foliosWithUnjoinedWires() const; QSet folioUuidsOfElements(const QSet &elements) const; diff --git a/sources/diagram.cpp b/sources/diagram.cpp index fe7d34346..7dcbf2427 100644 --- a/sources/diagram.cpp +++ b/sources/diagram.cpp @@ -788,6 +788,48 @@ void Diagram::ensureLoaded() } } +/** + @brief Diagram::buildSymbols + Build, of this folio not built yet (QET_LAZY_FOLIOS), the symbols + @p uuids only -- partners a shown folio draws from -- and keep them for + when the folio is built (stage D5b). They belong to the + folio's scene; the folio stays not built. + @return the symbols built now +*/ +QList Diagram::buildSymbols(const QSet &uuids) +{ + QList built; + if (isLoaded() || uuids.isEmpty()) { + return built; + } + const QDomElement xml = deferredXml(); + if (xml.isNull()) { + return built; + } + projectDataBase *db = m_project ? m_project->dataBase() : nullptr; + if (db) db->setBuildingFolio(true); + for (QDomElement element_xml : + QET::findInDomElement(xml, QStringLiteral("elements"), QStringLiteral("element"))) + { + const QUuid uuid(element_xml.attribute(QStringLiteral("uuid"))); + if (uuid.isNull() || !uuids.contains(uuid) || m_partial_elements.contains(uuid) + || !Element::valideXml(element_xml)) { + continue; + } + if (Element *element = buildElement(element_xml, m_partial_table, true)) { + m_partial_elements.insert(uuid, element); + //A partner unlinked or linked again: the folio is not + //saved as it was read any more + connect(element, &Element::linkedElementChanged, this, [this]() { + if (!isLoaded()) m_partial_changed = true; + }); + built << element; + } + } + if (db) db->setBuildingFolio(false); + return built; +} + /** @brief Diagram::deferredXml @return the XML of this folio's items while they are not built: kept @@ -826,6 +868,9 @@ bool Diagram::buildDeferred() m_loading_deferred = true; fromXml(xml, QPointF(), true, nullptr); m_loading_deferred = false; + m_partial_elements.clear(); + m_partial_table.clear(); + m_partial_changed = false; if (db) db->setBuildingFolio(false); return true; } @@ -1504,6 +1549,73 @@ void Diagram::folioSequentialsToXml(QHash &table_adr_id, + bool consider_informations) +{ + // cree un element dont le type correspond a l'id type + QString type_id = element_xml.attribute(QStringLiteral("type")); + ElementsLocation element_location; + if (type_id.startsWith(QStringLiteral("embed://"))) { + element_location = ElementsLocation(type_id, m_project); + } + else { + element_location = ElementsLocation(type_id); + } + + int state = 0; + Element *nvel_elmt = + ElementFactory::Instance() -> createElement( + element_location, nullptr, &state); + if (state) + { + QString debug_message = + QString("Diagram::fromXml() : Le chargement de " + "la description de l'element %1 a " + "echoue avec le code d'erreur %2").arg( + element_location.path()).arg(state); + qDebug() << qPrintable(debug_message); + delete nvel_elmt; + return nullptr; + } + + addItem(nvel_elmt); + //Loading fail, remove item from the diagram + if (!nvel_elmt->fromXml(element_xml, table_adr_id)) + { + removeItem(nvel_elmt); + delete nvel_elmt; + qDebug() << QStringLiteral("Diagram::fromXml() : Le chargement des parametres d'un element a echoue"); + return nullptr; + } 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->derivedItemUuid( + 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)); + return nvel_elmt; + } +return nullptr; +} + /** @brief Diagram::fromXml Imports the described schema into an XML document. @@ -1925,65 +2037,23 @@ bool Diagram::fromXml(QDomElement &document, //Load all elements from the XML QList added_elements; QHash table_adr_id; + if (m_loading_deferred) { + table_adr_id = m_partial_table; //the terminals of symbols built already + } for (auto element_xml : QET::findInDomElement(root, QStringLiteral("elements"), QStringLiteral("element"))) { if (!Element::valideXml(element_xml)) continue; - - // cree un element dont le type correspond a l'id type - QString type_id = element_xml.attribute(QStringLiteral("type")); - ElementsLocation element_location; - if (type_id.startsWith(QStringLiteral("embed://"))) { - element_location = ElementsLocation(type_id, m_project); - } - else { - element_location = ElementsLocation(type_id); - } - - int state = 0; - Element *nvel_elmt = - ElementFactory::Instance() -> createElement( - element_location, nullptr, &state); - if (state) - { - QString debug_message = - QString("Diagram::fromXml() : Le chargement de " - "la description de l'element %1 a " - "echoue avec le code d'erreur %2").arg( - element_location.path()).arg(state); - qDebug() << qPrintable(debug_message); - delete nvel_elmt; - continue; - } - - addItem(nvel_elmt); - //Loading fail, remove item from the diagram - if (!nvel_elmt->fromXml(element_xml, table_adr_id)) - { - removeItem(nvel_elmt); - 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->derivedItemUuid( - QStringLiteral("element"), - QStringList{type_id, - element_xml.attribute(QStringLiteral("x")), - element_xml.attribute(QStringLiteral("y")), - element_xml.attribute(QStringLiteral("orientation"))} - .join(QLatin1Char('\n')))); + //Built already as another folio's partner (buildSymbols()) + if (m_loading_deferred) { + Element *built = m_partial_elements.value(QUuid(element_xml.attribute(QStringLiteral("uuid")))); + if (built) { + added_elements << built; + continue; } - ItemGroups::setGroup(nvel_elmt, ItemGroups::read(element_xml)); - added_elements << nvel_elmt; } + if (Element *nvel_elmt = buildElement(element_xml, table_adr_id, consider_informations)) + added_elements << nvel_elmt; } //Texts, images and shapes written before they carried a uuid (or diff --git a/sources/diagram.h b/sources/diagram.h index a73f9938c..d79439795 100644 --- a/sources/diagram.h +++ b/sources/diagram.h @@ -143,6 +143,11 @@ class Diagram : public QGraphicsScene /// The XML of this folio's items, while they are not built yet QDomElement m_deferred; QString m_deferred_part; + QHash m_partial_elements; + QHash m_partial_table; + bool m_partial_changed = false; + Element *buildElement(QDomElement element_xml, QHash &table_adr_id, + bool consider_informations); QString m_opened_header; QByteArray m_kept_text; bool m_loading_deferred = false; @@ -220,6 +225,10 @@ class Diagram : public QGraphicsScene /// (stage D2) void setDeferredPart(const QString &file) {m_deferred = QDomElement(); m_deferred_part = file;} QString deferredPart() const {return m_deferred_part;} + QList buildSymbols(const QSet &uuids); + /// Symbols built as partners (buildSymbols()) changed since: + /// the folio must be built to be saved + bool partialChanged() const {return m_partial_changed;} QString headerXml(); /// The folio's text in the file it was read from, while it is /// not built (QETProject::toXml()) diff --git a/sources/qetgraphicsitem/element.cpp b/sources/qetgraphicsitem/element.cpp index 43511ae37..ae497bafb 100644 --- a/sources/qetgraphicsitem/element.cpp +++ b/sources/qetgraphicsitem/element.cpp @@ -1654,10 +1654,11 @@ void Element::initLink(QETProject *prj) // if nothing to link return now if (tmp_uuids_link.isEmpty()) return; - //The built folios only: a partner on a folio not built yet - //(QET_LAZY_FOLIOS) is linked when its folio is, and the saved list - //is kept until then, for the order it gives - ElementProvider ep(prj->builtFolios()); + //The symbols built: on the folios built, and those built alone as + //partners on folios not built (Diagram::buildSymbols()). A partner + //not built yet (QET_LAZY_FOLIOS) is linked when it is, and the + //saved list is kept until then, for the order it gives + ElementProvider ep(prj->folios()); QList uuids; for (const auto &linkInfo : tmp_uuids_link) { uuids.append(linkInfo.uuid); diff --git a/sources/qetgraphicsitem/terminal.cpp b/sources/qetgraphicsitem/terminal.cpp index 0e3684cea..5cade6949 100644 --- a/sources/qetgraphicsitem/terminal.cpp +++ b/sources/qetgraphicsitem/terminal.cpp @@ -1102,6 +1102,14 @@ QList relatedPotentialTerminal ( project->buildFoliosHoldingSymbols(waiting); } QList elmt_list = terminal -> parentElement() -> linkedElements(); + //A partner built alone (stage D5b) has no wires + //yet: its folio is built + if (!elmt_list.isEmpty() && project && elmt_list.first()->diagram() + && !elmt_list.first()->diagram()->isLoaded() + && project->state() != QETProject::ProjectParsingRunning) { + project->buildFolios({elmt_list.first()->diagram()}); + elmt_list = terminal->parentElement()->linkedElements(); + } if (!elmt_list.isEmpty()) { return (elmt_list.first()->terminals()); diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp index bfc52f86a..9937d7099 100644 --- a/sources/qetproject.cpp +++ b/sources/qetproject.cpp @@ -32,6 +32,7 @@ #include "diagram.h" #include "qetgraphicsitem/element.h" #include "qetgraphicsitem/conductor.h" +#include "qetgraphicsitem/dynamicelementtextitem.h" #include "qetgraphicsitem/ViewItem/qetgraphicstableitem.h" #include "qetgraphicsitem/ViewItem/projectdbmodel.h" #include "qetapp.h" @@ -487,7 +488,7 @@ QETProject::ProjectState QETProject::openFile(QFile *file) if (!index.isEmpty() && index.contains("folio_header") && QetContainer::join(entries, &document, &why, &folio_files)) { - if (m_data_base.fillFromIndex(index, &m_folio_headers, &why) + if (m_data_base.fillFromIndex(index, &m_folio_headers, &why, &m_index_tables) && m_folio_headers.size() == folio_files.size()) { xml_project = document; m_container_files = files; @@ -496,6 +497,7 @@ QETProject::ProjectState QETProject::openFile(QFile *file) } else { m_data_base.discardFill(); m_folio_headers.clear(); + m_index_tables.reset(); qInfo().noquote() << "Not opened from the index of" << fi.fileName() << ":" << why; } } @@ -728,6 +730,21 @@ void QETProject::buildFolios(const QList &folios) const for (Element *elmt : d->elements()) if (elmt->keepsSavedLinks()) elmt->initLink(self); + //A linked folio report's texts show a wire's properties: the + //report's own wire, else its partner's. A report built alone + //first, as a partner (buildPartnerSymbols()), linked before it had + //wires, and a report elsewhere may watch that partner: both look + //again now, as opening has them do once every wire exists (an + //unlinked report keeps its texts, as opening leaves them) + for (Diagram *d : std::as_const(built)) + for (Element *elmt : d->elements()) + if ((elmt->linkType() & Element::AllReport) && !elmt->linkedElements().isEmpty()) + { + emit elmt->linkedElementChanged(); + for (Element *partner : elmt->linkedElements()) + if (!built.contains(partner->diagram())) + emit partner->linkedElementChanged(); + } //Likewise a table whose previous table was on a folio not built for (Diagram *d : builtFolios()) for (QGraphicsItem *item : d->items()) @@ -913,6 +930,16 @@ QSet QETProject::tableChainFolios(Diagram *folio) const } } continue; + } + //Opened from an index: its tables, not the folio's part + if (m_index_tables) { + for (const projectDataBase::FolioTable &table : std::as_const(*m_index_tables)) { + if (table.folio == d->uuid()) { + folio_of.insert(table.table, d); + previous_of.insert(table.table, table.previous); + } + } + continue; } const QDomNodeList tables = d->deferredXml().elementsByTagName( QetGraphicsTableItem::xmlTagName()); @@ -983,11 +1010,43 @@ void QETProject::buildFolioToShow(Diagram *folio) return; } if (qEnvironmentVariableIntValue("QET_LAZY_FOLIOS_TRACE") == 1) { - qWarning().noquote() << "lazy folios:" << to_build.size() - << "folios built for the partners of folio" - << folio->folioIndex() + 1; + qWarning().noquote() << "lazy folios: the partners of folio" << folio->folioIndex() + 1 + << "built alone, on" << to_build.size() << "folios"; } - buildFolios(to_build); + //The partners alone, not their folios (stage D5b) + buildPartnerSymbols(to_build, partners); +} + +/** + @brief QETProject::buildPartnerSymbols + Build on the folios @p folios, not built, the symbols @p partners alone + (Diagram::buildSymbols()), as opening builds them: linked to the symbols + built already, their texts refreshed, in the state opening has. +*/ +void QETProject::buildPartnerSymbols(const QList &folios, const QSet &partners) +{ + const ProjectState state = m_state; + m_state = ProjectParsingRunning; + QList built; + for (Diagram *d : folios) { + const QList symbols = d->buildSymbols(partners); + if (!symbols.isEmpty()) { + built += symbols; + linkElementsToElementAutoNums(d); + } + } + for (Element *element : std::as_const(built)) { + element->initLink(this); + for (auto &text : element->dynamicTextItems()) + text->refreshLabelConnection(); + } + //A built symbol linked to one of them puts its partners back in + //their saved order + for (Diagram *d : builtFolios()) + for (Element *element : d->elements()) + if (element->keepsSavedLinks()) + element->initLink(this); + m_state = state; } /** @@ -2316,6 +2375,11 @@ QDomDocument QETProject::toXml(QHash *kept_folios, QStringList const bool keep = unloadedFolioCount() && !m_kept_folios_stale && ((kept_folios && m_kept_folios_trusted) || (kept_parts && m_kept_parts_trusted)); if (keep) { + //Partners built alone and linked or unlinked since: their folio + //is not what was read + for (Diagram *diagram : std::as_const(m_diagrams_list)) + if (!diagram->isLoaded() && diagram->partialChanged()) + buildFolios({diagram}); for (Diagram *diagram : std::as_const(m_diagrams_list)) if (diagram->headerChanged()) buildFolioToShow(diagram); @@ -2588,10 +2652,27 @@ QByteArray QETProject::containerIndex() seen.insert(uuid); return true; }; + //The drawn tables and the tables they continue: which folios a + //folio's tables are built with (tableChainFolios()) + QList tables; for (Diagram *diagram : std::as_const(m_diagrams_list)) { if (!unique(diagram->uuid())) return QByteArray(); if (!diagram->isLoaded()) { headers.append({diagram->uuid(), diagram->headerXml()}); + if (m_index_tables) { + for (const projectDataBase::FolioTable &table : std::as_const(*m_index_tables)) + if (table.folio == diagram->uuid()) + tables.append(table); + continue; + } + const QDomNodeList items = diagram->deferredXml().elementsByTagName( + QetGraphicsTableItem::xmlTagName()); + for (int i = 0 ; i < items.size() ; ++i) { + const QDomElement table = items.at(i).toElement(); + tables.append({QUuid(table.attribute(QStringLiteral("uuid"))), diagram->uuid(), + QUuid(table.firstChildElement(QStringLiteral("previous_table")) + .attribute(QStringLiteral("uuid")))}); + } continue; } for (Element *element : diagram->elements()) @@ -2599,8 +2680,14 @@ QByteArray QETProject::containerIndex() for (Conductor *conductor : diagram->conductors()) if (!unique(conductor->uuid())) return QByteArray(); headers.append({diagram->uuid(), diagram->headerXml()}); + for (QGraphicsItem *item : diagram->items()) { + if (item->type() == QetGraphicsTableItem::Type) { + auto table = static_cast(item); + tables.append({table->uuid(), diagram->uuid(), table->previousTableUuid()}); + } + } } - return m_data_base.indexFile(headers); + return m_data_base.indexFile(headers, tables); } /** @@ -3323,7 +3410,10 @@ QDomElement QETProject::readFolioPart(const QString &file) { QDomDocument folio; QString error; - if (!QetContainer::readFolio(m_container_files, file, &folio, &error)) { + if (!m_folio_reader) + m_folio_reader = std::make_unique( + m_container_files.value(QStringLiteral("project.sqlite"))); + if (!QetContainer::readFolio(m_container_files, file, &folio, &error, m_folio_reader.get())) { qWarning().noquote() << "Unable to read the folio" << file << ":" << error; return QDomElement(); } diff --git a/sources/qetproject.h b/sources/qetproject.h index 3a51c0d1f..aad50ad7e 100644 --- a/sources/qetproject.h +++ b/sources/qetproject.h @@ -32,6 +32,7 @@ #include "wiringrules.h" #include "diagram.h" #include "autoNum/assignvariables.h" +#include "container/qetcontainerdb.h" #ifdef BUILD_WITHOUT_KF # include "ui/nokde/kautosavefile.h" #else @@ -45,6 +46,7 @@ #include #include +#include #include class Diagram; @@ -130,6 +132,7 @@ class QETProject : public QObject QList cableLabelFolios(); QList foliosWithWiresNotReconnected(); void buildFolioToShow(Diagram *folio); + void buildPartnerSymbols(const QList &folios, const QSet &partners); void buildFoliosHolding(ElementData::Types kinds, bool free_only); void buildFoliosHoldingSymbols(const QSet &symbols); QSet symbolsOnUnbuiltFolios() const; @@ -545,8 +548,12 @@ class QETProject : public QObject bool m_kept_parts_trusted = false; QSet m_waiting_link_targets; QHash m_container_files; + ///Reads the folio parts' engineering data, opened once + std::unique_ptr m_folio_reader; QStringList m_folio_files; QList> m_folio_headers; + ///The drawn tables of the index opened from, if it has them + std::optional> m_index_tables; void readFoliosFromIndex(); bool m_kept_folios_stale = false; /// Something was edited since the project was opened: a table diff --git a/tests/qttest/lazy_folios_probe.cpp b/tests/qttest/lazy_folios_probe.cpp index 1704a0a9d..e0bcf3d4c 100644 --- a/tests/qttest/lazy_folios_probe.cpp +++ b/tests/qttest/lazy_folios_probe.cpp @@ -34,6 +34,7 @@ #include "../../sources/diagramevent/diagrameventaddpaste.h" #include #include "../../sources/qetgraphicsitem/conductor.h" +#include "../../sources/qetgraphicsitem/ViewItem/qetgraphicstableitem.h" #include "../../sources/qetgraphicsitem/terminal.h" #include "../../sources/undocommand/changeelementinformationcommand.h" #include "../../sources/undocommand/changetitleblockcommand.h" @@ -678,6 +679,22 @@ int main(int argc, char **argv) for (int i = 0; i < folios.size(); ++i) same(indexed, i, "shown from the .qetz"); check(indexed.toXml().toString() == eager.toXml().toString(), "the .qetz saves as when built"); + //A folio whose table continues one on another folio, shown + //first: the index, not the folios' parts, says which folios + //its table is built with (D5) + int continued = 0; + for (int i = 0; i < folios.size(); ++i) { + bool continues = false; + for (QGraphicsItem *item : folios.at(i)->items()) + if (item->type() == QetGraphicsTableItem::Type + && !static_cast(item)->previousTableUuid().isNull()) + continues = true; + if (!continues) continue; + QETProject fresh(zipped); + same(fresh, i, "a continued table shown first from the .qetz"); + ++continued; + } + check(continued > 0, "a folio whose table continues another"); //Saved as a .qetz without building the folios not touched //(D4): their parts and rows copied as they were