From aafc92f68f2058a11b67a38a7867b33f008206c4 Mon Sep 17 00:00:00 2001
From: Gennady Pospelov <g.pospelov@fz-juelich.de>
Date: Fri, 10 Nov 2017 14:20:12 +0100
Subject: [PATCH] Identity proxy strategy was extracted to separate class.

---
 GUI/coregui/Models/ComponentProxyModel.cpp   |  32 ++----
 GUI/coregui/Models/ComponentProxyModel.h     |  10 +-
 GUI/coregui/Models/ProxyModelStrategy.cpp    |  58 +++++++++++
 GUI/coregui/Models/ProxyModelStrategy.h      |  56 ++++++++++
 Tests/UnitTests/GUI/TestGUI.cpp              |   4 +-
 Tests/UnitTests/GUI/TestProxyModelStrategy.h | 102 +++++++++++++++++++
 6 files changed, 233 insertions(+), 29 deletions(-)
 create mode 100644 GUI/coregui/Models/ProxyModelStrategy.cpp
 create mode 100644 GUI/coregui/Models/ProxyModelStrategy.h
 create mode 100644 Tests/UnitTests/GUI/TestProxyModelStrategy.h

diff --git a/GUI/coregui/Models/ComponentProxyModel.cpp b/GUI/coregui/Models/ComponentProxyModel.cpp
index 10689537afb..1bea981d90e 100644
--- a/GUI/coregui/Models/ComponentProxyModel.cpp
+++ b/GUI/coregui/Models/ComponentProxyModel.cpp
@@ -17,12 +17,14 @@
 #include "ComponentProxyModel.h"
 #include "SessionModel.h"
 #include "ModelUtils.h"
+#include "ProxyModelStrategy.h"
 #include <functional>
 #include <QDebug>
 
 ComponentProxyModel::ComponentProxyModel(QObject* parent)
     : QAbstractProxyModel(parent)
     , m_model(nullptr)
+    , m_proxyStrategy(new IndentityProxyStrategy)
 {
 }
 
@@ -70,7 +72,7 @@ QModelIndex ComponentProxyModel::mapToSource(const QModelIndex& proxyIndex) cons
     if (!proxyIndex.isValid())
         return QModelIndex();
 
-    return m_sourceToProxy.key(proxyIndex);
+    return m_proxyStrategy->sourceToProxy().key(proxyIndex);
 }
 
 QModelIndex ComponentProxyModel::mapFromSource(const QModelIndex& sourceIndex) const
@@ -78,7 +80,7 @@ QModelIndex ComponentProxyModel::mapFromSource(const QModelIndex& sourceIndex) c
     if (!sourceIndex.isValid())
         return QModelIndex();
 
-    return m_sourceToProxy.value(sourceIndex);
+    return m_proxyStrategy->sourceToProxy().value(sourceIndex);
 }
 
 QModelIndex ComponentProxyModel::index(int row, int column, const QModelIndex& parent) const
@@ -87,7 +89,7 @@ QModelIndex ComponentProxyModel::index(int row, int column, const QModelIndex& p
     if (parent.isValid())
         sourceParent = mapToSource(parent);
 
-    QMapIterator<QPersistentModelIndex, QPersistentModelIndex> it(m_proxySourceParent);
+    QMapIterator<QPersistentModelIndex, QPersistentModelIndex> it(m_proxyStrategy->proxySourceParent());
     while (it.hasNext()) {
         it.next();
         if (it.value() == sourceParent && it.key().row() == row &&
@@ -99,7 +101,7 @@ QModelIndex ComponentProxyModel::index(int row, int column, const QModelIndex& p
 
 QModelIndex ComponentProxyModel::parent(const QModelIndex& child) const
 {
-    QModelIndex sourceParent = m_proxySourceParent.value(child);
+    QModelIndex sourceParent = m_proxyStrategy->proxySourceParent().value(child);
     if (sourceParent.isValid())
         return mapFromSource(sourceParent);
 
@@ -111,7 +113,7 @@ int ComponentProxyModel::rowCount(const QModelIndex& parent) const
     QModelIndex sourceParent;
     if (parent.isValid())
         sourceParent = mapToSource(parent);
-    QMapIterator<QPersistentModelIndex, QPersistentModelIndex> it(m_proxySourceParent);
+    QMapIterator<QPersistentModelIndex, QPersistentModelIndex> it(m_proxyStrategy->proxySourceParent());
 
     QSet<int> rows;
     while (it.hasNext()) {
@@ -177,24 +179,6 @@ void ComponentProxyModel::sourceRowsInserted(const QModelIndex& parent, int star
 
 void ComponentProxyModel::buildModelMap()
 {
-    m_sourceToProxy.clear();
-    m_proxySourceParent.clear();
-
-    ModelUtils::iterate(QModelIndex(), m_model, [=](const QModelIndex& index){
-       SessionItem* item = m_model->itemForIndex(index);
-
-//       qDebug() << "XXX index" << index << "index.parent" << index.parent();
-       QPersistentModelIndex proxy = createIndex(index.row(), index.column(), item);
-       m_sourceToProxy.insert(QPersistentModelIndex(index), proxy);
-
-       QPersistentModelIndex sourceParent;
-       if (index.parent().isValid())
-           sourceParent = index.parent();
-
-//       qDebug() << "YYY proxy" << proxy << "sourceParent" << sourceParent;
-       m_proxySourceParent.insert(proxy, sourceParent);
-//       qDebug() << " ";
-    });
-
+    m_proxyStrategy->buildModelMap(m_model, this);
     layoutChanged();
 }
diff --git a/GUI/coregui/Models/ComponentProxyModel.h b/GUI/coregui/Models/ComponentProxyModel.h
index 403061e0cca..d8640ce3373 100644
--- a/GUI/coregui/Models/ComponentProxyModel.h
+++ b/GUI/coregui/Models/ComponentProxyModel.h
@@ -18,11 +18,14 @@
 #define COMPONENTPROXYMODEL_H
 
 #include "WinDllMacros.h"
+#include "ProxyModelStrategy.h"
 #include <QAbstractProxyModel>
 #include <QPersistentModelIndex>
 #include <QMap>
+#include <memory>
 
 class SessionModel;
+class ProxyModelStrategy;
 
 //! Proxy model to adjust SessionModel for component editor (right bottom corner of SampleView
 //! and similar).
@@ -33,6 +36,8 @@ class SessionModel;
 class BA_CORE_API_ ComponentProxyModel : public QAbstractProxyModel
 {
     Q_OBJECT
+
+    friend class ProxyModelStrategy;
 public:
     ComponentProxyModel(QObject* parent = nullptr);
 
@@ -60,11 +65,8 @@ private slots:
 private:
     void buildModelMap();
 
-    //!< Mapping of proxy model indices to indices in source model
-    QMap<QPersistentModelIndex, QPersistentModelIndex> m_sourceToProxy;
-    //!< Mapping of proxy model indices to indices of parent in source model
-    QMap<QPersistentModelIndex, QPersistentModelIndex> m_proxySourceParent;
     SessionModel* m_model;
+    std::unique_ptr<ProxyModelStrategy> m_proxyStrategy;
 };
 
 #endif  // COMPONENTPROXYMODEL_H
diff --git a/GUI/coregui/Models/ProxyModelStrategy.cpp b/GUI/coregui/Models/ProxyModelStrategy.cpp
new file mode 100644
index 00000000000..769537a1d83
--- /dev/null
+++ b/GUI/coregui/Models/ProxyModelStrategy.cpp
@@ -0,0 +1,58 @@
+// ************************************************************************** //
+//
+//  BornAgain: simulate and fit scattering at grazing incidence
+//
+//! @file      GUI/coregui/Models/ProxyModelStrategy.cpp
+//! @brief     Implements class ProxyModelStrategy
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2016
+//! @authors   Scientific Computing Group at MLZ Garching
+//! @authors   Céline Durniak, Marina Ganeva, David Li, Gennady Pospelov
+//! @authors   Walter Van Herck, Joachim Wuttke
+//
+// ************************************************************************** //
+
+#include "ProxyModelStrategy.h"
+#include "ModelUtils.h"
+#include "ComponentProxyModel.h"
+
+const ProxyModelStrategy::map_t& ProxyModelStrategy::sourceToProxy()
+{
+    return m_sourceToProxy;
+}
+
+const ProxyModelStrategy::map_t& ProxyModelStrategy::proxySourceParent()
+{
+    return m_proxySourceParent;
+}
+
+//! Method to ask proxy to create an index using friendship of ProxyModelStrategy
+//! and ComponentProxyModel.
+
+QModelIndex ProxyModelStrategy::createIndex(ComponentProxyModel* proxy, int nrow, int ncol,
+                                            void* adata)
+{
+    return proxy->createIndex(nrow, ncol, adata);
+}
+
+//! Builds one-to-one mapping for source and proxy.
+
+void IndentityProxyStrategy::buildModelMap(QAbstractItemModel* source, ComponentProxyModel* proxy)
+{
+    m_sourceToProxy.clear();
+    m_proxySourceParent.clear();
+
+    ModelUtils::iterate(QModelIndex(), source, [=](const QModelIndex& index) {
+        QPersistentModelIndex proxyIndex
+            = createIndex(proxy, index.row(), index.column(), index.internalPointer());
+        m_sourceToProxy.insert(QPersistentModelIndex(index), proxyIndex);
+
+        QPersistentModelIndex sourceParent;
+        if (index.parent().isValid())
+            sourceParent = index.parent();
+
+        m_proxySourceParent.insert(proxyIndex, sourceParent);
+    });
+}
diff --git a/GUI/coregui/Models/ProxyModelStrategy.h b/GUI/coregui/Models/ProxyModelStrategy.h
new file mode 100644
index 00000000000..fbb1eb2b704
--- /dev/null
+++ b/GUI/coregui/Models/ProxyModelStrategy.h
@@ -0,0 +1,56 @@
+// ************************************************************************** //
+//
+//  BornAgain: simulate and fit scattering at grazing incidence
+//
+//! @file      GUI/coregui/Models/ProxyModelStrategy.h
+//! @brief     Defines class ProxyModelStrategy
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2016
+//! @authors   Scientific Computing Group at MLZ Garching
+//! @authors   Céline Durniak, Marina Ganeva, David Li, Gennady Pospelov
+//! @authors   Walter Van Herck, Joachim Wuttke
+//
+// ************************************************************************** //
+
+#ifndef PROXYMODELSTRATEGY_H
+#define PROXYMODELSTRATEGY_H
+
+#include "WinDllMacros.h"
+#include <QPersistentModelIndex>
+
+class QAbstractItemModel;
+class ComponentProxyModel;
+
+//! Base class for proxy strategies in ComponentProxyModel.
+
+class BA_CORE_API_ ProxyModelStrategy
+{
+public:
+    using map_t = QMap<QPersistentModelIndex, QPersistentModelIndex>;
+
+    virtual ~ProxyModelStrategy() = default;
+    virtual void buildModelMap(QAbstractItemModel* source, ComponentProxyModel* proxy) = 0;
+
+    const map_t& sourceToProxy();
+    const map_t& proxySourceParent();
+
+protected:
+    QModelIndex createIndex(ComponentProxyModel* proxy, int nrow, int ncol, void* adata);
+
+    //!< Mapping of proxy model indices to indices in source model
+    QMap<QPersistentModelIndex, QPersistentModelIndex> m_sourceToProxy;
+    //!< Mapping of proxy model indices to indices of parent in source model
+    QMap<QPersistentModelIndex, QPersistentModelIndex> m_proxySourceParent;
+};
+
+//! Strategy for ComponentProxyModel which makes it identical to source model.
+
+class BA_CORE_API_ IndentityProxyStrategy : public ProxyModelStrategy
+{
+public:
+    void buildModelMap(QAbstractItemModel* source, ComponentProxyModel* proxy);
+};
+
+#endif  // ProxyModelStrategy
diff --git a/Tests/UnitTests/GUI/TestGUI.cpp b/Tests/UnitTests/GUI/TestGUI.cpp
index b327e99aea8..fa44f095501 100644
--- a/Tests/UnitTests/GUI/TestGUI.cpp
+++ b/Tests/UnitTests/GUI/TestGUI.cpp
@@ -33,6 +33,7 @@
 #include "TestSessionItemController.h"
 #include "TestModelUtils.h"
 #include "TestComponentProxyModel.h"
+#include "TestProxyModelStrategy.h"
 #include <memory>
 
 class GUITestFactory {
@@ -95,8 +96,9 @@ int main(int argc, char** argv) {
 //    tests.add<TestParticleCoreShell>();
 //    tests.add<TestPropertyRepeater>();
 //    tests.add<TestSessionItemController>();
-    tests.add<TestModelUtils>();
+//    tests.add<TestModelUtils>();
     tests.add<TestComponentProxyModel>();
+    tests.add<TestProxyModelStrategy>();
 
     return tests.runAll(argc, argv);
 }
diff --git a/Tests/UnitTests/GUI/TestProxyModelStrategy.h b/Tests/UnitTests/GUI/TestProxyModelStrategy.h
new file mode 100644
index 00000000000..d1878667b4a
--- /dev/null
+++ b/Tests/UnitTests/GUI/TestProxyModelStrategy.h
@@ -0,0 +1,102 @@
+#include <QtTest>
+#include "ModelUtils.h"
+#include "SessionModel.h"
+#include "item_constants.h"
+#include "ComponentProxyModel.h"
+#include "ProxyModelStrategy.h"
+#include "VectorItem.h"
+#include "ParticleItem.h"
+#include "FormFactorItems.h"
+#include <QSignalSpy>
+#include <QDebug>
+
+class TestProxyModelStrategy : public QObject
+{
+    Q_OBJECT
+public:
+
+private slots:
+    void test_identityStrategy();
+    void test_identityStrategyParticle();
+};
+
+//! Checking the mapping in the case of PropertyItem inserted in the source.
+
+inline void TestProxyModelStrategy::test_identityStrategy()
+{
+    SessionModel model("TestModel");
+    ComponentProxyModel proxy;
+    IndentityProxyStrategy strategy;
+
+    QCOMPARE(strategy.sourceToProxy().size(), 0);
+    QCOMPARE(strategy.proxySourceParent().size(), 0);
+
+    // building the map of empty source
+    strategy.buildModelMap(&model, &proxy);
+    QCOMPARE(strategy.sourceToProxy().size(), 0);
+    QCOMPARE(strategy.proxySourceParent().size(), 0);
+
+    // building map when simple item
+    SessionItem* item = model.insertNewItem(Constants::PropertyType);
+    strategy.buildModelMap(&model, &proxy);
+    QCOMPARE(strategy.sourceToProxy().size(), 2);
+    QCOMPARE(strategy.proxySourceParent().size(), 2);
+
+    // Checking of persistent indices of source and proxy
+    auto it = strategy.sourceToProxy().begin();
+    // index of source, col=0
+    QCOMPARE(it.key().row(), 0);
+    QCOMPARE(it.key().column(), 0);
+    QCOMPARE(it.key().internalPointer(), item);
+    // index of proxy, col=0
+    QCOMPARE(it.value().row(), 0);
+    QCOMPARE(it.value().column(), 0);
+    QCOMPARE(it.value().internalPointer(), item);
+    ++it;
+    // index of source, col=1
+    QCOMPARE(it.key().row(), 0);
+    QCOMPARE(it.key().column(), 1);
+    QCOMPARE(it.key().internalPointer(), item);
+    // index of proxy, col=1
+    QCOMPARE(it.value().row(), 0);
+    QCOMPARE(it.value().column(), 1);
+    QCOMPARE(it.value().internalPointer(), item);
+
+    // Checking parent of proxy
+    it = strategy.proxySourceParent().begin();
+    QCOMPARE(it.key().row(), 0);
+    QCOMPARE(it.key().column(), 0);
+    QCOMPARE(it.key().internalPointer(), item);
+    QVERIFY(it.value() == QModelIndex());
+}
+
+//! Checking the mapping in the case of ParticleItem inserted in the source.
+
+inline void TestProxyModelStrategy::test_identityStrategyParticle()
+{
+    SessionModel model("TestModel");
+    ComponentProxyModel proxy;
+    IndentityProxyStrategy strategy;
+
+    SessionItem* item = model.insertNewItem(Constants::ParticleType);
+
+    // building the map of empty source
+    strategy.buildModelMap(&model, &proxy);
+    SessionItem* group = item->getItem(ParticleItem::P_FORM_FACTOR);
+    SessionItem* ffItem = item->getGroupItem(ParticleItem::P_FORM_FACTOR);
+    QVERIFY(ffItem->parent() == group);
+    QVERIFY(ffItem->modelType() == Constants::CylinderType);
+
+    // Checking "real" parent of proxy index related to form factor.
+    // For identity model we are testing, it has to be just group property.
+    auto ffProxyIndex = strategy.sourceToProxy().value(model.indexOfItem(ffItem));
+    auto parentOfProxy = strategy.proxySourceParent().value(ffProxyIndex);
+    QVERIFY(parentOfProxy == model.indexOfItem(group));
+
+    // Checking "real" parent of Cylinders radius. It has to be CylinderItem
+    SessionItem* radiusItem = ffItem->getItem(CylinderItem::P_RADIUS);
+    auto radiusProxyIndex = strategy.sourceToProxy().value(model.indexOfItem(radiusItem));
+    parentOfProxy = strategy.proxySourceParent().value(radiusProxyIndex);
+    QVERIFY(parentOfProxy == model.indexOfItem(ffItem));
+}
+
-- 
GitLab