diff --git a/sources/autoNum/numerotationcontext.cpp b/sources/autoNum/numerotationcontext.cpp index 3355a1bb4..e23578186 100644 --- a/sources/autoNum/numerotationcontext.cpp +++ b/sources/autoNum/numerotationcontext.cpp @@ -275,3 +275,80 @@ QString NumerotationContext::formatValue(const QStringList &item) return QString("%1").arg(value.toInt(), 3, 10, QChar('0')); return QString::number(value.toInt()); } + +/** + @brief NumerotationContext::saveToSettings + Save a hash of named NumerotationContexts to QSettings. + @param contexts : the named rules to save + @param currentRule : the name of the currently active rule + @param settings : QSettings instance + @param prefix : settings key prefix (e.g. "autonum/conductor") +*/ +void NumerotationContext::saveToSettings( + const QHash &contexts, + const QString ¤tRule, + QSettings &settings, + const QString &prefix) +{ + settings.setValue(prefix + "/current", currentRule); + + // Clear stale array entries before writing (beginWriteArray does not + // remove entries beyond the new size). + settings.remove(prefix + "/rules"); + + QStringList names = contexts.keys(); + settings.beginWriteArray(prefix + "/rules", names.size()); + for (int i = 0; i < names.size(); ++i) { + settings.setArrayIndex(i); + const QString &name = names.at(i); + const NumerotationContext &nc = contexts.value(name); + + settings.setValue("name", name); + + // Serialize context to XML string + QDomDocument doc; + NumerotationContext nc_copy = nc; + QDomElement root = nc_copy.toXml(doc, "context"); + doc.appendChild(root); + settings.setValue("xml", doc.toString()); + } + settings.endArray(); +} + +/** + @brief NumerotationContext::loadFromSettings + Load named NumerotationContexts from QSettings. + @param settings : QSettings instance + @param prefix : settings key prefix (e.g. "autonum/conductor") + @return pair of (hash of named rules, name of current rule) +*/ +QPair, QString> NumerotationContext::loadFromSettings( + QSettings &settings, + const QString &prefix) +{ + QPair, QString> result; + QHash &contexts = result.first; + QString ¤tRule = result.second; + + currentRule = settings.value(prefix + "/current").toString(); + + int size = settings.beginReadArray(prefix + "/rules"); + for (int i = 0; i < size; ++i) { + settings.setArrayIndex(i); + QString name = settings.value("name").toString(); + QString xmlStr = settings.value("xml").toString(); + + if (name.isEmpty() || xmlStr.isEmpty()) continue; + + QDomDocument doc; + if (!doc.setContent(xmlStr)) continue; + + QDomElement root = doc.documentElement(); + NumerotationContext nc; + nc.fromXml(root); + contexts.insert(name, nc); + } + settings.endArray(); + + return result; +} diff --git a/sources/autoNum/numerotationcontext.h b/sources/autoNum/numerotationcontext.h index b1575e468..74b6a5fb4 100644 --- a/sources/autoNum/numerotationcontext.h +++ b/sources/autoNum/numerotationcontext.h @@ -21,6 +21,8 @@ #include #include #include +#include +#include /** This class represents a numerotation context, i.e. the data (type, value, increase) @@ -60,6 +62,14 @@ class NumerotationContext /// UI preview of a part's value matches what actually gets rendered. static QString formatValue(const QStringList &item); + static void saveToSettings(const QHash &contexts, + const QString ¤tRule, + QSettings &settings, + const QString &prefix); + static QPair, QString> loadFromSettings( + QSettings &settings, + const QString &prefix); + private: QStringList content_; }; diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp index 18afbdde6..1befef7a3 100644 --- a/sources/qetproject.cpp +++ b/sources/qetproject.cpp @@ -83,6 +83,30 @@ m_project_properties_handler{this} m_default_guides.append(g); } settings.endArray(); + + //Load global auto-numbering defaults from QSettings + { + auto conductorData = NumerotationContext::loadFromSettings(settings, QStringLiteral("autonum/conductor")); + for (auto it = conductorData.first.constBegin(); it != conductorData.first.constEnd(); ++it) { + addConductorAutoNum(it.key(), it.value()); + } + if (!conductorData.second.isEmpty()) { + setCurrentConductorAutoNum(conductorData.second); + } + + auto elementData = NumerotationContext::loadFromSettings(settings, QStringLiteral("autonum/element")); + for (auto it = elementData.first.constBegin(); it != elementData.first.constEnd(); ++it) { + addElementAutoNum(it.key(), it.value()); + } + if (!elementData.second.isEmpty()) { + setCurrrentElementAutonum(elementData.second); + } + + auto folioData = NumerotationContext::loadFromSettings(settings, QStringLiteral("autonum/folio")); + for (auto it = folioData.first.constBegin(); it != folioData.first.constEnd(); ++it) { + addFolioAutoNum(it.key(), it.value()); + } + } } ProjectPropertiesHandler &QETProject::projectPropertiesHandler() diff --git a/sources/ui/configpage/configpages.cpp b/sources/ui/configpage/configpages.cpp index 8c027ccac..7c2db969d 100644 --- a/sources/ui/configpage/configpages.cpp +++ b/sources/ui/configpage/configpages.cpp @@ -31,6 +31,8 @@ #include "../titleblockpropertieswidget.h" #include "../xrefpropertieswidget.h" #include "guidespropertieswidget.h" +#include "../autoNum/numerotationcontext.h" +#include "../autoNum/ui/selectautonumw.h" #include #include #include @@ -101,6 +103,32 @@ NewDiagramPage::NewDiagramPage(QETProject *project, } m_gpw->setGuides(loaded_guides); + // global auto-numbering defaults (only when editing global settings, not a project) + if (!m_project) { + auto saw_conductor = new SelectAutonumW(1); + auto saw_element = new SelectAutonumW(0); + auto saw_folio = new SelectAutonumW(2); + + initAutoNumTab(m_autonum_conductor, saw_conductor, QStringLiteral("autonum/conductor")); + initAutoNumTab(m_autonum_element, saw_element, QStringLiteral("autonum/element")); + initAutoNumTab(m_autonum_folio, saw_folio, QStringLiteral("autonum/folio")); + + QSettings autonum_settings; + loadAutoNumTab(m_autonum_conductor, autonum_settings); + loadAutoNumTab(m_autonum_element, autonum_settings); + loadAutoNumTab(m_autonum_folio, autonum_settings); + + // Intercept Return key in the combo line edits so it doesn't + // activate the dialog's default button (OK). + for (auto *tab : {&m_autonum_conductor, &m_autonum_element, &m_autonum_folio}) { + if (QComboBox *combo = tab->widget->contextComboBox()) { + if (combo->lineEdit()) { + combo->lineEdit()->installEventFilter(this); + } + } + } + } + //If there is a project, we edit his properties if (m_project) { bpw -> setProperties (m_project -> defaultBorderProperties()); @@ -114,6 +142,7 @@ NewDiagramPage::NewDiagramPage(QETProject *project, // main tab widget QTabWidget *tab_widget = new QTabWidget(this); + m_tab_widget = tab_widget; QWidget *diagram_widget = new QWidget(); QVBoxLayout *diagram_layout = new QVBoxLayout(diagram_widget); @@ -127,6 +156,19 @@ NewDiagramPage::NewDiagramPage(QETProject *project, tab_widget -> addTab (xrefpw, tr("Références croisées")); tab_widget -> addTab (m_gpw, tr("Guides")); + // add auto-numbering tab only for global settings (not per project) + if (!m_project) { + QWidget *autonum_widget = new QWidget(); + QVBoxLayout *autonum_layout = new QVBoxLayout(autonum_widget); + autonum_layout->addWidget(new QLabel(tr("Définir les règles de numérotation automatique par défaut pour les nouveaux projets :"))); + QTabWidget *autonum_inner_tab = new QTabWidget(); + autonum_inner_tab->addTab(m_autonum_conductor.widget, tr("Conducteurs")); + autonum_inner_tab->addTab(m_autonum_element.widget, tr("Eléments")); + autonum_inner_tab->addTab(m_autonum_folio.widget, tr("Folios")); + autonum_layout->addWidget(autonum_inner_tab); + tab_widget -> addTab (autonum_widget, tr("Numérotation auto")); + } + QVBoxLayout *vlayout1 = new QVBoxLayout(); vlayout1->addWidget(tab_widget); @@ -230,6 +272,9 @@ void NewDiagramPage::applyConf() settings.setValue(QStringLiteral("color"), current_guides[i].color.name()); } settings.endArray(); + + // save global auto-numbering defaults + persistAutonumSettings(); } } @@ -295,6 +340,134 @@ void NewDiagramPage::loadSavedTbp() applyConf(); } +/** + @brief NewDiagramPage::isPlaceholder + Return true if @a name matches the combo box's built-in placeholder text + (first item). This is locale-independent because it reads the actual item text. +*/ +bool NewDiagramPage::isPlaceholder(QComboBox *combo, const QString &name) +{ + return !combo->count() || name == combo->itemText(0); +} + +/** + @brief NewDiagramPage::initAutoNumTab + Initialise an AutoNumTab struct and connect its signals. +*/ +void NewDiagramPage::initAutoNumTab(AutoNumTab &tab, SelectAutonumW *w, const QString &prefix) +{ + tab.widget = w; + tab.prefix = prefix; + + connect(w, &SelectAutonumW::applyPressed, this, [this, &tab]() { saveAutoNumContext(tab); }); + connect(w, &SelectAutonumW::removeClicked, this, [this, &tab]() { removeAutoNumContext(tab); }); + connect(w->contextComboBox(), &QComboBox::activated, this, [this, &tab](int index) { + if (index >= 0) { + QString name = tab.widget->contextComboBox()->itemText(index); + if (tab.contexts.contains(name)) { + tab.widget->setContext(tab.contexts.value(name)); + } + } + }); +} + +/** + @brief NewDiagramPage::loadAutoNumTab + Load saved rules from QSettings into an AutoNumTab. +*/ +void NewDiagramPage::loadAutoNumTab(AutoNumTab &tab, QSettings &settings) +{ + auto data = NumerotationContext::loadFromSettings(settings, tab.prefix); + tab.contexts = data.first; + for (auto it = tab.contexts.constBegin(); it != tab.contexts.constEnd(); ++it) { + tab.widget->contextComboBox()->addItem(it.key()); + } + if (!tab.contexts.isEmpty() && !data.second.isEmpty() + && tab.contexts.contains(data.second)) { + tab.widget->contextComboBox()->setCurrentText(data.second); + tab.widget->setContext(tab.contexts.value(data.second)); + } +} + +/** + @brief NewDiagramPage::saveAutoNumContext + Save the current context from an AutoNumTab's widget into its hash and + persist to QSettings immediately. +*/ +void NewDiagramPage::saveAutoNumContext(AutoNumTab &tab) +{ + QString name = tab.widget->contextComboBox()->currentText().trimmed(); + if (name.isEmpty() || isPlaceholder(tab.widget->contextComboBox(), name)) { + return; + } + tab.contexts.insert(name, tab.widget->toNumContext()); + if (tab.widget->contextComboBox()->findText(name) == -1) { + tab.widget->contextComboBox()->addItem(name); + } + persistAutonumSettings(); +} + +/** + @brief NewDiagramPage::removeAutoNumContext + Remove the current context from an AutoNumTab's hash and persist. +*/ +void NewDiagramPage::removeAutoNumContext(AutoNumTab &tab) +{ + QString name = tab.widget->contextComboBox()->currentText().trimmed(); + if (name.isEmpty() || isPlaceholder(tab.widget->contextComboBox(), name)) { + return; + } + int idx = tab.widget->contextComboBox()->findText(name); + if (idx == -1) return; + tab.contexts.remove(name); + tab.widget->contextComboBox()->removeItem(idx); + tab.widget->contextComboBox()->setCurrentText(QString()); + tab.widget->setContext(NumerotationContext()); + persistAutonumSettings(); +} + +/** + @brief NewDiagramPage::persistAutonumSettings + Save all autonum contexts to QSettings immediately. +*/ +void NewDiagramPage::persistAutonumSettings() +{ + QSettings settings; + for (auto *tab : {&m_autonum_conductor, &m_autonum_element, &m_autonum_folio}) { + QString current; + QComboBox *combo = tab->widget->contextComboBox(); + if (!isPlaceholder(combo, combo->currentText().trimmed()) + && tab->contexts.contains(combo->currentText().trimmed())) { + current = combo->currentText().trimmed(); + } + NumerotationContext::saveToSettings(tab->contexts, current, + settings, tab->prefix); + } +} + +/** + @brief NewDiagramPage::eventFilter + Intercept Return/Enter in combo box line edits so it doesn't close the + settings dialog. +*/ +bool NewDiagramPage::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + auto *ke = static_cast(event); + if ((ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter)) { + // Check if this is a line edit inside one of our autonum combos + for (auto *tab : {&m_autonum_conductor, &m_autonum_element, &m_autonum_folio}) { + if (QComboBox *combo = tab->widget->contextComboBox()) { + if (combo->lineEdit() && combo->lineEdit() == obj) { + return true; // eat the event + } + } + } + } + } + return ConfigPage::eventFilter(obj, event); +} + /** Constructeur @param parent QWidget parent diff --git a/sources/ui/configpage/configpages.h b/sources/ui/configpage/configpages.h index 0a4740f90..9ddb2a523 100644 --- a/sources/ui/configpage/configpages.h +++ b/sources/ui/configpage/configpages.h @@ -20,6 +20,7 @@ #include "configpage.h" #include "../projectpropertiesdialog.h" #include "../titleblockpropertieswidget.h" +#include "../autoNum/numerotationcontext.h" #include #include @@ -32,6 +33,7 @@ class XRefPropertiesWidget; class GuidesPropertiesWidget; class QETProject; class TitleBlockProperties; +class SelectAutonumW; /** @brief The NewDiagramPage class @@ -48,6 +50,7 @@ class NewDiagramPage : public ConfigPage { ~NewDiagramPage() override; private: NewDiagramPage(const NewDiagramPage &); + bool eventFilter(QObject *obj, QEvent *event) override; public slots: void changeToAutoFolioTab(); void setFolioAutonum(QString); @@ -72,7 +75,25 @@ public slots: XRefPropertiesWidget *xrefpw; ///< Widget to edit default xref properties GuidesPropertiesWidget *m_gpw; ///< Widget to edit guides TitleBlockProperties savedTbp; ///< Used to save current TBP and retrieve later + QTabWidget *m_tab_widget; ///< Main tab widget (stored for later access) + // auto-numbering tab data + struct AutoNumTab { + SelectAutonumW *widget = nullptr; + QHash contexts; + QString prefix; + }; + AutoNumTab m_autonum_conductor; + AutoNumTab m_autonum_element; + AutoNumTab m_autonum_folio; + + void initAutoNumTab(AutoNumTab &tab, SelectAutonumW *w, const QString &prefix); + void loadAutoNumTab(AutoNumTab &tab, QSettings &settings); + void saveAutoNumContext(AutoNumTab &tab); + void removeAutoNumContext(AutoNumTab &tab); + void persistAutonumSettings(); + + static bool isPlaceholder(QComboBox *combo, const QString &name); }; /**