From 7d00b49f914b4d36331df163cd8e9fe1233c53cb Mon Sep 17 00:00:00 2001
From: Mirko Waechter <mirko.waechter@kit.edu>
Date: Wed, 15 Jun 2016 17:54:35 +0200
Subject: [PATCH] refactored and improved treeview search

---
 .../StateTreeController.cpp                   |  78 +-----------
 .../StateTreeController.h                     |  16 +--
 .../libraries/ArmarXGuiBase/CMakeLists.txt    |   3 +
 .../widgets/InfixFilterModel.cpp              | 117 ++++++++++++++++++
 .../ArmarXGuiBase/widgets/InfixFilterModel.h  |  60 +++++++++
 5 files changed, 188 insertions(+), 86 deletions(-)
 create mode 100644 source/ArmarXGui/libraries/ArmarXGuiBase/widgets/InfixFilterModel.cpp
 create mode 100644 source/ArmarXGui/libraries/ArmarXGuiBase/widgets/InfixFilterModel.h

diff --git a/source/ArmarXGui/gui-plugins/StatechartEditorPlugin/StateTreeController.cpp b/source/ArmarXGui/gui-plugins/StatechartEditorPlugin/StateTreeController.cpp
index 69ad4119..a3eb8fe6 100644
--- a/source/ArmarXGui/gui-plugins/StatechartEditorPlugin/StateTreeController.cpp
+++ b/source/ArmarXGui/gui-plugins/StatechartEditorPlugin/StateTreeController.cpp
@@ -71,7 +71,7 @@ StateTreeController::StateTreeController(Ice::CommunicatorPtr ic, VariantInfoPtr
     this->communicator = ic;
 
 
-    proxyModel = new StateFilterModel(this);
+    proxyModel = new InfixFilterModel(this);
     proxyModel->setSourceModel(this);
     proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
     treeView->setModel(proxyModel);
@@ -170,7 +170,7 @@ StateTreeController::StateTreeController(Ice::CommunicatorPtr ic, VariantInfoPtr
     connect(actionGroupProperties, SIGNAL(triggered()), this, SLOT(onGroupProperties()));
 
     connect(filterLineEdit, SIGNAL(textChanged(QString)), proxyModel, SLOT(setFilterFixedString(QString)));
-    connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(expandAll(QString)));
+    connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(expandFilterResults(QString)));
 }
 
 StateTreeController::~StateTreeController()
@@ -1418,39 +1418,14 @@ void StateTreeController::stateAddedOrRemoved()
     treeView->repaint();
 }
 
-void StateTreeController::expandAll(const QString& filterString)
+void StateTreeController::expandFilterResults(const QString& filterString)
 {
     if (filterString.length() == 0)
     {
         return;
     }
 
-    treeView->collapseAll();
-
-    QList<QModelIndex> indexList;
-    for (int i = 0; i <  proxyModel->rowCount(); ++i)
-    {
-        indexList << proxyModel->index(i, 0);
-    }
-    while (indexList.size() > 0)
-    {
-        QModelIndex& index = indexList.front();
-        if (proxyModel->data(index).toString().contains(proxyModel->filterRegExp()))
-        {
-            if (index.parent().isValid())
-            {
-                treeView->expand(index.parent());
-            }
-        }
-        int i = 0;
-
-        while (index.child(i, 0).isValid())
-        {
-            indexList << index.child(i, 0);
-            i++;
-        }
-        indexList.pop_front();
-    }
+    InfixFilterModel::ExpandFilterResults(treeView);
 
 }
 
@@ -1607,48 +1582,3 @@ bool StateTreeController::StateMimeData::isPublic() const
     return stateTreeModel->getNodeByState(this->state)->isPublic();
 }
 
-
-bool StateFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
-{
-
-    QModelIndex index0 = sourceModel()->index(source_row, 0, source_parent);
-    QList<QModelIndex> indexList;
-    indexList << index0;
-    auto checkIndex = [this](QModelIndex index)
-    {
-        return sourceModel()->data(index).toString().contains(filterRegExp());
-    };
-    //    if (checkIndex(index0))
-    //    {
-    //        ARMARX_INFO_S << "Direct hit " << sourceModel()->data(index0).toString().toStdString() << LogSender::CreateBackTrace();
-    //        emit matchedItem(mapFromSource(index0)); // this is a direct match -> expand treeview to this
-    //    }
-
-    while (indexList.size() > 0)
-    {
-        QModelIndex& index = indexList.front();
-        if (checkIndex(index))
-        {
-            return true;
-        }
-        int i = 0;
-
-        while (index.child(i, 0).isValid())
-        {
-            indexList << index.child(i, 0);
-            i++;
-        }
-        indexList.pop_front();
-    }
-    QModelIndex parent = source_parent;
-    while (parent.isValid())
-    {
-        if (sourceModel()->data(parent).toString().contains(filterRegExp()))
-        {
-            return true;
-        }
-        parent = parent.parent();
-
-    }
-    return false;
-}
diff --git a/source/ArmarXGui/gui-plugins/StatechartEditorPlugin/StateTreeController.h b/source/ArmarXGui/gui-plugins/StatechartEditorPlugin/StateTreeController.h
index 8547a6ac..028ed127 100644
--- a/source/ArmarXGui/gui-plugins/StatechartEditorPlugin/StateTreeController.h
+++ b/source/ArmarXGui/gui-plugins/StatechartEditorPlugin/StateTreeController.h
@@ -40,6 +40,7 @@
 #include <ArmarXCore/statechart/xmlstates/profiles/StatechartProfiles.h>
 #include <ArmarXGui/gui-plugins/StatechartEditorPlugin/cloning/GroupCloner.h>
 #include <ArmarXGui/gui-plugins/StatechartEditorPlugin/renaming/GroupRenamer.h>
+#include <ArmarXGui/libraries/ArmarXGuiBase/widgets/InfixFilterModel.h>
 
 
 class QProcess;
@@ -50,16 +51,7 @@ namespace armarx
     typedef boost::shared_ptr<StatechartProfiles> StatechartProfilesPtr;
 
 
-    class StateFilterModel :
-        public QSortFilterProxyModel
-    {
-        Q_OBJECT
-    public:
-        StateFilterModel(QObject* parent = 0) : QSortFilterProxyModel(parent) {}
-        // QSortFilterProxyModel interface
-    protected:
-        bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
-    };
+
 
 
     class StateTreeController;
@@ -151,7 +143,7 @@ namespace armarx
         void processOutputReady();
         void processErrorsReady();
         void stateAddedOrRemoved();
-        void expandAll(const QString& filterString);
+        void expandFilterResults(const QString& filterString);
     private:
 
         QModelIndex getIndex(StateTreeNodePtr node);
@@ -161,7 +153,7 @@ namespace armarx
         QList<QVariant> headerInfo;
         QTreeView* treeView;
         StateTreeModelPtr stateTreeModel;
-        StateFilterModel* proxyModel;
+        InfixFilterModel* proxyModel;
         GroupClonerPtr groupCloner;
         GroupRenamerPtr groupRenamer;
         StatechartGroupPtr activeGroup;
diff --git a/source/ArmarXGui/libraries/ArmarXGuiBase/CMakeLists.txt b/source/ArmarXGui/libraries/ArmarXGuiBase/CMakeLists.txt
index 2d87959e..84fbb67b 100644
--- a/source/ArmarXGui/libraries/ArmarXGuiBase/CMakeLists.txt
+++ b/source/ArmarXGui/libraries/ArmarXGuiBase/CMakeLists.txt
@@ -36,6 +36,7 @@ set(LIB_FILES   ArmarXWidgetController.cpp
                 widgets/cpp-markdown/markdown.cpp
                 widgets/cpp-markdown/markdown-tokens.cpp
                 widgets/TipDialog.cpp
+                widgets/InfixFilterModel.cpp
                 )
 
 set(LIB_HEADERS ArmarXGuiInterface.h
@@ -52,6 +53,7 @@ set(LIB_HEADERS ArmarXGuiInterface.h
                 widgets/cpp-markdown/markdown.h
                 widgets/cpp-markdown/markdown-tokens.h
                 widgets/TipDialog.h
+                widgets/InfixFilterModel.h
                 )
 
 set(GUI_UIS
@@ -74,6 +76,7 @@ qt4_wrap_cpp(LIB_FILES
     widgets/JoystickControlWidget.h
     widgets/MarkdownEditor.h
     widgets/TipDialog.h
+    widgets/InfixFilterModel.h
     )
 qt4_wrap_ui(UI_HEADER ${GUI_UIS})
 
diff --git a/source/ArmarXGui/libraries/ArmarXGuiBase/widgets/InfixFilterModel.cpp b/source/ArmarXGui/libraries/ArmarXGuiBase/widgets/InfixFilterModel.cpp
new file mode 100644
index 00000000..380eef67
--- /dev/null
+++ b/source/ArmarXGui/libraries/ArmarXGuiBase/widgets/InfixFilterModel.cpp
@@ -0,0 +1,117 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package    ArmarX
+ * @author     Mirko Waechter( mirko.waechter at kit dot edu)
+ * @date       2016
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+#include "InfixFilterModel.h"
+#include <ArmarXCore/core/logging/Logging.h>
+#include <QTreeView>
+
+namespace armarx
+{
+
+
+    InfixFilterModel::InfixFilterModel(QObject* parent) : QSortFilterProxyModel(parent)
+    {
+
+    }
+
+    void InfixFilterModel::ExpandFilterResults(QTreeView* treeView)
+    {
+        if (!treeView)
+        {
+            return;
+        }
+        treeView->collapseAll();
+        QSortFilterProxyModel* proxyModel = qobject_cast<QSortFilterProxyModel*>(treeView->model());
+        if (!proxyModel)
+        {
+            ARMARX_WARNING_S << "Could not cast treeview model to proxymodel!";
+            return;
+        }
+        QList<QModelIndex> indexList;
+        for (int i = 0; i <  proxyModel->rowCount(); ++i)
+        {
+            indexList << proxyModel->index(i, 0);
+        }
+        while (indexList.size() > 0)
+        {
+            QModelIndex& index = indexList.front();
+            if (proxyModel->data(index).toString().contains(proxyModel->filterRegExp()))
+            {
+                if (index.parent().isValid())
+                {
+                    treeView->expand(index.parent());
+                }
+            }
+            int i = 0;
+
+            while (index.child(i, 0).isValid())
+            {
+                indexList << index.child(i, 0);
+                i++;
+            }
+            indexList.pop_front();
+        }
+    }
+
+    bool InfixFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
+    {
+
+        QModelIndex index0 = sourceModel()->index(source_row, 0, source_parent);
+        QList<QModelIndex> indexList;
+        indexList << index0;
+        auto checkIndex = [this](QModelIndex index)
+        {
+            return sourceModel()->data(index).toString().contains(filterRegExp());
+        };
+
+        while (indexList.size() > 0)
+        {
+            QModelIndex& index = indexList.front();
+            if (checkIndex(index))
+            {
+                return true;
+            }
+            int i = 0;
+
+            while (index.child(i, 0).isValid())
+            {
+                indexList << index.child(i, 0);
+                i++;
+            }
+            indexList.pop_front();
+        }
+        QModelIndex parent = source_parent;
+        while (parent.isValid())
+        {
+            if (sourceModel()->data(parent).toString().contains(filterRegExp()))
+            {
+                return true;
+            }
+            parent = parent.parent();
+
+        }
+        return false;
+    }
+
+
+} // namespace armarx
diff --git a/source/ArmarXGui/libraries/ArmarXGuiBase/widgets/InfixFilterModel.h b/source/ArmarXGui/libraries/ArmarXGuiBase/widgets/InfixFilterModel.h
new file mode 100644
index 00000000..9fae2ec7
--- /dev/null
+++ b/source/ArmarXGui/libraries/ArmarXGuiBase/widgets/InfixFilterModel.h
@@ -0,0 +1,60 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package    ArmarX
+ * @author     Mirko Waechter( mirko.waechter at kit dot edu)
+ * @date       2016
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+#ifndef H_ARMARX_INFIXFILTERMODEL_H
+#define H_ARMARX_INFIXFILTERMODEL_H
+
+#include <QObject>
+#include <QSortFilterProxyModel>
+
+class QTreeView;
+
+namespace armarx
+{
+
+    /**
+     * @brief This proxy model reimplements the filterAcceptsRow function with a new behavior:
+     * All elements that fit the filter string are shown alongside with all their parents as well as all their children.
+     * This means the full sub-tree which contains matching elements is shown.
+     *
+     */
+    class InfixFilterModel :
+        public QSortFilterProxyModel
+    {
+        Q_OBJECT
+    public:
+        InfixFilterModel(QObject* parent = 0);
+        // QSortFilterProxyModel interface
+        /**
+         * @brief Expands the treeview that all items that match the filterstring are expanded and directly visible.
+         * Anything else is collapsed.
+         * @param treeView
+         */
+        static void ExpandFilterResults(QTreeView* treeView);
+    protected:
+        bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
+    };
+
+}
+
+#endif
-- 
GitLab