mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-14 17:44:13 +02:00
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:
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user