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..f41786945 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,65 @@ 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. On a one-folio project: a --run script + // is stopped after 30 s, and a sanitizer build is slow. + 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'); +var renumbered = qet.renumberElementAutoNum(schemes[0].split(': formula=')[0]) > 0; +save(); +if (renumbered) 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 "/741.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"));