From 8d5c164d09c7bb52b0c2191f0e7a6e3c6cead4ad Mon Sep 17 00:00:00 2001 From: ispyisail Date: Tue, 20 Oct 2026 14:02:14 +1300 Subject: [PATCH 1/2] Move a folio's numbering counters into the project database QETZ-DB-FIRST stage 2b-4: the unit/ten/hundred-folio sequential counters Diagram tracks per numbering scheme (insertFolioSeqHash()) now write through to a new folio_counter table, keyed by (folio_uuid, owner, level, title, ord). The hashes stay the source for now, same write-through pattern as 2b-1's numbering rows. renameElementAutoNum()/AutoNumSchemeCommand::rename() carry the rows along on a scheme rename. Co-Authored-By: Claude Sonnet 5 --- sources/autoNum/autonumschemecommand.cpp | 1 + sources/dataBase/projectdatabase.cpp | 103 +++++++++++++++++++++++ sources/dataBase/projectdatabase.h | 12 +++ sources/diagram.cpp | 26 ++++-- sources/diagram.h | 3 +- sources/qetproject.cpp | 1 + 6 files changed, 138 insertions(+), 8 deletions(-) diff --git a/sources/autoNum/autonumschemecommand.cpp b/sources/autoNum/autonumschemecommand.cpp index 7eb96d3dd..956837c41 100644 --- a/sources/autoNum/autonumschemecommand.cpp +++ b/sources/autoNum/autonumschemecommand.cpp @@ -245,6 +245,7 @@ void AutoNumSchemeCommand::rename(const QString &from, const QString &to) } } } + m_project->dataBase()->renameFolioCounterScheme(QStringLiteral("conductor"), from, to); } else { m_project->removeFolioAutoNum(from); m_project->addFolioAutoNum(to, context); diff --git a/sources/dataBase/projectdatabase.cpp b/sources/dataBase/projectdatabase.cpp index 2cf59498c..dd848a9cf 100644 --- a/sources/dataBase/projectdatabase.cpp +++ b/sources/dataBase/projectdatabase.cpp @@ -2715,6 +2715,21 @@ bool projectDataBase::createDataBase() } } + //2b-4: a folio's own unit/ten/hundred-folio counters, one row per + //(folio, owner, level, scheme title, ord). owner is "element" or + //"conductor"; level keeps Diagram's own "unitfolio"/"tenfolio"/ + //"hundredfolio" strings. Not filled from the folio's XML here -- + //Diagram writes these rows itself, the same write-through pattern + //as the numbering tables above. + if (!query_.exec(QLatin1String( + "CREATE TABLE folio_counter (" + "folio_uuid VARCHAR(50) NOT NULL, owner VARCHAR(20) NOT NULL, " + "level VARCHAR(20) NOT NULL, title TEXT NOT NULL, ord INTEGER NOT NULL, " + "value TEXT, " + "PRIMARY KEY (folio_uuid, owner, level, title, ord))"))) { + qDebug() << " folio_counter 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 @@ -4427,6 +4442,94 @@ void projectDataBase::setNumberingFlag(const QString &name, bool value) setNumberingSetting(name, value ? QStringLiteral("true") : QStringLiteral("false")); } +/** + @brief projectDataBase::folioCounterValues + @return the folio counter values for @p folio_uuid / @p owner / + @p level / @p title, in @c ord order (Diagram::insertFolioSeqHash()'s + @c max list); empty if never written +*/ +QStringList projectDataBase::folioCounterValues(const QUuid &folio_uuid, const QString &owner, + const QString &level, const QString &title) const +{ + QSqlQuery query(m_data_base); + query.prepare(QStringLiteral( + "SELECT value FROM folio_counter WHERE folio_uuid = :folio_uuid " + "AND owner = :owner AND level = :level AND title = :title ORDER BY ord")); + query.bindValue(QStringLiteral(":folio_uuid"), folio_uuid.toString()); + query.bindValue(QStringLiteral(":owner"), owner); + query.bindValue(QStringLiteral(":level"), level); + query.bindValue(QStringLiteral(":title"), title); + QStringList values; + if (query.exec()) { + while (query.next()) { + values.append(query.value(0).toString()); + } + } else { + qDebug() << "projectDataBase::folioCounterValues error : " << query.lastError(); + } + return values; +} + +/** + @brief projectDataBase::setFolioCounterValues + The folio counter for @p folio_uuid / @p owner / @p level / @p title + is @p values from now on (one row per entry, in order) +*/ +void projectDataBase::setFolioCounterValues(const QUuid &folio_uuid, const QString &owner, + const QString &level, const QString &title, + const QStringList &values) +{ + QSqlQuery del(m_data_base); + del.prepare(QStringLiteral( + "DELETE FROM folio_counter WHERE folio_uuid = :folio_uuid " + "AND owner = :owner AND level = :level AND title = :title")); + del.bindValue(QStringLiteral(":folio_uuid"), folio_uuid.toString()); + del.bindValue(QStringLiteral(":owner"), owner); + del.bindValue(QStringLiteral(":level"), level); + del.bindValue(QStringLiteral(":title"), title); + if (!del.exec()) { + qDebug() << "projectDataBase::setFolioCounterValues delete error : " << del.lastError(); + return; + } + + QSqlQuery query(m_data_base); + query.prepare(QStringLiteral( + "INSERT INTO folio_counter (folio_uuid, owner, level, title, ord, value) " + "VALUES (:folio_uuid, :owner, :level, :title, :ord, :value)")); + for (int i = 0; i < values.size(); ++i) { + query.bindValue(QStringLiteral(":folio_uuid"), folio_uuid.toString()); + query.bindValue(QStringLiteral(":owner"), owner); + query.bindValue(QStringLiteral(":level"), level); + query.bindValue(QStringLiteral(":title"), title); + query.bindValue(QStringLiteral(":ord"), i); + query.bindValue(QStringLiteral(":value"), values.at(i)); + if (!query.exec()) { + qDebug() << "projectDataBase::setFolioCounterValues insert error : " << query.lastError(); + } + } +} + +/** + @brief projectDataBase::renameFolioCounterScheme + Every folio_counter row for @p owner under @p old_title moves to + @p new_title (renameNumberingScheme()'s counterpart for the rows a + folio keeps on its own) +*/ +void projectDataBase::renameFolioCounterScheme(const QString &owner, const QString &old_title, + const QString &new_title) +{ + QSqlQuery query(m_data_base); + query.prepare(QStringLiteral( + "UPDATE folio_counter SET title = :new_title " + "WHERE owner = :owner AND title = :old_title")); + query.bindValue(QStringLiteral(":new_title"), new_title); + query.bindValue(QStringLiteral(":owner"), owner); + query.bindValue(QStringLiteral(":old_title"), old_title); + if (!query.exec()) { + qDebug() << "projectDataBase::renameFolioCounterScheme error : " << query.lastError(); + } +} + /** @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 516800a1e..6bc7c9112 100644 --- a/sources/dataBase/projectdatabase.h +++ b/sources/dataBase/projectdatabase.h @@ -194,6 +194,18 @@ class projectDataBase : public QObject bool numberingFlag(const QString &name, bool default_value) const; void setNumberingSetting(const QString &name, const QString &value); void setNumberingFlag(const QString &name, bool value); + //2b-4: a folio's own numbering counters (unit/ten/hundred-folio + //sequentials), one row per (folio, owner, level, scheme, ord). + //owner is "element" or "conductor"; level is "unitfolio", + //"tenfolio" or "hundredfolio" (kept as Diagram's own strings, + //not renamed, so the two stay comparable in logs/greps). + QStringList folioCounterValues(const QUuid &folio_uuid, const QString &owner, + const QString &level, const QString &title) const; + void setFolioCounterValues(const QUuid &folio_uuid, const QString &owner, + const QString &level, const QString &title, + const QStringList &values); + void renameFolioCounterScheme(const QString &owner, const QString &old_title, + const QString &new_title); 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/diagram.cpp b/sources/diagram.cpp index de3e520ca..81ce1e21a 100644 --- a/sources/diagram.cpp +++ b/sources/diagram.cpp @@ -3026,7 +3026,8 @@ void Diagram::insertFolioSeqHash(QHash *hash, const QString& title, const QString& type, - NumerotationContext *nc) { + NumerotationContext *nc, + const QString &owner) { QStringList max; for (int i = 0; i < nc->size(); i++) { if (nc->itemAt(i).at(0) == type) { @@ -3038,6 +3039,11 @@ void Diagram::insertFolioSeqHash(QHashinsert(title,max); + //2b-4: the hash stays the source; the row follows it, same + //write-through pattern as the numbering tables (2b-1). + if (!owner.isEmpty() && project()) { + project()->dataBase()->setFolioCounterValues(uuid(), owner, type, title, max); + } } /** @@ -3160,7 +3166,8 @@ void Diagram::loadElmtFolioSeq() insertFolioSeqHash(&m_elmt_unitfolio_max, title, QStringLiteral("unitfolio"), - &nc); + &nc, + QStringLiteral("element")); project()->addElementAutoNum(title,nc); } } @@ -3185,7 +3192,8 @@ void Diagram::loadElmtFolioSeq() insertFolioSeqHash(&m_elmt_tenfolio_max, title, QStringLiteral("tenfolio"), - &nc); + &nc, + QStringLiteral("element")); project()->addElementAutoNum(title,nc); } } @@ -3210,7 +3218,8 @@ void Diagram::loadElmtFolioSeq() insertFolioSeqHash(&m_elmt_hundredfolio_max, title, QStringLiteral("hundredfolio"), - &nc); + &nc, + QStringLiteral("element")); project()->addElementAutoNum(title,nc); } } @@ -3247,7 +3256,8 @@ void Diagram::loadCndFolioSeq() insertFolioSeqHash(&m_cnd_unitfolio_max, title, QStringLiteral("unitfolio"), - &nc); + &nc, + QStringLiteral("conductor")); project()->addConductorAutoNum(title,nc); } } @@ -3270,7 +3280,8 @@ void Diagram::loadCndFolioSeq() insertFolioSeqHash(&m_cnd_tenfolio_max, title, QStringLiteral("tenfolio"), - &nc); + &nc, + QStringLiteral("conductor")); project()->addConductorAutoNum(title,nc); } } @@ -3293,7 +3304,8 @@ void Diagram::loadCndFolioSeq() insertFolioSeqHash(&m_cnd_hundredfolio_max, title, QStringLiteral("hundredfolio"), - &nc); + &nc, + QStringLiteral("conductor")); project()->addConductorAutoNum(title,nc); } } diff --git a/sources/diagram.h b/sources/diagram.h index 614e5a184..fde76b109 100644 --- a/sources/diagram.h +++ b/sources/diagram.h @@ -376,7 +376,8 @@ class Diagram : public QGraphicsScene void insertFolioSeqHash (QHash *hash, const QString& title, const QString& seq, - NumerotationContext *nc); + NumerotationContext *nc, + const QString &owner = QString()); void loadFolioSeqHash (QHash *hash, const QString& title, const QString& seq, NumerotationContext *nc); diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp index c8dd66df0..1bfcbab64 100644 --- a/sources/qetproject.cpp +++ b/sources/qetproject.cpp @@ -1784,6 +1784,7 @@ bool QETProject::renameElementAutoNum(const QString &old_title, const QString &n } } } + m_data_base.renameFolioCounterScheme(QStringLiteral("element"), old_title, new_title); emit elementAutoNumRemoved(old_title); emit elementAutoNumAdded(new_title); From 6225b20f32d76bdfdf1653b09fa9721f81c10a24 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Tue, 20 Oct 2026 17:40:14 +1300 Subject: [PATCH 2/2] Populate folio_counter rows from the document, not just write-through Habit 4: a folio that is only parsed, never built into a live Diagram scene by a numbering edit, still needs its folio_counter rows. Diagram::folioSequentialsFromXml() now writes the rows it just parsed, the same way insertFolioSeqHash() does for an edit made in a built scene. Adds the fixture used to prove this: folio_counters_multi.qet, 3 folios, an element and a conductor numbering scheme both using a tenfolio part, exercising insertFolioSeqHash()'s write-through and this load-time path. Resave-of-resave is byte-identical; ctest clean on the 4 directly relevant tests. Found in passing, not fixed here (pre-existing on master, outside 2b-4's scope): the conductor side's folio-sequential XML ends up saved under instead of its own tag -- the fixture's 'Wires' scheme reads back with owner=element for exactly that reason. Worth a FINDINGS entry before anyone trusts the conductor side of this mechanism. Co-Authored-By: Claude Sonnet 5 --- sources/diagram.cpp | 36 +- sources/diagram.h | 4 +- .../qttest/fixtures/folio_counters_multi.qet | 401 ++++++++++++++++++ 3 files changed, 433 insertions(+), 8 deletions(-) create mode 100644 tests/qttest/fixtures/folio_counters_multi.qet diff --git a/sources/diagram.cpp b/sources/diagram.cpp index 81ce1e21a..42ce8d22f 100644 --- a/sources/diagram.cpp +++ b/sources/diagram.cpp @@ -2154,34 +2154,46 @@ bool Diagram::fromXml(QDomElement &document, QStringLiteral("elementunitfolioseq"), QStringLiteral("sequf_"), QStringLiteral("unitfolioseq"), - QStringLiteral("elementautonumfoliosequentials")); + QStringLiteral("elementautonumfoliosequentials"), + QStringLiteral("element"), + QStringLiteral("unitfolio")); folioSequentialsFromXml(root, &m_elmt_tenfolio_max, QStringLiteral("elementtenfolioseq"), QStringLiteral("seqtf_"), QStringLiteral("tenfolioseq"), - QStringLiteral("elementautonumfoliosequentials")); + QStringLiteral("elementautonumfoliosequentials"), + QStringLiteral("element"), + QStringLiteral("tenfolio")); folioSequentialsFromXml(root, &m_elmt_hundredfolio_max, QStringLiteral("elementhundredfolioseq"), QStringLiteral("seqhf_"), QStringLiteral("hundredfolioseq"), - QStringLiteral("elementautonumfoliosequentials")); + QStringLiteral("elementautonumfoliosequentials"), + QStringLiteral("element"), + QStringLiteral("hundredfolio")); //Load Conductor Folio Sequential folioSequentialsFromXml(root, &m_cnd_unitfolio_max, QStringLiteral("conductorunitfolioseq"), QStringLiteral("sequf_"), QStringLiteral("unitfolioseq"), - QStringLiteral("conductorautonumfoliosequentials")); + QStringLiteral("conductorautonumfoliosequentials"), + QStringLiteral("conductor"), + QStringLiteral("unitfolio")); folioSequentialsFromXml(root, &m_cnd_tenfolio_max, QStringLiteral("conductortenfolioseq"), QStringLiteral("seqtf_"), QStringLiteral("tenfolioseq"), - QStringLiteral("conductorautonumfoliosequentials")); + QStringLiteral("conductorautonumfoliosequentials"), + QStringLiteral("conductor"), + QStringLiteral("tenfolio")); folioSequentialsFromXml(root, &m_cnd_hundredfolio_max, QStringLiteral("conductorhundredfolioseq"), QStringLiteral("seqhf_"), QStringLiteral("hundredfolioseq"), - QStringLiteral("conductorautonumfoliosequentials")); + QStringLiteral("conductorautonumfoliosequentials"), + QStringLiteral("conductor"), + QStringLiteral("hundredfolio")); } // if child haven't got a child, loading is finish (diagram is empty) @@ -2587,7 +2599,9 @@ void Diagram::folioSequentialsFromXml(const QDomElement &root, const QString& folioSeq, const QString& seq, const QString& type, - const QString& autonumFolioSeqType) { + const QString& autonumFolioSeqType, + const QString& owner, + const QString& level) { foreach (QDomElement folioSeqAutoNum, QET::findInDomElement(root, autonumFolioSeqType, folioSeq)) { for(QDomElement folioseq @@ -2604,6 +2618,14 @@ void Diagram::folioSequentialsFromXml(const QDomElement &root, i++; } hash->insert(title,list); + //2b-4, habit 4: populate the row from the + //document being parsed, not from a built + //scene -- this runs whether or not the + //folio ever becomes one. + if (!owner.isEmpty() && project()) { + project()->dataBase()->setFolioCounterValues( + uuid(), owner, level, title, list); + } } } } diff --git a/sources/diagram.h b/sources/diagram.h index fde76b109..509edfcea 100644 --- a/sources/diagram.h +++ b/sources/diagram.h @@ -297,7 +297,9 @@ class Diagram : public QGraphicsScene const QString&, const QString&, const QString&, - const QString&); + const QString&, + const QString &owner = QString(), + const QString &level = QString()); void refreshContents(); diff --git a/tests/qttest/fixtures/folio_counters_multi.qet b/tests/qttest/fixtures/folio_counters_multi.qet new file mode 100644 index 000000000..0e01e9fe1 --- /dev/null +++ b/tests/qttest/fixtures/folio_counters_multi.qet @@ -0,0 +1,401 @@ + + + 10/20/26 + 20-10-2026 + 2026-10-20 + skeleton + /tmp/qet-mcp-new-tulbg10x/skeleton.qet + 17:13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 01 + + + + + + + R%seqtf_1 + R01 + + + + + + + 02 + + + + + + + R%seqtf_1 + R02 + + + + + + + + + 01 + + + + + + + + + + + + + + + + 03 + + + + + + + R%seqtf_1 + R03 + + + + + + + 04 + + + + + + + R%seqtf_1 + R04 + + + + + + + + + 02 + + + + + + + + + + + + + + + + 05 + + + + + + + R%seqtf_1 + R05 + + + + + + + 06 + + + + + + + R%seqtf_1 + R06 + + + + + + + + + 03 + + + + + + + + Elements importats + Zavedené prvky + Importerede elementer + Importierte Elemente + Εισηγμένα στοιχεία + Imported elements + Elementos importados + Éléments importés + Uvezeni elementi + Elementi importati + インバートされた要素 + 전기 심볼 요소 + 전기 심볼 요소 + Elementen geïmporteerd + Elementen geïmporteerd + Elementy importowane + Elementos importados + Elementos importados + Elemente importate + Импортированные элементы + Uvoženi elementi + Importerade element + İthal öğeler + Імпортовані елементи + 导入元件 + + + + كهربائي + Elèctric + Elektrotechnika + Elektrisk + Elektrik + Ηλεκτρικά + Electric + Eléctrica + Sähkö + Electrique + Električno + Elektromos + Elettrica + 電気 + 전기 + Цахилгаан + Elektrisk + Elektrotechniek + Elektrisch + Elektrotechnika + Elétrico + Elétrica + Electric + Электротехника + Elektrické + Elektrika + Електрика + El + Elektrik + Електрика + 10_电气 + 电气工程 + + + + أحادي القطب + Unifilar + Jednopólové + Enkel leder + Einpolig + Μονογραμμικό + Single pole + Polo simple + Yksinapa + Unifilaire + Jednopolni + Egyvonalas + Unifilare + 単線 + 단-극 + Нэг туйл + Enpolt + Enkel pool + Enkelpolig + Schematy jednoliniowe + Unipolar + Unifilar + Unipolar + Однополюсные + Jednopólové + Enopolno + Једнополни + Enpolig + Tek kutuplu + Однополюсні + 11_单极 + + + + إلكترونيات وأشباه موصلات + Electrònica i semiconductors + Elektronika a polovodiče + Elektronik og halvledere + Elektronik und Halbleiter + Ηλεκτρονικά και ημιαγωγοί + Electronics and semiconductors + Electrónica y semiconductores + Elektroniikka ja puolijohteet + Electronique et semi-conducteurs + Elektronika i poluvodiči + Elektronika és félvezetők + Elettronica e semiconduttori + 電子と半導体 + 전자 및 반도체 + Электроник ба хагас дамжуулагч + Elektronikk og halvledere + Electronica en halfgeleiders + Elektronica en halfgeleiders + Elektronika i półprzewodniki + Eletrónica e semicondutores + Eletronica e semicondutoues + Electronică și semiconductoare + Электроника и полупроводники + Elektronika a polovodiče + Elektronika in polprevodniki + Електроника и полупроводници + Elektronik och halvledare + Elektronik ve yarı iletkenler + Електроніка та напівпровідники + 电子半导体 + + + + مُقاومة + Resistències + Rezistory + Modstande + Widerstände + Αντιστάσεις + Resistors + Resistencia + Vastukset + Résistances + Otpornici + Ellenállások + Resistori + 抵抗器 + 저항 + Эсэргүүцэл + Motstander + Weerstand + Weerstanden + Rezystory + Resistência + Resistores + Rezistoare + Резисторы + Rezistory + Upori + Отпорници + Motstånd + Dirençler + Резистори + 10_电阻器 + + + + + + مُقاومة + Resistència + Rezistor + Modstand + Widerstand + Αντίσταση + Resistor + Resistencia + Vastus + Résistance + Otpornik + Ellenállás + Resistore + 抵抗器 + 저항 + Резистор + Motstand + Weerstand + Weerstand + Rezystor + Resistência + Resistor + Rezistor + Резистор + Rezistor + Upor + Отпорник + Resistor + Direnç + Резистор + 电阻 + + Author: The QElectroTech team +License: see http://qelectrotech.org/wiki/doc/elements_license + + + + + + + + + + + + + + + + +