Show a folio without building its partners' folios (stage D5b)

Showing a folio built it and every folio holding a partner of its
symbols. Now a partner is built alone, as a symbol, into its folio's
scene (Diagram::buildSymbols()), and reused when that folio is built.

- A linked folio report built alone had no wires when it was linked,
  so its texts showing a wire's properties stayed empty: after any
  build, linked reports on the built folios and their partners look at
  their wires again, as opening does. Unlinked reports keep their texts,
  as opening leaves them.
- A report whose partner was built alone builds the partner's folio
  when its potential is followed.
- tableChainFolios() parsed every unbuilt folio's file. The index now
  lists the drawn tables and the table each continues (folio_table),
  and a .qetz opened from it uses that list. An index without it falls
  back to the folio files.
- A .qetz's project.sqlite is opened once per project
  (QetContainerDb::FolioReader), not once per folio built.

nuri (136 folios), opened fresh, one folio shown: median 1104 -> 184 ms,
each show builds one folio; industrial 234 -> 87 ms.

tst_lazyfolios: a folio whose table continues another is shown first
from a .qetz (fails when the index's chains are ignored). ctest 96/96
eager and lazy; 342-project corpus: master, lazy, .qetz lazy and .qetz
kept all the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-10-12 02:22:25 +13:00
parent 2f94724df3
commit b891b6354a
13 changed files with 371 additions and 76 deletions
+7 -4
View File
@@ -355,7 +355,8 @@ QList<QetZip::Entry> QetContainer::split(const QDomDocument &project, const QStr
engineering data back from project.sqlite.
*/
bool QetContainer::readFolio(const QHash<QString, QByteArray> &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<QString, QByteArray> &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);
}
/**
+4 -1
View File
@@ -18,6 +18,7 @@
#ifndef QETCONTAINER_H
#define QETCONTAINER_H
#include "qetcontainerdb.h"
#include "qetzip.h"
#include <QDomDocument>
@@ -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<QString, QByteArray> &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);
+41
View File
@@ -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<Private>())
{
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
+13
View File
@@ -21,6 +21,8 @@
#include <QByteArray>
#include <QDomElement>
#include <memory>
/**
@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<Private> d;
};
/// true if @p database names its folios by their uuid (format 3)
bool foliosByUuid(const QByteArray &database);
}
+32 -5
View File
@@ -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<std::pair<QUuid, QString>> &folio_headers)
QByteArray projectDataBase::indexFile(const QList<std::pair<QUuid, QString>> &folio_headers,
const QList<FolioTable> &tables)
{
if (m_content_changed) {
updateDB();
@@ -952,7 +955,9 @@ QByteArray projectDataBase::indexFile(const QList<std::pair<QUuid, QString>> &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<QString, QString>{QStringLiteral("schema"), stamp},
@@ -970,6 +975,14 @@ QByteArray projectDataBase::indexFile(const QList<std::pair<QUuid, QString>> &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<std::pair<QUuid, QString>> *folio_headers,
QString *why)
QString *why,
std::optional<QList<FolioTable>> *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<FolioTable> 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
+8 -2
View File
@@ -29,6 +29,8 @@
#include <QSet>
#include <QUuid>
#include <optional>
#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<std::pair<QUuid, QString>> &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<std::pair<QUuid, QString>> &folio_headers,
const QList<FolioTable> &tables = QList<FolioTable>());
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<std::pair<QUuid, QString>> *folio_headers,
QString *why = nullptr);
QString *why = nullptr,
std::optional<QList<FolioTable>> *folio_tables = nullptr);
QString prefillReport() const;
QSet<QUuid> foliosWithUnjoinedWires() const;
QSet<QUuid> folioUuidsOfElements(const QSet<QUuid> &elements) const;
+123 -53
View File
@@ -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<Element *> Diagram::buildSymbols(const QSet<QUuid> &uuids)
{
QList<Element *> 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<QString,
}
}
/**
@brief Diagram::buildElement
Make the symbol @p element_xml describes and add it to this folio, as
fromXml() does for each symbol it reads.
@return the symbol, or nullptr if it could not be made (said in the log)
*/
Element *Diagram::buildElement(QDomElement element_xml, QHash<int, Terminal *> &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<Element *> added_elements;
QHash<int, Terminal *> 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
+9
View File
@@ -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<QUuid, Element *> m_partial_elements;
QHash<int, Terminal *> m_partial_table;
bool m_partial_changed = false;
Element *buildElement(QDomElement element_xml, QHash<int, Terminal *> &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<Element *> buildSymbols(const QSet<QUuid> &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())
+5 -4
View File
@@ -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<QUuid> uuids;
for (const auto &linkInfo : tmp_uuids_link) {
uuids.append(linkInfo.uuid);
+8
View File
@@ -1102,6 +1102,14 @@ QList<Terminal *> relatedPotentialTerminal (
project->buildFoliosHoldingSymbols(waiting);
}
QList <Element *> 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());
+97 -7
View File
@@ -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<Diagram *> &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<Diagram *> 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<Diagram *> &folios, const QSet<QUuid> &partners)
{
const ProjectState state = m_state;
m_state = ProjectParsingRunning;
QList<Element *> built;
for (Diagram *d : folios) {
const QList<Element *> 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<int, QByteArray> *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<projectDataBase::FolioTable> 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<QetGraphicsTableItem *>(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<QetContainerDb::FolioReader>(
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();
}
+7
View File
@@ -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 <QFuture>
#include <array>
#include <memory>
#include <optional>
class Diagram;
@@ -130,6 +132,7 @@ class QETProject : public QObject
QList<Diagram *> cableLabelFolios();
QList<Diagram *> foliosWithWiresNotReconnected();
void buildFolioToShow(Diagram *folio);
void buildPartnerSymbols(const QList<Diagram *> &folios, const QSet<QUuid> &partners);
void buildFoliosHolding(ElementData::Types kinds, bool free_only);
void buildFoliosHoldingSymbols(const QSet<QUuid> &symbols);
QSet<QUuid> symbolsOnUnbuiltFolios() const;
@@ -545,8 +548,12 @@ class QETProject : public QObject
bool m_kept_parts_trusted = false;
QSet<QUuid> m_waiting_link_targets;
QHash<QString, QByteArray> m_container_files;
///Reads the folio parts' engineering data, opened once
std::unique_ptr<QetContainerDb::FolioReader> m_folio_reader;
QStringList m_folio_files;
QList<std::pair<QUuid, QString>> m_folio_headers;
///The drawn tables of the index opened from, if it has them
std::optional<QList<projectDataBase::FolioTable>> m_index_tables;
void readFoliosFromIndex();
bool m_kept_folios_stale = false;
/// Something was edited since the project was opened: a table
+17
View File
@@ -34,6 +34,7 @@
#include "../../sources/diagramevent/diagrameventaddpaste.h"
#include <QKeyEvent>
#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<QetGraphicsTableItem *>(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