mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-14 17:44:13 +02:00
Save a new title, author or revision without building the folio (stage D5)
A folio not built whose own data changed was built to save, with the folios its links reach. When what changed is only title block fields nothing on a folio is computed from -- its title, author, date, revision index, version, file name, template; a formula reads the folio's label, plant, location, extra fields and grid -- its items as read are what building it would write: it is saved from them with its own data as it is now (Diagram::deferredXmlWithHeader()), .qet and .qetz alike, with the information of its symbols changed through the project database (or built, when one of those needs it). Any other change still builds it. 136-folio .qetz, revision index and author set on every folio by a script: 0.8 s, saved in 6.8 s, no folio built; with every folio built, the same file. tst_lazyfolios: a new revision on every folio of a .qet and of a .qetz saved as when built, building only the folios holding a table (fails, 47 built, when every change counts); a folio whose label changed is built (fails when none counts). ctest 96/96 eager and lazy; 342 corpus projects save the same in all four arms and export the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -830,6 +830,57 @@ QList<Element *> Diagram::buildSymbols(const QSet<QUuid> &uuids)
|
||||
return built;
|
||||
}
|
||||
|
||||
namespace {
|
||||
//The title block fields nothing on a folio is computed from: a formula
|
||||
//reads the folio's label, plant, location, extra fields and grid
|
||||
//(autonum::FormulaContext), none of these
|
||||
const QStringList ShownOnlyFields = {
|
||||
QStringLiteral("title"), QStringLiteral("author"), QStringLiteral("date"),
|
||||
QStringLiteral("indexrev"), QStringLiteral("version"), QStringLiteral("filename"),
|
||||
QStringLiteral("titleblocktemplate"), QStringLiteral("titleblocktemplateCollection")};
|
||||
|
||||
QString headerWithoutShownOnly(const QString &header)
|
||||
{
|
||||
QDomDocument document;
|
||||
document.setContent(header);
|
||||
QDomElement root = document.documentElement();
|
||||
for (const QString &field : ShownOnlyFields) root.removeAttribute(field);
|
||||
return document.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::headerChangeShowsOnly
|
||||
@return true if what changed in this folio's own data since it was read
|
||||
(headerChanged()) is only title block fields nothing on the folio is
|
||||
computed from (its title, author, date, revision, file name, template):
|
||||
its items, as read, are what building it would write
|
||||
*/
|
||||
bool Diagram::headerChangeShowsOnly()
|
||||
{
|
||||
return headerWithoutShownOnly(headerXml()) == headerWithoutShownOnly(m_opened_header);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::deferredXmlWithHeader
|
||||
@return this folio, not built, as it saves: its own data as it is now
|
||||
(writeHeader()) and its items as read (deferredXml()), in @p document --
|
||||
when its items are what building it would write (headerChangeShowsOnly())
|
||||
*/
|
||||
QDomElement Diagram::deferredXmlWithHeader(QDomDocument &document)
|
||||
{
|
||||
QDomElement folio = document.createElement(QStringLiteral("diagram"));
|
||||
writeHeader(document, folio);
|
||||
QSet<QString> header_children;
|
||||
for (QDomElement child = folio.firstChildElement() ; !child.isNull() ; child = child.nextSiblingElement())
|
||||
header_children.insert(child.tagName());
|
||||
const QDomElement items = deferredXml();
|
||||
for (QDomElement child = items.firstChildElement() ; !child.isNull() ; child = child.nextSiblingElement())
|
||||
if (!header_children.contains(child.tagName()))
|
||||
folio.appendChild(document.importNode(child, true));
|
||||
return folio;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::deferredXml
|
||||
@return the XML of this folio's items while they are not built: kept
|
||||
|
||||
@@ -240,6 +240,8 @@ class Diagram : public QGraphicsScene
|
||||
/// headerChanged() (QETProject::toXml())
|
||||
void rememberHeader() {m_opened_header = headerXml();}
|
||||
bool headerChanged() {return headerXml() != m_opened_header;}
|
||||
bool headerChangeShowsOnly();
|
||||
QDomElement deferredXmlWithHeader(QDomDocument &document);
|
||||
void ensureLoaded();
|
||||
bool buildDeferred();
|
||||
//The last argument is only for a copy fragment: when it is
|
||||
|
||||
+28
-2
@@ -2374,9 +2374,17 @@ QDomDocument QETProject::toXml(QHash<int, QByteArray> *kept_folios, QStringList
|
||||
changed << diagram;
|
||||
}
|
||||
buildFolios(changed);
|
||||
for (Diagram *diagram : std::as_const(m_diagrams_list))
|
||||
if (diagram->headerChanged())
|
||||
//-- unless it changed only in what nothing is computed from (its
|
||||
//title, date, revision...): written from its items as read
|
||||
//(Diagram::deferredXmlWithHeader())
|
||||
for (Diagram *diagram : std::as_const(m_diagrams_list)) {
|
||||
if (!diagram->headerChanged()) continue;
|
||||
bool edits_need_build = false;
|
||||
if (!diagram->isLoaded())
|
||||
m_data_base.unbuiltEditsOnFolio(diagram->uuid(), &edits_need_build);
|
||||
if (diagram->isLoaded() || !diagram->headerChangeShowsOnly() || edits_need_build)
|
||||
buildFolioToShow(diagram);
|
||||
}
|
||||
//A table filled from the database saves the size its rows
|
||||
//give it, which any edit can change
|
||||
if (m_edited_since_open)
|
||||
@@ -2490,6 +2498,24 @@ QDomDocument QETProject::toXml(QHash<int, QByteArray> *kept_folios, QStringList
|
||||
<< "["
|
||||
<< diagram
|
||||
<< "]";
|
||||
if (keep && !diagram->isLoaded() && diagram->headerChanged()) {
|
||||
QDomElement xml_diagram = diagram->deferredXmlWithHeader(xml_doc);
|
||||
//Its symbols whose information changed while it was not built
|
||||
const QHash<QUuid, DiagramContext> edits = m_data_base.unbuiltEditsOnFolio(diagram->uuid());
|
||||
for (const QDomElement &list : QET::findInDomElement(xml_diagram, QStringLiteral("elements")))
|
||||
for (QDomElement element = list.firstChildElement(QStringLiteral("element")) ; !element.isNull() ;
|
||||
element = element.nextSiblingElement(QStringLiteral("element"))) {
|
||||
const auto edit = edits.constFind(QUuid(element.attribute(QStringLiteral("uuid"))));
|
||||
if (edit == edits.constEnd()) continue;
|
||||
QDomElement information = xml_doc.createElement(QStringLiteral("elementInformations"));
|
||||
edit->toXml(information, QStringLiteral("elementInformation"));
|
||||
const QDomElement old_information = element.firstChildElement(QStringLiteral("elementInformations"));
|
||||
element.replaceChild(information, old_information); //it has one (unbuiltEditsOnFolio())
|
||||
}
|
||||
xml_diagram.setAttribute("order", order_num ++);
|
||||
project_root.appendChild(xml_diagram);
|
||||
continue;
|
||||
}
|
||||
if (keep && !diagram->isLoaded() && kept_parts) {
|
||||
auto placeholder = xml_doc.createElement(QStringLiteral("diagram"));
|
||||
placeholder.setAttribute(QStringLiteral("qetz-kept-part"), diagram->deferredPart());
|
||||
|
||||
@@ -49,6 +49,73 @@
|
||||
#include <QDomDocument>
|
||||
|
||||
static void check(bool value, const char *message) { if (!value) throw std::runtime_error(message); }
|
||||
static QStringList sortedLines(QString xml, const QString &path);
|
||||
|
||||
//A new revision on every folio (its title block's revision index and
|
||||
//author), saved lazily and with every folio built: what saving builds,
|
||||
//and whether the two saves hold the same. A change to folio
|
||||
//@p label_folio's label (what formulas read) when it is not -1
|
||||
static void revisionEveryFolio(const QString &source, const QString &output, const QString &name,
|
||||
const QString &path, int label_folio = -1)
|
||||
{
|
||||
QString error;
|
||||
qputenv("QET_LAZY_FOLIOS", "1");
|
||||
QETProject revised(source);
|
||||
const int unbuilt = revised.unloadedFolioCount();
|
||||
QetScriptApi api(&revised);
|
||||
for (int i = 0 ; i < api.folioCount() ; ++i) {
|
||||
check(api.setFolioProperty(i, "indexrev", "B"), "a folio's revision set");
|
||||
check(api.setFolioProperty(i, "author", "Revised"), "a folio's author set");
|
||||
}
|
||||
if (label_folio >= 0)
|
||||
check(api.setFolioProperty(label_folio, "folio", "X%id"), "a folio's label set");
|
||||
check(revised.unloadedFolioCount() == unbuilt, "setting the folios' title blocks builds none");
|
||||
int with_tables = 0;
|
||||
for (Diagram *folio : revised.folios())
|
||||
if (!folio->isLoaded() && revised.unbuiltFolioHasTables(folio)) ++with_tables;
|
||||
const bool qetz = QETProject::isContainerPath(source);
|
||||
const QString lazy_path = output + "/" + name + (qetz ? ".qetz" : ".qet");
|
||||
if (qetz) check(revised.writeContainerFile(lazy_path, &error), "revised project saved");
|
||||
else { revised.setFilePath(lazy_path); check(revised.write().isOk(), "revised project saved"); }
|
||||
const int built = unbuilt - revised.unloadedFolioCount();
|
||||
std::printf("%s: new revision on every folio, %d folios built by saving, %d hold a table%s\n",
|
||||
qPrintable(name), built, with_tables,
|
||||
label_folio >= 0 ? ", one label changed" : "");
|
||||
if (label_folio < 0)
|
||||
check(built <= with_tables, "saving a new revision builds only the folios holding a table");
|
||||
else
|
||||
check(revised.folios().at(label_folio)->isLoaded(), "a folio whose label changed is built to save");
|
||||
qunsetenv("QET_LAZY_FOLIOS");
|
||||
const QString usual_path = output + "/" + name + "-usual" + (qetz ? ".qetz" : ".qet");
|
||||
{
|
||||
QETProject usual(source);
|
||||
QetScriptApi usual_api(&usual);
|
||||
for (int i = 0 ; i < usual_api.folioCount() ; ++i) {
|
||||
usual_api.setFolioProperty(i, "indexrev", "B");
|
||||
usual_api.setFolioProperty(i, "author", "Revised");
|
||||
}
|
||||
if (label_folio >= 0) usual_api.setFolioProperty(label_folio, "folio", "X%id");
|
||||
if (qetz) check(usual.writeContainerFile(usual_path, &error), "revision saved when built");
|
||||
else { usual.setFilePath(usual_path); check(usual.write().isOk(), "revision saved when built"); }
|
||||
}
|
||||
QETProject lazy_back(lazy_path), usual_back(usual_path);
|
||||
qputenv("QET_LAZY_FOLIOS", "1");
|
||||
//What a save says of itself (file name, path, time) left out
|
||||
auto lines = [&path](QETProject &project) {
|
||||
QStringList list = sortedLines(project.toXml().toString(), path);
|
||||
list.removeIf([](const QString &line) { return line.contains(QLatin1String("name=\"saved")); });
|
||||
return list;
|
||||
};
|
||||
const QStringList lazy_lines = lines(lazy_back), usual_lines = lines(usual_back);
|
||||
if (lazy_lines != usual_lines) {
|
||||
int shown = 0;
|
||||
for (const QString &line : usual_lines)
|
||||
if (!lazy_lines.contains(line) && shown++ < 8) std::printf(" when built: %s\n", qPrintable(line.left(300)));
|
||||
for (const QString &line : lazy_lines)
|
||||
if (!usual_lines.contains(line) && shown++ < 16) std::printf(" lazily: %s\n", qPrintable(line.left(300)));
|
||||
}
|
||||
check(lazy_lines == usual_lines, "a new revision saved as when built");
|
||||
}
|
||||
|
||||
//Every symbol's numbering, in a stable order
|
||||
static QStringList numbering(const QETProject &project)
|
||||
@@ -380,6 +447,7 @@ int main(int argc, char **argv)
|
||||
for (const QString &title : titles)
|
||||
check(gaps(numbered, title) == gaps(eager, title), "a scheme's gaps as when built");
|
||||
check(numbered.unloadedFolioCount() == folios.size() - strip_folios.size(), "numbering read without building");
|
||||
revisionEveryFolio(path, output, "revised-qet", path);
|
||||
{
|
||||
//Wire numbers exported from the database filled from the
|
||||
//document, as when built (D5)
|
||||
@@ -880,6 +948,11 @@ int main(int argc, char **argv)
|
||||
"a symbol changed before its folio was built shows the change");
|
||||
}
|
||||
|
||||
//A new revision on every folio: saved from the folios as read,
|
||||
//not built; a label change builds its folio (D5)
|
||||
revisionEveryFolio(zipped, output, "revised", path);
|
||||
revisionEveryFolio(zipped, output, "relabelled", path, busiest);
|
||||
|
||||
//The wire numbers and wiring list exports, and the terminal
|
||||
//numbering dialog: as when built, building only the folios
|
||||
//holding a terminal (D5)
|
||||
|
||||
Reference in New Issue
Block a user