From d05cc654add7b1a5f0d9d184806f58cb662d55b1 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Mon, 19 Oct 2026 21:33:49 +1300 Subject: [PATCH] Keep the project's numbering in the project database too QETZ-DB-FIRST stage 2b-1. The numbering schemes of wires, symbols, folios and cables, and the settings saved with them (the scheme in use of each kind, freezing new labels, auto-break, the cable axis and question), get three tables of the project database: numbering_scheme, numbering_part (one row per part, in order) and numbering_setting. QETProject writes them when it reads its file and after each change of its numbering, from every setter; nothing reads them yet. The save writes the numbering through writeNumberingXml(), the code it had moved out of writeDefaultPropertiesXml(), so the same writer can take the rows once the save moves to them (2b-2). QET_CHECK_NUMBERING_ROWS=1 writes the numbering from the project and from the rows at each save and says whether the two differ. tst_elementautonumids::numberingRowsFollowEveryChange checks it after each of 23 changes; with one setter not writing the rows, one save differs and it fails. Co-Authored-By: Claude Opus 5.5 --- sources/dataBase/projectdatabase.cpp | 133 ++++++++++++++++ sources/dataBase/projectdatabase.h | 23 +++ sources/qetproject.cpp | 211 ++++++++++++++++++------- sources/qetproject.h | 4 + tests/qttest/tst_elementautonumids.cpp | 60 +++++++ 5 files changed, 376 insertions(+), 55 deletions(-) diff --git a/sources/dataBase/projectdatabase.cpp b/sources/dataBase/projectdatabase.cpp index 28e04971b..7c1472fd1 100644 --- a/sources/dataBase/projectdatabase.cpp +++ b/sources/dataBase/projectdatabase.cpp @@ -2695,6 +2695,25 @@ bool projectDataBase::createDataBase() qDebug() << " link_table query : " << query_.lastError(); } + //The numbering tables (QETZ-DB-FIRST stage 2b): the project's + //schemes, one row per part of each in its order, and the settings + //saved with them. Not filled from the folios: QETProject writes + //them when it reads its file and whenever its numbering changes. + for (const char *table : { + "CREATE TABLE numbering_scheme (" + "kind VARCHAR(20) NOT NULL, title TEXT NOT NULL, uuid VARCHAR(50), " + "PRIMARY KEY (kind, title))", + "CREATE TABLE numbering_part (" + "kind VARCHAR(20) NOT NULL, title TEXT NOT NULL, ord INTEGER NOT NULL, " + "type TEXT, value TEXT, increase TEXT, initialvalue TEXT, modulus TEXT, format TEXT, " + "PRIMARY KEY (kind, title, ord), " + "FOREIGN KEY (kind, title) REFERENCES numbering_scheme (kind, title))", + "CREATE TABLE numbering_setting (name VARCHAR(50) PRIMARY KEY NOT NULL, value TEXT)"}) { + if (!query_.exec(QLatin1String(table))) { + qDebug() << " numbering table query : " << query_.lastError(); + } + } + //Create the terminal table. //Terminal::uuid() is the terminal-position id baked into the catalog //.elmt definition (e.g. "the top terminal") -- identical across every @@ -4091,6 +4110,120 @@ QStringList projectDataBase::linkRowMismatches() return mismatches; } +/** + @brief projectDataBase::setNumbering + The numbering tables hold @p numbering from now on, and nothing else + (QETZ-DB-FIRST stage 2b-1: QETProject writes them after reading its + file and after each change of its numbering) +*/ +void projectDataBase::setNumbering(const Numbering &numbering) +{ + QSqlQuery query(m_data_base); + bool ok = true; + for (const char *table : {"numbering_part", "numbering_scheme", "numbering_setting"}) + ok = ok && query.exec(QStringLiteral("DELETE FROM %1").arg(QLatin1String(table))); + + QSqlQuery scheme(m_data_base); + scheme.prepare(QStringLiteral("INSERT INTO numbering_scheme (kind, title, uuid) " + "VALUES (:kind, :title, :uuid)")); + QSqlQuery part(m_data_base); + part.prepare(QStringLiteral("INSERT INTO numbering_part (kind, title, ord, type, value, " + "increase, initialvalue, modulus, format) VALUES (:kind, :title, " + ":ord, :type, :value, :increase, :initialvalue, :modulus, :format)")); + for (const NumberingScheme &item : numbering.schemes) + { + scheme.bindValue(QStringLiteral(":kind"), item.kind); + scheme.bindValue(QStringLiteral(":title"), item.title); + scheme.bindValue(QStringLiteral(":uuid"), item.uuid.isNull() ? QVariant() + : QVariant(item.uuid.toString())); + ok = ok && scheme.exec(); + for (int i = 0 ; i < item.context.size() ; ++i) + { + const QStringList fields = item.context.itemAt(i); + part.bindValue(QStringLiteral(":kind"), item.kind); + part.bindValue(QStringLiteral(":title"), item.title); + part.bindValue(QStringLiteral(":ord"), i); + int field = 0; + for (const char *name : {":type", ":value", ":increase", ":initialvalue", ":modulus", ":format"}) { + part.bindValue(QLatin1String(name), field < fields.size() ? QVariant(fields.at(field)) + : QVariant()); + ++field; + } + ok = ok && part.exec(); + } + } + + QSqlQuery setting(m_data_base); + setting.prepare(QStringLiteral("INSERT INTO numbering_setting (name, value) VALUES (:name, :value)")); + auto set = [&setting, &ok](const QString &name, const QString &value) { + setting.bindValue(QStringLiteral(":name"), name); + setting.bindValue(QStringLiteral(":value"), value); + ok = ok && setting.exec(); + }; + auto flag = [](bool value) { return value ? QStringLiteral("true") : QStringLiteral("false"); }; + set(QStringLiteral("conductor_current"), numbering.current_conductor); + set(QStringLiteral("element_current"), numbering.current_element); + set(QStringLiteral("cable_current"), numbering.current_cable); + set(QStringLiteral("freeze_new_conductors"), flag(numbering.freeze_new_conductors)); + set(QStringLiteral("freeze_new_elements"), flag(numbering.freeze_new_elements)); + set(QStringLiteral("auto_break_conductors"), flag(numbering.auto_break_conductors)); + set(QStringLiteral("cable_x_axis_first"), flag(numbering.cable_x_axis_first)); + set(QStringLiteral("cable_ask_numbering"), flag(numbering.cable_ask_numbering)); + + if (!ok) { + qDebug() << "projectDataBase::setNumbering error : " << query.lastError() + << scheme.lastError() << part.lastError() << setting.lastError(); + } +} + +/** + @brief projectDataBase::numbering + @return the numbering the numbering tables hold (setNumbering()); the + schemes in no particular order +*/ +projectDataBase::Numbering projectDataBase::numbering() const +{ + Numbering numbering; + QSqlQuery query(m_data_base); + query.exec(QStringLiteral("SELECT kind, title, uuid FROM numbering_scheme")); + QHash, int> index; + while (query.next()) { + NumberingScheme scheme; + scheme.kind = query.value(0).toString(); + scheme.title = query.value(1).toString(); + scheme.uuid = QUuid(query.value(2).toString()); + index.insert({scheme.kind, scheme.title}, numbering.schemes.size()); + numbering.schemes << scheme; + } + query.exec(QStringLiteral("SELECT kind, title, type, value, increase, initialvalue, modulus, " + "format FROM numbering_part ORDER BY kind, title, ord")); + while (query.next()) { + const int at = index.value({query.value(0).toString(), query.value(1).toString()}, -1); + if (at < 0) { + continue; + } + numbering.schemes[at].context.addValue(query.value(2).toString(), query.value(3).toString(), + query.value(4).toInt(), query.value(5).toInt(), + query.value(6).toInt(), query.value(7).toString()); + } + query.exec(QStringLiteral("SELECT name, value FROM numbering_setting")); + while (query.next()) + { + const QString name = query.value(0).toString(); + const QString value = query.value(1).toString(); + const bool flag = value == QLatin1String("true"); + if (name == QLatin1String("conductor_current")) numbering.current_conductor = value; + else if (name == QLatin1String("element_current")) numbering.current_element = value; + else if (name == QLatin1String("cable_current")) numbering.current_cable = value; + else if (name == QLatin1String("freeze_new_conductors")) numbering.freeze_new_conductors = flag; + else if (name == QLatin1String("freeze_new_elements")) numbering.freeze_new_elements = flag; + else if (name == QLatin1String("auto_break_conductors")) numbering.auto_break_conductors = flag; + else if (name == QLatin1String("cable_x_axis_first")) numbering.cable_x_axis_first = flag; + else if (name == QLatin1String("cable_ask_numbering")) numbering.cable_ask_numbering = flag; + } + return numbering; +} + /** @brief projectDataBase::savedLinks The links @p element saves, read from its link rows, in their order diff --git a/sources/dataBase/projectdatabase.h b/sources/dataBase/projectdatabase.h index 2c1118a58..8d75dc585 100644 --- a/sources/dataBase/projectdatabase.h +++ b/sources/dataBase/projectdatabase.h @@ -36,6 +36,7 @@ #include "../titleblockproperties.h" #include "../conductorproperties.h" #include "../autoNum/assignvariables.h" +#include "../autoNum/numerotationcontext.h" class Element; class QETProject; @@ -156,6 +157,28 @@ class projectDataBase : public QObject void conductorUuidChanged(Conductor *conductor, const QUuid &old_uuid); QStringList conductorPropertiesMismatches() const; QStringList linkRowMismatches(); + /// A numbering scheme of the project (QETZ-DB-FIRST stage 2b) + struct NumberingScheme { + QString kind; ///< conductor, element, folio or cable + QString title; + QUuid uuid; ///< element schemes only + NumerotationContext context; + }; + /// The project's numbering: its schemes of every kind and the + /// settings saved with them + struct Numbering { + QList schemes; + QString current_conductor; + QString current_element; + QString current_cable; + bool freeze_new_conductors = false; + bool freeze_new_elements = false; + bool auto_break_conductors = false; + bool cable_x_axis_first = true; + bool cable_ask_numbering = true; + }; + void setNumbering(const Numbering &numbering); + Numbering numbering() const; bool savedLinks(const Element *element, QList> *links); //The link rows are written (opening filled them): a symbol //built links from them (Element::initLink()) diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp index cb4d6aff7..cc1de0d6f 100644 --- a/sources/qetproject.cpp +++ b/sources/qetproject.cpp @@ -140,6 +140,8 @@ m_project_properties_handler{this} setCurrentCableAutoNum(cableData.second); } } + //m_auto_break_conductor above is read from the settings directly + writeNumberingRows(); } ProjectPropertiesHandler &QETProject::projectPropertiesHandler() @@ -1504,6 +1506,7 @@ QString QETProject::elementCurrentAutoNum () const */ void QETProject::setCurrrentElementAutonum(QString autoNum) { m_current_element_autonum = std::move(autoNum); + writeNumberingRows(); } /** @@ -1549,6 +1552,7 @@ QString QETProject::cableCurrentAutoNum() const void QETProject::setCurrentCableAutoNum(QString autoNum) { m_current_cable_autonum = std::move(autoNum); + writeNumberingRows(); } /** @@ -1581,6 +1585,7 @@ bool QETProject::cableXAxisFirst() const void QETProject::setCableXAxisFirst(bool x_axis_first) { m_cable_axis_x_first = x_axis_first; + writeNumberingRows(); } /** @@ -1607,6 +1612,7 @@ bool QETProject::cableAskNumbering() const void QETProject::setCableAskNumbering(bool ask) { m_cable_ask_numbering = ask; + writeNumberingRows(); } /** @@ -1660,6 +1666,7 @@ QString QETProject::conductorCurrentAutoNum () const */ void QETProject::setCurrentConductorAutoNum(QString autoNum) { m_current_conductor_autonum = std::move(autoNum); + writeNumberingRows(); } /** @@ -1680,6 +1687,7 @@ QHash QETProject::folioAutoNum() const */ void QETProject::addConductorAutoNum(const QString& key, const NumerotationContext& context) { m_conductor_autonum.insert(key, context); + writeNumberingRows(); emit autoNumContextUpdated(); } @@ -1724,6 +1732,7 @@ void QETProject::addElementAutoNum(const QString &key, } m_element_autonum_id.insert(key, scheme_id); m_element_autonum.insert(key, context); + writeNumberingRows(); emit elementAutoNumAdded(key); emit autoNumContextUpdated(); } @@ -1795,6 +1804,7 @@ bool QETProject::renameElementAutoNum(const QString &old_title, const QString &n } } + writeNumberingRows(); emit elementAutoNumRemoved(old_title); emit elementAutoNumAdded(new_title); emit autoNumContextUpdated(); @@ -2023,6 +2033,7 @@ QList QETProject::symbolNumbering() const */ void QETProject::addFolioAutoNum(const QString& key, const NumerotationContext& context) { m_folio_autonum.insert(key, context); + writeNumberingRows(); emit autoNumContextUpdated(); } @@ -2049,6 +2060,7 @@ void QETProject::addCableAutoNum(const QString& key, const NumerotationContext& || !m_cable_autonum.contains(m_current_cable_autonum))) { m_current_cable_autonum = key; } + writeNumberingRows(); emit autoNumContextUpdated(); } @@ -2059,6 +2071,7 @@ void QETProject::addCableAutoNum(const QString& key, const NumerotationContext& */ void QETProject::removeConductorAutoNum(const QString& key) { m_conductor_autonum.remove(key); + writeNumberingRows(); } /** @@ -2070,6 +2083,7 @@ void QETProject::removeElementAutoNum(const QString& key) { m_element_autonum.remove(key); m_element_autonum_id.remove(key); + writeNumberingRows(); emit elementAutoNumRemoved(key); } @@ -2080,6 +2094,7 @@ void QETProject::removeElementAutoNum(const QString& key) */ void QETProject::removeFolioAutoNum(const QString& key) { m_folio_autonum.remove(key); + writeNumberingRows(); } /** @@ -2094,6 +2109,7 @@ void QETProject::removeCableAutoNum(const QString& key) m_current_cable_autonum = m_cable_autonum.isEmpty() ? QString() : m_cable_autonum.keys().first(); } + writeNumberingRows(); emit autoNumContextUpdated(); } @@ -2186,6 +2202,7 @@ bool QETProject::isFreezeNewConductors() */ void QETProject::setFreezeNewConductors(bool set) { m_freeze_new_conductors = set; + writeNumberingRows(); } /** @@ -2230,6 +2247,7 @@ bool QETProject::isFreezeNewElements() */ void QETProject::setFreezeNewElements(bool set) { m_freeze_new_elements = set; + writeNumberingRows(); } /** @@ -2363,6 +2381,7 @@ void QETProject::setAutoBreakConductor(bool abc) { if (abc != m_auto_break_conductor) m_auto_break_conductor = abc; + writeNumberingRows(); } /** @@ -4044,6 +4063,7 @@ void QETProject::readDefaultPropertiesXml(QDomDocument &xml_project) m_default_guides.append(g); } } + writeNumberingRows(); } /** @@ -4227,21 +4247,116 @@ void QETProject::writeDefaultPropertiesXml(QDomElement &xml_element) } xml_element.appendChild(xrefs_elmt); + writeNumberingXml(numberingOfProject(), xml_element); + //QET_CHECK_NUMBERING_ROWS=1: say whether the project database's + //numbering rows write what the project writes (QETZ-DB-FIRST stage + //2b-1). A test and debugging aid; it changes nothing. + if (qEnvironmentVariableIntValue("QET_CHECK_NUMBERING_ROWS") == 1) { + QDomDocument from_project, from_rows; + QDomElement project_root = from_project.createElement(QStringLiteral("numbering")); + QDomElement rows_root = from_rows.createElement(QStringLiteral("numbering")); + from_project.appendChild(project_root); + from_rows.appendChild(rows_root); + writeNumberingXml(numberingOfProject(), project_root); + writeNumberingXml(m_data_base.numbering(), rows_root); + const bool same = from_project.toString() == from_rows.toString(); + qWarning().noquote() << "numbering rows:" << (same ? 0 : 1) << "differ"; + } + + // Export default guides + QDomElement guides_elmt = xml_document.createElement("guides"); + for (const auto &g : m_default_guides) { + QDomElement guide_elmt = xml_document.createElement("guide"); + guide_elmt.setAttribute("orientation", static_cast(g.orientation)); + guide_elmt.setAttribute("position", g.position); + guide_elmt.setAttribute("color", g.color.name()); + guides_elmt.appendChild(guide_elmt); + } + xml_element.appendChild(guides_elmt); +} + +/** + @brief QETProject::numberingOfProject + @return the project's numbering schemes and their settings, as the + project holds them, for the project database's numbering tables + (QETZ-DB-FIRST stage 2b) and for the save +*/ +projectDataBase::Numbering QETProject::numberingOfProject() const +{ + projectDataBase::Numbering numbering; + auto add = [&numbering](const QString &kind, const QHash &schemes, + const QHash &ids) { + for (auto it = schemes.cbegin() ; it != schemes.cend() ; ++it) + numbering.schemes << projectDataBase::NumberingScheme{kind, it.key(), ids.value(it.key()), it.value()}; + }; + add(QStringLiteral("conductor"), m_conductor_autonum, {}); + add(QStringLiteral("folio"), m_folio_autonum, {}); + add(QStringLiteral("element"), m_element_autonum, m_element_autonum_id); + add(QStringLiteral("cable"), m_cable_autonum, {}); + numbering.current_conductor = m_current_conductor_autonum; + numbering.current_element = m_current_element_autonum; + numbering.current_cable = m_current_cable_autonum; + numbering.freeze_new_conductors = m_freeze_new_conductors; + numbering.freeze_new_elements = m_freeze_new_elements; + numbering.auto_break_conductors = m_auto_break_conductor; + numbering.cable_x_axis_first = m_cable_axis_x_first; + numbering.cable_ask_numbering = m_cable_ask_numbering; + return numbering; +} + +/** + @brief QETProject::writeNumberingRows + Write the project's numbering into the project database's numbering + tables (QETZ-DB-FIRST stage 2b-1): after reading the file and after + each change of the numbering, so the rows are always what the save + writes (QET_CHECK_NUMBERING_ROWS checks it) +*/ +void QETProject::writeNumberingRows() +{ + m_data_base.setNumbering(numberingOfProject()); +} + +/** + @brief QETProject::writeNumberingXml + Export @p numbering as the , , + and children of @p xml_element +*/ +void QETProject::writeNumberingXml(const projectDataBase::Numbering &numbering, + QDomElement &xml_element) +{ + QDomDocument xml_document = xml_element.ownerDocument(); + //Sorted, as the xrefs are: the schemes come in no particular order + //(they were QHash, whose key order is randomised per process), so + //an unsorted write reorders these children on every save. + auto schemes = [&numbering](const QString &kind) { + QMap sorted; + QStringList titles; + for (const auto &scheme : numbering.schemes) { + if (scheme.kind == kind) { + sorted.insert(scheme.title, scheme); + titles << scheme.title; + } + } + titles.sort(); + QList list; + for (const QString &title : std::as_const(titles)) + list << sorted.value(title); + return list; + }; + auto formula = [](const projectDataBase::NumberingScheme &scheme) { + return autonum::numerotationContextToFormula(scheme.context); + }; + //Export Conductor Autonums QDomElement conductor_autonums = xml_document.createElement("conductors_autonums"); - conductor_autonums.setAttribute("current_autonum", m_current_conductor_autonum); - conductor_autonums.setAttribute("freeze_new_conductors", m_freeze_new_conductors ? "true" : "false"); - conductor_autonums.setAttribute("auto_break_conductors", m_auto_break_conductor ? "true" : "false"); - //Sorted for the same reason as the xrefs above: these three - //collections are QHash, whose key order is randomised per process, - //so an unsorted write reorders these children on every save. - QStringList conductor_autonum_keys = conductorAutoNum().keys(); - conductor_autonum_keys.sort(); - for (const QString &key : std::as_const(conductor_autonum_keys)) { - QDomElement conductor_autonum = conductorAutoNum(key).toXml(xml_document, "conductor_autonum"); - if (key != "" && conductorAutoNumFormula(key) != "") { - conductor_autonum.setAttribute("title", key); - conductor_autonum.setAttribute("formula", conductorAutoNumFormula(key)); + conductor_autonums.setAttribute("current_autonum", numbering.current_conductor); + conductor_autonums.setAttribute("freeze_new_conductors", numbering.freeze_new_conductors ? "true" : "false"); + conductor_autonums.setAttribute("auto_break_conductors", numbering.auto_break_conductors ? "true" : "false"); + for (auto scheme : schemes(QStringLiteral("conductor"))) { + QDomElement conductor_autonum = scheme.context.toXml(xml_document, "conductor_autonum"); + if (scheme.title != "" && formula(scheme) != "") { + conductor_autonum.setAttribute("title", scheme.title); + conductor_autonum.setAttribute("formula", formula(scheme)); conductor_autonums.appendChild(conductor_autonum); } } @@ -4249,31 +4364,30 @@ void QETProject::writeDefaultPropertiesXml(QDomElement &xml_element) //Export Folio Autonums QDomElement folio_autonums = xml_document.createElement("folio_autonums"); - QStringList folio_autonum_keys = folioAutoNum().keys(); - folio_autonum_keys.sort(); - for (const QString &key : std::as_const(folio_autonum_keys)) { - QDomElement folio_autonum = folioAutoNum(key).toXml(xml_document, "folio_autonum"); - folio_autonum.setAttribute("title", key); + for (auto scheme : schemes(QStringLiteral("folio"))) { + QDomElement folio_autonum = scheme.context.toXml(xml_document, "folio_autonum"); + folio_autonum.setAttribute("title", scheme.title); folio_autonums.appendChild(folio_autonum); } xml_element.appendChild(folio_autonums); //Export Element Autonums + const QList element_schemes = schemes(QStringLiteral("element")); + QUuid current_element_id; + for (const auto &scheme : element_schemes) + if (scheme.title == numbering.current_element) current_element_id = scheme.uuid; QDomElement element_autonums = xml_document.createElement("element_autonums"); - element_autonums.setAttribute("current_autonum", m_current_element_autonum); - if (!elementAutoNumId(m_current_element_autonum).isNull()) { - element_autonums.setAttribute("current_autonum_id", - elementAutoNumId(m_current_element_autonum).toString()); + element_autonums.setAttribute("current_autonum", numbering.current_element); + if (!current_element_id.isNull()) { + element_autonums.setAttribute("current_autonum_id", current_element_id.toString()); } - element_autonums.setAttribute("freeze_new_elements", m_freeze_new_elements ? "true" : "false"); - QStringList element_autonum_keys = elementAutoNum().keys(); - element_autonum_keys.sort(); - for (const QString &key : std::as_const(element_autonum_keys)) { - QDomElement element_autonum = elementAutoNum(key).toXml(xml_document, "element_autonum"); - if (key != "" && elementAutoNumFormula(key) != "") { - element_autonum.setAttribute("title", key); - element_autonum.setAttribute("id", elementAutoNumId(key).toString()); - element_autonum.setAttribute("formula", elementAutoNumFormula(key)); + element_autonums.setAttribute("freeze_new_elements", numbering.freeze_new_elements ? "true" : "false"); + for (auto scheme : element_schemes) { + QDomElement element_autonum = scheme.context.toXml(xml_document, "element_autonum"); + if (scheme.title != "" && formula(scheme) != "") { + element_autonum.setAttribute("title", scheme.title); + element_autonum.setAttribute("id", scheme.uuid.toString()); + element_autonum.setAttribute("formula", formula(scheme)); element_autonums.appendChild(element_autonum); } } @@ -4293,23 +4407,21 @@ void QETProject::writeDefaultPropertiesXml(QDomElement &xml_element) //drawn, an axis chosen before it, and the numbering question //answered with no are answers only this element can carry. QDomElement cable_autonums = xml_document.createElement("cable_autonums"); - cable_autonums.setAttribute("current_autonum", m_current_cable_autonum); + cable_autonums.setAttribute("current_autonum", numbering.current_cable); cable_autonums.setAttribute("axis_priority", - m_cable_axis_x_first ? "x" : "y"); + numbering.cable_x_axis_first ? "x" : "y"); //Only the no is written down: a project which has not answered //the numbering question yet asks again, so silence in the file //means asking rather than holding back. - if (!m_cable_ask_numbering) { + if (!numbering.cable_ask_numbering) { cable_autonums.setAttribute("ask_numbering_rule", "false"); } int written_cable_rules = 0; - QStringList cable_autonum_keys = cableAutoNum().keys(); - cable_autonum_keys.sort(); - for (const QString &key : std::as_const(cable_autonum_keys)) { - QDomElement cable_autonum = cableAutoNum(key).toXml(xml_document, "cable_autonum"); - if (key != "" && cableAutoNumFormula(key) != "") { - cable_autonum.setAttribute("title", key); - cable_autonum.setAttribute("formula", cableAutoNumFormula(key)); + for (auto scheme : schemes(QStringLiteral("cable"))) { + QDomElement cable_autonum = scheme.context.toXml(xml_document, "cable_autonum"); + if (scheme.title != "" && formula(scheme) != "") { + cable_autonum.setAttribute("title", scheme.title); + cable_autonum.setAttribute("formula", formula(scheme)); cable_autonums.appendChild(cable_autonum); ++written_cable_rules; } @@ -4318,23 +4430,12 @@ void QETProject::writeDefaultPropertiesXml(QDomElement &xml_element) //chose an axis other than the default and has not answered the //numbering question with no -- then this element would say //nothing the reader does not assume anyway. - if (!m_current_cable_autonum.isEmpty() - || !m_cable_ask_numbering - || !m_cable_axis_x_first + if (!numbering.current_cable.isEmpty() + || !numbering.cable_ask_numbering + || !numbering.cable_x_axis_first || written_cable_rules > 0) { xml_element.appendChild(cable_autonums); } - - // Export default guides - QDomElement guides_elmt = xml_document.createElement("guides"); - for (const auto &g : m_default_guides) { - QDomElement guide_elmt = xml_document.createElement("guide"); - guide_elmt.setAttribute("orientation", static_cast(g.orientation)); - guide_elmt.setAttribute("position", g.position); - guide_elmt.setAttribute("color", g.color.name()); - guides_elmt.appendChild(guide_elmt); - } - xml_element.appendChild(guides_elmt); } /** diff --git a/sources/qetproject.h b/sources/qetproject.h index 260937972..a7b9e7613 100644 --- a/sources/qetproject.h +++ b/sources/qetproject.h @@ -457,6 +457,10 @@ class QETProject : public QObject void writeProjectPropertiesXml(QDomElement &); void writeDefaultPropertiesXml(QDomElement &); + projectDataBase::Numbering numberingOfProject() const; + void writeNumberingRows(); + static void writeNumberingXml(const projectDataBase::Numbering &numbering, + QDomElement &xml_element); void writeUsageXml(QDomElement &); void writeWireHopsXml(QDomElement &); void writeSymbolTextsXml(QDomElement &); diff --git a/tests/qttest/tst_elementautonumids.cpp b/tests/qttest/tst_elementautonumids.cpp index 1c703efda..46fe3d60e 100644 --- a/tests/qttest/tst_elementautonumids.cpp +++ b/tests/qttest/tst_elementautonumids.cpp @@ -42,6 +42,7 @@ class tst_elementautonumids : public QObject int m_run = 0; QString m_preferences; // contents of the settings file of the next run + bool m_check_numbering = false; // QET_CHECK_NUMBERING_ROWS for the runs QProcessEnvironment environment() { @@ -61,6 +62,9 @@ class tst_elementautonumids : public QObject env.insert(QStringLiteral("QET_SETTINGS_DIR"), settings_dir); env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen")); env.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1")); + if (m_check_numbering) { + env.insert(QStringLiteral("QET_CHECK_NUMBERING_ROWS"), QStringLiteral("1")); + } env.insert(QStringLiteral("HOME"), home); env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/config")); env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/data")); @@ -725,6 +729,62 @@ qet.log('PROBE ' + JSON.stringify(r)); // the folios which read the numbering with it (26 folios of // industrial.qet read "Wire"), a numbering which folios read cannot be // removed, an unused one can, and every step is one undo. + // The project database's numbering rows (QETZ-DB-FIRST stage 2b-1) + // follow every change of the numbering: each save below writes the + // numbering from the project and from the rows, and the two must be + // the same (QET_CHECK_NUMBERING_ROWS). A scheme of each kind added, + // renamed, removed, undone and redone, then made current; a + // renumbering and its undo. + void numberingRowsFollowEveryChange() + { + const QString saved = m_dir.filePath(QStringLiteral("numbering-rows.qet")); + const QString script = QStringLiteral(R"JS( +var saves = 0; +function save() { if (qet.save(%1)) saves++; } +save(); +['conductor', 'element', 'folio'].forEach(function (k) { + qet.addAutoNum(k, 'Rows ' + k, ['string:R-', 'unit:7:2']); save(); + qet.renameAutoNum(k, 'Rows ' + k, 'Rows renamed ' + k); save(); + qet.undo(); save(); + qet.redo(); save(); +}); +['conductor', 'element', 'folio'].forEach(function (k) { + qet.removeAutoNum(k, 'Rows renamed ' + k); save(); + qet.undo(); save(); +}); +qet.useElementAutoNum('Rows renamed element'); save(); +qet.useConductorAutoNum(0, 'Rows renamed conductor'); save(); +var schemes = qet.autoNums('element'); +qet.renumberElementAutoNum(schemes[0].split(': formula=')[0]); save(); +qet.undo(); save(); +qet.log('PROBE ' + JSON.stringify({saves: saves})); +)JS").arg(QStringLiteral("'") + saved + QStringLiteral("'")); + + m_check_numbering = true; + const QString path = m_dir.filePath(QStringLiteral("probe%1.js").arg(m_run)); + QFile f(path); + QVERIFY(f.open(QIODevice::WriteOnly)); + f.write(script.toUtf8()); + f.close(); + QProcess proc; + proc.setProcessEnvironment(environment()); + m_check_numbering = false; + proc.start(QStringLiteral(QET_TEST_BINARY_PATH), + {QStringLiteral("--run"), path, QStringLiteral(QET_EXAMPLES_DIR "/industrial.qet")}); + QVERIFY(proc.waitForFinished(180000)); + const QString out = QString::fromUtf8(proc.readAllStandardOutput() + proc.readAllStandardError()); + + QVERIFY2(out.contains(QLatin1String("PROBE {\"saves\":23}")), qPrintable(out.right(2000))); + QCOMPARE(out.count(QLatin1String("numbering rows: 0 differ")), 23); + QCOMPARE(out.count(QLatin1String("numbering rows:")), 23); + // the changes are in the file: the folio numbering's removal was + // undone, the wires of the first folio follow the new numbering + const QByteArray xml = read(saved); + QVERIFY(xml.contains("title=\"Rows renamed folio\"")); + QCOMPARE(diagramAttributes(xml, QStringLiteral("conductorAutonum")).value(0), + QStringLiteral("Rows renamed conductor")); + } + void conductorNumberings() { const QString saved = m_dir.filePath(QStringLiteral("conductors.qet"));