diff --git a/sources/SearchAndReplace/searchandreplaceworker.cpp b/sources/SearchAndReplace/searchandreplaceworker.cpp index 9f74248d7..28c053e4a 100644 --- a/sources/SearchAndReplace/searchandreplaceworker.cpp +++ b/sources/SearchAndReplace/searchandreplaceworker.cpp @@ -76,7 +76,20 @@ void SearchAndReplaceWorker::replaceDiagram(QList diagram_list) } } - new_properties.context.add(m_titleblock_properties.context); + // The replace dialog lists every custom variable of the folios + // (#1113), so an empty value means "leave unchanged", exactly like + // the fields of the main tab. + const DiagramContext &change = m_titleblock_properties.context; + for (const QString &key : change.keys()) { + const QString value = change.value(key).toString(); + if (value.isEmpty()) { + continue; + } + new_properties.context.addValue( + key, + applyChange(new_properties.context.value(key).toString(), + value)); + } if (old_propertie != new_properties) { project->undoStack()->push(new ChangeTitleBlockCommand(d, old_propertie, new_properties)); diff --git a/sources/SearchAndReplace/ui/searchandreplacewidget.cpp b/sources/SearchAndReplace/ui/searchandreplacewidget.cpp index af229b0c5..998e1b2ad 100644 --- a/sources/SearchAndReplace/ui/searchandreplacewidget.cpp +++ b/sources/SearchAndReplace/ui/searchandreplacewidget.cpp @@ -30,6 +30,7 @@ #include "../../qeticons.h" #include "../../qetinformation.h" #include "../../qetproject.h" +#include "../../ui/titleblockpropertieswidget.h" #include "replaceadvanceddialog.h" #include "replaceconductordialog.h" #include "replaceelementdialog.h" @@ -1109,7 +1110,26 @@ void SearchAndReplaceWidget::on_m_previous_pb_clicked() void SearchAndReplaceWidget::on_m_folio_pb_clicked() { ReplaceFolioDialog *dialog = new ReplaceFolioDialog(this); - dialog->setTitleBlockProperties(m_worker.m_titleblock_properties); + + // List the custom variables the folios already use, so the user only + // has to type a value instead of guessing each name (#1113). + TitleBlockProperties properties = m_worker.m_titleblock_properties; + for (const QPointer &diagram : std::as_const(m_diagram_hash)) + { + if (!diagram) { + continue; + } + for (const QString &key : + diagram->border_and_titleblock.exportTitleBlock().context.keys()) { + if (!properties.context.contains(key)) { + properties.context.addValue(key, QString()); + } + } + TitleBlockPropertiesWidget::addTemplateVariables( + properties.context, + diagram->border_and_titleblock.titleBlockTemplate()); + } + dialog->setTitleBlockProperties(properties); int result = dialog->exec(); if (result == QDialogButtonBox::AcceptRole) diff --git a/sources/titleblocktemplate.cpp b/sources/titleblocktemplate.cpp index 79d6b0adf..69214702e 100644 --- a/sources/titleblocktemplate.cpp +++ b/sources/titleblocktemplate.cpp @@ -27,6 +27,7 @@ #include #include +#include /** @brief TitleBlockTemplate::TitleBlockTemplate Constructor @@ -1818,14 +1819,46 @@ QString TitleBlockTemplate::interpreteVariables( const QString &string, const DiagramContext &diagram_context) const { + // A variable nobody has given a value to yet -- e.g. one just added to + // the template, which is not in the folio's Custom tab until its + // properties are opened (#1113) -- must render blank rather than as its + // own name, the same as an auto-added but unset one already does (#973). + // Collect those from the template text before substituting, so a value + // that happens to contain "%something" is never touched. + // A bare "%name" is unset only if no key is a prefix of it, because the + // substitution below replaces "%key" wherever it appears. + static const QRegularExpression rx( + QStringLiteral("%\\{([a-z0-9_-]+)\\}|%([a-z0-9_-]+)")); + const QStringList keys = + diagram_context.keys(DiagramContext::DecreasingLength); + QStringList unset; + auto it = rx.globalMatch(string); + while (it.hasNext()) { + const QRegularExpressionMatch m = it.next(); + const QString name = m.captured(1).isEmpty() + ? m.captured(2) : m.captured(1); + bool known = diagram_context.contains(name); + if (!known && m.captured(1).isEmpty()) { + for (const QString &key : keys) { + if (name.startsWith(key)) { known = true; break; } + } + } + if (!known) unset << m.captured(0); + } + QString interpreted_string = string; - foreach (QString key, - diagram_context.keys(DiagramContext::DecreasingLength)) { + foreach (QString key, keys) { interpreted_string.replace("%{" % key % "}", diagram_context[key].toString()); interpreted_string.replace("%" % key, diagram_context[key].toString()); } + std::sort(unset.begin(), unset.end(), + [](const QString &a, const QString &b) { + return a.length() > b.length(); + }); + for (const QString &placeholder : unset) + interpreted_string.remove(placeholder); return(interpreted_string); } @@ -1834,7 +1867,7 @@ QString TitleBlockTemplate::interpreteVariables( Get list of variables @return The list of string with variables */ -QStringList TitleBlockTemplate::listOfVariables() +QStringList TitleBlockTemplate::listOfVariables() const { QStringList list; // Match both the braced "%{name}" form and the bare "%name" form diff --git a/sources/titleblocktemplate.h b/sources/titleblocktemplate.h index a7f6c9686..8bc1821d6 100644 --- a/sources/titleblocktemplate.h +++ b/sources/titleblocktemplate.h @@ -188,7 +188,7 @@ class TitleBlockTemplate : public QObject { void applyCellSpan(TitleBlockCell *); void applyRowColNums(); void rowColsChanged(); - QStringList listOfVariables(); + QStringList listOfVariables() const; protected: void loadInformation(const QDomElement &); diff --git a/sources/ui/titleblockpropertieswidget.cpp b/sources/ui/titleblockpropertieswidget.cpp index b3ff972cc..929b557c2 100644 --- a/sources/ui/titleblockpropertieswidget.cpp +++ b/sources/ui/titleblockpropertieswidget.cpp @@ -483,13 +483,23 @@ TitleBlockTemplate *TitleBlockPropertiesWidget::templateForIndex(int index) cons Add to @p context every CUSTOM variable used by the currently selected template that is not already present, with an empty value — so the user only has to fill in the values instead of declaring the variables (#271). - The standard fields (title, author, date, …) are handled by their own - widgets and are skipped. Existing values in @p context are preserved. */ void TitleBlockPropertiesWidget::addTemplateVariables( DiagramContext &context, int index) const { - TitleBlockTemplate *tpl = templateForIndex(index); + addTemplateVariables(context, templateForIndex(index)); +} + +/** + @brief TitleBlockPropertiesWidget::addTemplateVariables + Add to @p context every CUSTOM variable used by @p tpl that is not already + present, with an empty value. + The standard fields (title, author, date, …) are handled by their own + widgets and are skipped. Existing values in @p context are preserved. +*/ +void TitleBlockPropertiesWidget::addTemplateVariables( + DiagramContext &context, const TitleBlockTemplate *tpl) +{ if (!tpl) return; // Variables rendered from the dedicated standard-field widgets; they must diff --git a/sources/ui/titleblockpropertieswidget.h b/sources/ui/titleblockpropertieswidget.h index 00e8c1758..0622da8a5 100644 --- a/sources/ui/titleblockpropertieswidget.h +++ b/sources/ui/titleblockpropertieswidget.h @@ -71,6 +71,10 @@ class TitleBlockPropertiesWidget : public QWidget void setTitleBlockTemplatesVisible(const bool &visible); void setReadOnly (const bool &ro); + static void addTemplateVariables( + DiagramContext &context, + const TitleBlockTemplate *tpl); + private: void addCollection ( TitleBlockTemplatesCollection *tbt_collection);