From e22f94175532219bc0d16f62422e8bfe0e1d1f40 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Thu, 29 Feb 2024 15:28:38 +0100
Subject: [PATCH] ensure that there are no duplicate names in lists

---
 GUI/Model/Type/NamedItem.cpp  | 60 +++++++++++++++++++++++++++++++++++
 GUI/Model/Type/NamedItem.h    |  7 +++-
 GUI/Model/Type/SetWithModel.h |  5 ++-
 3 files changed, 70 insertions(+), 2 deletions(-)
 create mode 100644 GUI/Model/Type/NamedItem.cpp

diff --git a/GUI/Model/Type/NamedItem.cpp b/GUI/Model/Type/NamedItem.cpp
new file mode 100644
index 00000000000..3a61656596a
--- /dev/null
+++ b/GUI/Model/Type/NamedItem.cpp
@@ -0,0 +1,60 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      GUI/Model/Type/NamedItem.cpp
+//! @brief     Implements class NamedItem.
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2021
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#include "GUI/Model/Type/NamedItem.h"
+#include <QRegularExpression>
+#include <QRegularExpressionMatch>
+
+namespace {
+
+QStringList splitName(const QString& s) {
+    QRegularExpression pattern("(.*)_(\\d+)");
+    QRegularExpressionMatch match = pattern.match(s);
+    if (match.hasMatch()) {
+        QStringList groups;
+        for (int i = 1; i < match.lastCapturedIndex() + 1; ++i)
+            groups << match.captured(i);
+        return groups;
+    }
+    return {}; // s does not contain '_\d+'
+}
+
+} // namespace
+
+
+void NamedItem::renumber(const QStringList& extant_names)
+{
+    // Item name consists of a stem and an optional number
+    QStringList ns = splitName(name());
+    QString stem = ns.isEmpty() ? name() : ns[0];
+
+    // Determine highest number for given stem in extant_items
+    int imax = 0;
+    for (QString tname : extant_names) {
+	QStringList ts = splitName(tname);
+	if (ts.isEmpty()) {
+	    if (tname == stem)
+		imax = std::max(imax, 1);
+	} else {
+	    if (ts[0] == stem)
+		imax = std::max(imax, ts[1].toInt());
+	}
+    }
+
+    if (imax != 0)
+	setName(stem + "_" + QString::number(imax + 1));
+
+    if (name().contains(' '))
+	setName(name().replace(' ', '_'));
+}
diff --git a/GUI/Model/Type/NamedItem.h b/GUI/Model/Type/NamedItem.h
index ef088ba376c..f60bf46783e 100644
--- a/GUI/Model/Type/NamedItem.h
+++ b/GUI/Model/Type/NamedItem.h
@@ -3,7 +3,7 @@
 //  BornAgain: simulate and fit reflection and scattering
 //
 //! @file      GUI/Model/Type/NamedItem.h
-//! @brief     Defines and implements class NamedItem.
+//! @brief     Defines class NamedItem.
 //!
 //! @homepage  http://www.bornagainproject.org
 //! @license   GNU General Public License v3 or higher (see COPYING)
@@ -16,6 +16,7 @@
 #define BORNAGAIN_GUI_MODEL_TYPE_NAMEDITEM_H
 
 #include <QString>
+#include <vector>
 
 //! Base class of items that have a name and a description.
 
@@ -33,6 +34,10 @@ public:
     QString description() const { return m_description; }
     void setDescription(const QString& description) { m_description = description; }
 
+    //! Changes name of item to avoid duplication of any name from extant items.
+    //! Also replaces blank by underscore characters.
+    void renumber(const QStringList& extant_names);
+
 private:
     QString m_name;
     QString m_description;
diff --git a/GUI/Model/Type/SetWithModel.h b/GUI/Model/Type/SetWithModel.h
index 0c57c403f29..b2605d0ac0f 100644
--- a/GUI/Model/Type/SetWithModel.h
+++ b/GUI/Model/Type/SetWithModel.h
@@ -74,6 +74,7 @@ public:
     void add_item(T* t)
     {
         m_qmodel->beginInsertRows({}, size(), size());
+	t->renumber(itemNames());
         m_vec.push_back(t);
         m_idx = m_vec.size() - 1;
         m_qmodel->endInsertRows();
@@ -82,8 +83,10 @@ public:
     void add_items(std::vector<T*> v)
     {
         m_qmodel->beginInsertRows({}, size(), size());
-        for (T* t : v)
+        for (T* t : v) {
+	    t->renumber(itemNames());
             m_vec.push_back(t);
+	}
         m_idx = m_vec.size() - 1;
         m_qmodel->endInsertRows();
         emit AbstractSetModel::setChanged();
-- 
GitLab