From 61ce75e3800f7c0e9e53b7b9b57f74f1b5bb7bd8 Mon Sep 17 00:00:00 2001 From: Andre Rummler Date: Mon, 2 Feb 2026 06:32:28 +0100 Subject: [PATCH] First draft of elements renumber function. --- cmake/qet_compilation_vars.cmake | 4 + sources/autoNum/renumberelementscommand.cpp | 78 +++++++++++ sources/autoNum/renumberelementscommand.h | 70 ++++++++++ .../autoNum/ui/autonumberingmanagementw.cpp | 20 +++ sources/autoNum/ui/autonumberingmanagementw.h | 1 + .../autoNum/ui/autonumberingmanagementw.ui | 7 + sources/autoNum/ui/renumberelementsdialog.cpp | 75 ++++++++++ sources/autoNum/ui/renumberelementsdialog.h | 59 ++++++++ sources/qetproject.cpp | 128 ++++++++++++++++++ sources/qetproject.h | 14 ++ 10 files changed, 456 insertions(+) create mode 100644 sources/autoNum/renumberelementscommand.cpp create mode 100644 sources/autoNum/renumberelementscommand.h create mode 100644 sources/autoNum/ui/renumberelementsdialog.cpp create mode 100644 sources/autoNum/ui/renumberelementsdialog.h diff --git a/cmake/qet_compilation_vars.cmake b/cmake/qet_compilation_vars.cmake index c0daa3aca..6cd543f73 100644 --- a/cmake/qet_compilation_vars.cmake +++ b/cmake/qet_compilation_vars.cmake @@ -277,6 +277,8 @@ set(QET_SRC_FILES ${QET_DIR}/sources/autoNum/numerotationcontextcommands.h ${QET_DIR}/sources/autoNum/numerotationcontext.cpp ${QET_DIR}/sources/autoNum/numerotationcontext.h + ${QET_DIR}/sources/autoNum/renumberelementscommand.cpp + ${QET_DIR}/sources/autoNum/renumberelementscommand.h ${QET_DIR}/sources/autoNum/ui/autonumberingdockwidget.cpp ${QET_DIR}/sources/autoNum/ui/autonumberingdockwidget.h ${QET_DIR}/sources/autoNum/ui/autonumberingmanagementw.cpp @@ -287,6 +289,8 @@ set(QET_SRC_FILES ${QET_DIR}/sources/autoNum/ui/formulaautonumberingw.h ${QET_DIR}/sources/autoNum/ui/numparteditorw.cpp ${QET_DIR}/sources/autoNum/ui/numparteditorw.h + ${QET_DIR}/sources/autoNum/ui/renumberelementsdialog.cpp + ${QET_DIR}/sources/autoNum/ui/renumberelementsdialog.h ${QET_DIR}/sources/autoNum/ui/selectautonumw.cpp ${QET_DIR}/sources/autoNum/ui/selectautonumw.h diff --git a/sources/autoNum/renumberelementscommand.cpp b/sources/autoNum/renumberelementscommand.cpp new file mode 100644 index 000000000..27f2c5912 --- /dev/null +++ b/sources/autoNum/renumberelementscommand.cpp @@ -0,0 +1,78 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ + +#include "renumberelementscommand.h" + +#include "../qetproject.h" +#include "../qetgraphicsitem/element.h" + +RenumberElementsCommand::RenumberElementsCommand( + QETProject *project, + QVector changes, + QHash old_ctx, + QHash new_ctx, + const QString &text) + : QUndoCommand(text) + , project_(project) + , changes_(std::move(changes)) + , old_ctx_(std::move(old_ctx)) + , new_ctx_(std::move(new_ctx)) +{ +} + +void RenumberElementsCommand::undo() +{ + apply(false); +} + +void RenumberElementsCommand::redo() +{ + // QUndoStack calls redo() once immediately after push(); keep standard behaviour. + if (first_redo_) { + first_redo_ = false; + } + apply(true); +} + +void RenumberElementsCommand::apply(bool use_new) +{ + if (!project_) return; + + // Restore project contexts + const auto &ctx = use_new ? new_ctx_ : old_ctx_; + for (auto it = ctx.constBegin(); it != ctx.constEnd(); ++it) { + project_->addElementAutoNum(it.key(), it.value()); + } + + // Apply per-element changes + for (const ElementChange &c : changes_) { + if (!c.element) continue; + + const bool frozen = use_new ? c.new_frozen : c.old_frozen; + const auto &infos = use_new ? c.new_infos : c.old_infos; + const auto &seq = use_new ? c.new_seq : c.old_seq; + + // Temporarily unfreeze so that label/infos update correctly. + const bool was_frozen = c.element->isFreezeLabel(); + if (was_frozen) c.element->freezeLabel(false); + + c.element->rSequenceStruct() = seq; + c.element->setElementInformations(infos); + c.element->freezeLabel(frozen); + } +} diff --git a/sources/autoNum/renumberelementscommand.h b/sources/autoNum/renumberelementscommand.h new file mode 100644 index 000000000..4c3d1c871 --- /dev/null +++ b/sources/autoNum/renumberelementscommand.h @@ -0,0 +1,70 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ + +#ifndef RENUMBERELEMENTSCOMMAND_H +#define RENUMBERELEMENTSCOMMAND_H + +#include +#include +#include + +#include "../diagramcontext.h" +#include "assignvariables.h" // defines autonum::sequentialNumbers + +class Element; +class QETProject; + +/** + * @brief Undoable renumbering of element labels and sequence structs. + */ +class RenumberElementsCommand final : public QUndoCommand +{ +public: + struct ElementChange + { + Element *element = nullptr; + DiagramContext old_infos; + DiagramContext new_infos; + autonum::sequentialNumbers old_seq; + autonum::sequentialNumbers new_seq; + bool old_frozen = false; + bool new_frozen = false; + }; + + RenumberElementsCommand( + QETProject *project, + QVector changes, + QHash old_ctx, + QHash new_ctx, + const QString &text); + + void undo() override; + void redo() override; + +private: + void apply(bool use_new); + +private: + QETProject *project_ = nullptr; + QVector changes_; + QHash old_ctx_; + QHash new_ctx_; + bool first_redo_ = true; +}; + +#endif // RENUMBERELEMENTSCOMMAND_H diff --git a/sources/autoNum/ui/autonumberingmanagementw.cpp b/sources/autoNum/ui/autonumberingmanagementw.cpp index ba3e7f826..4e6499548 100644 --- a/sources/autoNum/ui/autonumberingmanagementw.cpp +++ b/sources/autoNum/ui/autonumberingmanagementw.cpp @@ -23,6 +23,7 @@ #include "formulaautonumberingw.h" #include "numparteditorw.h" #include "qdebug.h" +#include "renumberelementsdialog.h" #include "ui_autonumberingmanagementw.h" #include "ui_formulaautonumberingw.h" @@ -48,6 +49,8 @@ AutoNumberingManagementW::AutoNumberingManagementW(QETProject *project, ui->m_selected_folios_le->setDisabled(true); ui->m_selected_folios_le->setReadOnly(true); ui->m_apply_project_rb->setChecked(true); + // Enabled only when project is in "Under Development" status and not read-only. + ui->m_renumber_elements_pb->setEnabled(ui->m_status_cb->currentIndex() == 0 && project_ && !project_->isReadOnly()); setProjectContext(); } @@ -87,6 +90,7 @@ void AutoNumberingManagementW::on_m_status_cb_currentIndexChanged(int index) ui->m_both_conductor_rb->setChecked(true); ui->m_both_element_rb->setChecked(true); ui->m_both_folio_rb->setChecked(true); + ui->m_renumber_elements_pb->setEnabled(true); } //Installing else if (index == 1) { @@ -96,15 +100,31 @@ void AutoNumberingManagementW::on_m_status_cb_currentIndexChanged(int index) ui->m_new_conductor_rb->setChecked(true); ui->m_new_element_rb->setChecked(true); ui->m_new_folio_rb->setChecked(true); + ui->m_renumber_elements_pb->setEnabled(true); } //Built else if (index == 2) { ui->m_disable_conductor_rb->setChecked(true); ui->m_disable_element_rb->setChecked(true); ui->m_disable_folio_rb->setChecked(true); + ui->m_renumber_elements_pb->setEnabled(false); } } +void AutoNumberingManagementW::on_m_renumber_elements_pb_clicked() +{ + if (!project_ || project_->isReadOnly()) return; + // Only allowed during "Under Development" + // if (ui->m_status_cb->currentIndex() != 1) return; + + QStringList titles = project_->elementAutoNum().keys(); + titles.sort(Qt::CaseInsensitive); + RenumberElementsDialog dlg(titles, this); + if (dlg.exec() != QDialog::Accepted) return; + + project_->renumberElementsBySchemeTitle(dlg.selectedSchemeTitle()); +} + /** @brief AutoNumberingManagementW::on_m_apply_folios_rb_clicked Set From Folios Combobox diff --git a/sources/autoNum/ui/autonumberingmanagementw.h b/sources/autoNum/ui/autonumberingmanagementw.h index 5ecb6841a..c5893544e 100644 --- a/sources/autoNum/ui/autonumberingmanagementw.h +++ b/sources/autoNum/ui/autonumberingmanagementw.h @@ -50,6 +50,7 @@ class AutoNumberingManagementW : public QWidget void on_m_from_folios_cb_currentIndexChanged(int); void on_m_to_folios_cb_currentIndexChanged(int); void on_m_status_cb_currentIndexChanged(int); + void on_m_renumber_elements_pb_clicked(); void on_m_apply_folios_rb_clicked(); void on_m_apply_project_rb_clicked(); void on_buttonBox_clicked(QAbstractButton *); diff --git a/sources/autoNum/ui/autonumberingmanagementw.ui b/sources/autoNum/ui/autonumberingmanagementw.ui index be70d22b6..7099bcef7 100644 --- a/sources/autoNum/ui/autonumberingmanagementw.ui +++ b/sources/autoNum/ui/autonumberingmanagementw.ui @@ -308,6 +308,13 @@ + + + + Renumber element(s)… + + + diff --git a/sources/autoNum/ui/renumberelementsdialog.cpp b/sources/autoNum/ui/renumberelementsdialog.cpp new file mode 100644 index 000000000..a8c4c9dd1 --- /dev/null +++ b/sources/autoNum/ui/renumberelementsdialog.cpp @@ -0,0 +1,75 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ + +#include "renumberelementsdialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +RenumberElementsDialog::RenumberElementsDialog(const QStringList &scheme_titles, QWidget *parent) + : QDialog(parent) + , m_scheme_titles(scheme_titles) +{ + setWindowTitle(tr("Renumber element(s)")); + setModal(true); + + auto *vl = new QVBoxLayout(this); + + auto *scope = new QGroupBox(tr("Scope"), this); + auto *scope_l = new QVBoxLayout(scope); + m_all_rb = new QRadioButton(tr("All schemes"), scope); + m_one_rb = new QRadioButton(tr("One scheme"), scope); + scope_l->addWidget(m_all_rb); + scope_l->addWidget(m_one_rb); + vl->addWidget(scope); + + auto *form = new QFormLayout(); + m_combo = new QComboBox(this); + m_combo->addItems(m_scheme_titles); + form->addRow(new QLabel(tr("Scheme:"), this), m_combo); + vl->addLayout(form); + + m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + connect(m_buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + vl->addWidget(m_buttons); + + m_all_rb->setChecked(true); + updateUi(); + + connect(m_all_rb, &QRadioButton::toggled, this, &RenumberElementsDialog::updateUi); + connect(m_one_rb, &QRadioButton::toggled, this, &RenumberElementsDialog::updateUi); +} + +QString RenumberElementsDialog::selectedSchemeTitle() const +{ + if (m_all_rb && m_all_rb->isChecked()) return QString(); + return m_combo ? m_combo->currentText() : QString(); +} + +void RenumberElementsDialog::updateUi() +{ + const bool one = m_one_rb && m_one_rb->isChecked(); + if (m_combo) m_combo->setEnabled(one); +} diff --git a/sources/autoNum/ui/renumberelementsdialog.h b/sources/autoNum/ui/renumberelementsdialog.h new file mode 100644 index 000000000..8a49fac93 --- /dev/null +++ b/sources/autoNum/ui/renumberelementsdialog.h @@ -0,0 +1,59 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ + +#ifndef RENUMBERELEMENTSDIALOG_H +#define RENUMBERELEMENTSDIALOG_H + +#include +#include + +class QComboBox; +class QRadioButton; +class QDialogButtonBox; + +/** + * @brief Simple dialog to pick renumbering scope for elements. + * + * The user can choose between: + * - all element autonumbering schemes + * - one selected scheme title + */ +class RenumberElementsDialog final : public QDialog +{ + Q_OBJECT + +public: + explicit RenumberElementsDialog(const QStringList &scheme_titles, QWidget *parent = nullptr); + + /** + * @return Empty string if "all" is selected, otherwise the chosen scheme title. + */ + QString selectedSchemeTitle() const; + +private slots: + void updateUi(); + +private: + QStringList m_scheme_titles; + QRadioButton *m_all_rb = nullptr; + QRadioButton *m_one_rb = nullptr; + QComboBox *m_combo = nullptr; + QDialogButtonBox *m_buttons = nullptr; +}; + +#endif // RENUMBERELEMENTSDIALOG_H diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp index d296ebf74..66f1c1b5b 100644 --- a/sources/qetproject.cpp +++ b/sources/qetproject.cpp @@ -22,7 +22,9 @@ #include "autoNum/assignvariables.h" #include "autoNum/numerotationcontext.h" #include "autoNum/numerotationcontextcommands.h" +#include "autoNum/renumberelementscommand.h" #include "diagram.h" +#include "qetgraphicsitem/element.h" #include "qetapp.h" #include "qetmessagebox.h" #include "qetresult.h" @@ -40,7 +42,30 @@ #include #include #include + +namespace { + +/** + * @brief Reset numeric fields of a NumerotationContext so renumbering starts at 1. + * Keeps non-numeric parts (string/idfolio/folio/plant/locmach/elementline/elementcolumn/elementprefix) unchanged. + */ +NumerotationContext resetContextForRenumber(const NumerotationContext &tmpl) +{ + NumerotationContext out = tmpl; + for (int i = 0; i < out.size(); ++i) { + const QStringList parts = out.itemAt(i); + if (parts.isEmpty()) continue; + const QString type = parts.at(0); + if (out.keyIsNumber(type)) { + out.replaceValue(i, QStringLiteral("1")); + } + } + return out; +} + +} // namespace #include +#include #include static int BACKUP_INTERVAL = 1200000; //interval in ms of backup = 20min @@ -682,6 +707,109 @@ void QETProject::setCurrrentElementAutonum(QString autoNum) { m_current_element_autonum = std::move(autoNum); } +/** + @brief QETProject::renumberElementsBySchemeTitle + Renumber existing elements by element autonumbering scheme title. + + Elements do not store the scheme title; they store a "formula" (elementInformations["formula"]). + This method matches elements to schemes by comparing the stored formula with the formula derived + from each scheme's NumerotationContext. + + If scheme_title is empty, all schemes are renumbered. Otherwise only that scheme is renumbered. + The operation is undoable. +*/ +void QETProject::renumberElementsBySchemeTitle(const QString &scheme_title) +{ + if (!m_undo_stack) return; + if (isReadOnly()) return; + + // Build map: scheme title -> canonical formula + QHash scheme_formula; + for (const QString &k : m_element_autonum.keys()) { + if (!scheme_title.isEmpty() && k != scheme_title) continue; + scheme_formula.insert(k, autonum::numerotationContextToFormula(m_element_autonum.value(k))); + } + if (scheme_formula.isEmpty()) return; + + // Collect elements per scheme by formula match + QHash> by_key; + for (Diagram *d : diagrams()) { + if (!d) continue; + const auto items = d->items(); + for (QGraphicsItem *it : items) { + auto *el = qgraphicsitem_cast(it); + if (!el) continue; + if (el->linkType() == Element::Slave || (el->linkType() & Element::AllReport)) + continue; + + const QString el_formula = el->elementInformations().value(QStringLiteral("formula")).toString(); + if (el_formula.isEmpty()) continue; + + QString matched_key; + for (auto itf = scheme_formula.constBegin(); itf != scheme_formula.constEnd(); ++itf) { + if (itf.value() == el_formula) { matched_key = itf.key(); break; } + } + if (matched_key.isEmpty()) continue; + by_key[matched_key].append(el); + } + } + if (by_key.isEmpty()) return; + + QVector changes; + QHash old_ctx; + QHash new_ctx; + + for (auto it = by_key.constBegin(); it != by_key.constEnd(); ++it) { + old_ctx.insert(it.key(), m_element_autonum.value(it.key())); + } + + for (auto it = by_key.begin(); it != by_key.end(); ++it) { + const QString key = it.key(); + auto &elements = it.value(); + std::sort(elements.begin(), elements.end(), [](Element *a, Element *b){ return comparPos(a, b); }); + + NumerotationContext base_tmpl = m_element_autonum.value(key); + NumerotationContext nc = resetContextForRenumber(base_tmpl); + NumerotationContextCommands ncc(nc); + + for (Element *el : elements) { + RenumberElementsCommand::ElementChange ch; + ch.element = el; + ch.old_infos = el->elementInformations(); + ch.old_seq = el->sequenceStruct(); + ch.old_frozen = el->isFreezeLabel(); + ch.new_frozen = ch.old_frozen; // preserve frozen state + + const QString formula = ch.old_infos.value(QStringLiteral("formula")).toString(); + autonum::sequentialNumbers new_seq; + new_seq.clear(); + autonum::setSequential(formula, new_seq, nc, el->diagram(), key); + + DiagramContext new_infos = ch.old_infos; + new_infos.addValue(QStringLiteral("label"), autonum::AssignVariables::formulaToLabel(formula, new_seq, el->diagram(), el, nullptr)); + ch.new_infos = new_infos; + ch.new_seq = new_seq; + changes.append(ch); + + // advance + nc = ncc.next(); + ncc = NumerotationContextCommands(nc); + } + + new_ctx.insert(key, nc); + } + + if (changes.isEmpty()) return; + + auto *cmd = new RenumberElementsCommand( + this, + changes, + old_ctx, + new_ctx, + scheme_title.isEmpty() ? tr("Renumber elements") : tr("Renumber elements (%1)").arg(scheme_title)); + m_undo_stack->push(cmd); +} + /** @brief QETProject::conductorAutoNumFormula @param key : autonum title diff --git a/sources/qetproject.h b/sources/qetproject.h index 3817abad9..2c968f9d5 100644 --- a/sources/qetproject.h +++ b/sources/qetproject.h @@ -169,6 +169,20 @@ class QETProject : public QObject QString elementCurrentAutoNum() const; void setCurrrentElementAutonum(QString autoNum); + /** + * @brief Renumber existing elements by element autonumbering scheme. + * + * Elements do not store the scheme title but they store the corresponding formula. + * This operation matches elements to schemes by comparing the stored formula + * with the formula derived from the scheme's NumerotationContext. + * + * If @p scheme_title is empty, all schemes are renumbered. + * If @p scheme_title is non-empty, only that scheme is renumbered. + * + * The operation is undoable. + */ + void renumberElementsBySchemeTitle(const QString &scheme_title = QString()); + //Element void freezeExistentElementLabel(bool freeze, int from, int to); void freezeNewElementLabel(bool freeze, int from, int to);