From 2a4b97ba1a2fb4512ca7968162a9eb49287867b6 Mon Sep 17 00:00:00 2001
From: Rainer Kartmann <rainer.kartmann@kit.edu>
Date: Fri, 9 Oct 2020 15:42:43 +0200
Subject: [PATCH] Refactor TreeWidgetBuilder into own file

---
 .../gui-plugins/ObjectPoseGui/CMakeLists.txt  |   7 +-
 .../ObjectPoseGui/ObjectPoseGuiWidget.ui      |  27 +-
 .../ObjectPoseGuiWidgetController.cpp         | 256 +++-------------
 .../ObjectPoseGui/TreeWidgetBuilder.h         | 287 ++++++++++++++++++
 4 files changed, 342 insertions(+), 235 deletions(-)
 create mode 100644 source/RobotAPI/gui-plugins/ObjectPoseGui/TreeWidgetBuilder.h

diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/CMakeLists.txt b/source/RobotAPI/gui-plugins/ObjectPoseGui/CMakeLists.txt
index d4d29bd3e..e588dbbb7 100644
--- a/source/RobotAPI/gui-plugins/ObjectPoseGui/CMakeLists.txt
+++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/CMakeLists.txt
@@ -8,12 +8,15 @@ armarx_set_target("ObjectPoseGuiPlugin")
 armarx_build_if(ArmarXGui_FOUND "ArmarXGui not available")
 
 set(SOURCES
-    ObjectPoseGuiPlugin.cpp ObjectPoseGuiWidgetController.cpp
+    ObjectPoseGuiPlugin.cpp
+    ObjectPoseGuiWidgetController.cpp
 )
 
 # do not rename this variable, it is used in armarx_gui_library()...
 set(HEADERS
-    ObjectPoseGuiPlugin.h ObjectPoseGuiWidgetController.h
+    ObjectPoseGuiPlugin.h
+    ObjectPoseGuiWidgetController.h
+    TreeWidgetBuilder.h
 )
 
 set(GUI_MOC_HDRS ${HEADERS})
diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui
index bc8167582..00677428b 100644
--- a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui
+++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui
@@ -143,9 +143,6 @@
            <property name="columnCount">
             <number>3</number>
            </property>
-           <attribute name="headerCascadingSectionResizes">
-            <bool>true</bool>
-           </attribute>
            <attribute name="headerStretchLastSection">
             <bool>true</bool>
            </attribute>
@@ -166,7 +163,7 @@
            </column>
            <item>
             <property name="text">
-             <string>Dataset2</string>
+             <string>Dataset1</string>
             </property>
             <property name="text">
              <string/>
@@ -176,10 +173,10 @@
             </property>
             <item>
              <property name="text">
-              <string>ClassName3</string>
+              <string>ClassName1</string>
              </property>
              <property name="text">
-              <string>Provider1</string>
+              <string>Provider2</string>
              </property>
              <property name="text">
               <string/>
@@ -187,16 +184,19 @@
             </item>
             <item>
              <property name="text">
-              <string>ClassName3</string>
+              <string>ClassName2</string>
              </property>
              <property name="text">
-              <string>Provider2</string>
+              <string>Provider1</string>
+             </property>
+             <property name="text">
+              <string/>
              </property>
             </item>
            </item>
            <item>
             <property name="text">
-             <string>Dataset1</string>
+             <string>Dataset2</string>
             </property>
             <property name="text">
              <string/>
@@ -206,7 +206,7 @@
             </property>
             <item>
              <property name="text">
-              <string>ClassName2</string>
+              <string>ClassName3</string>
              </property>
              <property name="text">
               <string>Provider1</string>
@@ -217,14 +217,11 @@
             </item>
             <item>
              <property name="text">
-              <string>ClassName1</string>
+              <string>ClassName3</string>
              </property>
              <property name="text">
               <string>Provider2</string>
              </property>
-             <property name="text">
-              <string/>
-             </property>
             </item>
            </item>
           </widget>
@@ -258,7 +255,7 @@
             <double>0.250000000000000</double>
            </property>
            <property name="value">
-            <double>5.000000000000000</double>
+            <double>30.000000000000000</double>
            </property>
           </widget>
          </item>
diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp
index 5742df60a..495ad1972 100644
--- a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp
+++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp
@@ -31,6 +31,8 @@
 #include <RobotAPI/components/ObjectPoseObserver/ice_conversions.h>
 #include <RobotAPI/components/ObjectPoseObserver/ObjectPose.h>
 
+#include "TreeWidgetBuilder.h"
+
 
 namespace armarx
 {
@@ -144,194 +146,6 @@ namespace armarx
     }
 
 
-    template <class ContainerT>
-    struct TreeVisitor
-    {
-        using ElementT = typename ContainerT::value_type;
-
-        /// Return < 0 if `element < item`, 0 if `element == item`, and > 0 if `element > item`.
-        using CompareFn = std::function<int(const ElementT& element, QTreeWidgetItem* item)>;
-        using NameFn = std::function<std::string(const ElementT& element)>;
-        using MakeItemFn = std::function<QTreeWidgetItem*(const ElementT& element)>;
-        using UpdateItemFn = std::function<bool(const ElementT& element, QTreeWidgetItem* item)>;
-
-        TreeVisitor() = default;
-        TreeVisitor(const ContainerT&)
-        {}
-
-        static bool NoUpdate(const ElementT& element, QTreeWidgetItem* item)
-        {
-            (void) element, (void) item;
-            return true;
-        }
-
-        void enableSort(QTreeWidget* tree)
-        {
-            tree->sortItems(0, Qt::SortOrder::AscendingOrder);
-            tree->isSortingEnabled();
-        }
-
-        template <typename ParentT>
-        void updateTree(ParentT* parent, const ContainerT& elements,
-                        CompareFn compareFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate)
-        {
-            int currentIndex = 0;
-            for (const auto& element : elements)
-            {
-                QTreeWidgetItem* item = nullptr;
-                if (currentIndex >= getItemCount(parent))
-                {
-                    // Add elements to the end of the list.
-                    item = makeItemFn(element);
-                    insertItem(parent, getItemCount(parent), item);
-                    ++currentIndex;
-                }
-                else
-                {
-                    QTreeWidgetItem* currentItem = getItem(parent, currentIndex);
-                    while (currentItem != nullptr && compareFn(element, currentItem) > 0)
-                    {
-                        delete takeItem(parent, currentIndex);
-                        currentItem = getItem(parent, currentIndex);
-                    }
-                    if (currentItem == nullptr || compareFn(element, currentItem) < 0)
-                    {
-                        // Insert new item before child.
-                        item = makeItemFn(element);
-                        insertItem(parent, currentIndex, item);
-                        ++currentIndex;
-                    }
-                    else if (currentItem != nullptr && compareFn(element, currentItem) == 0)
-                    {
-                        // Already existing.
-                        item = currentItem;
-                        ++currentIndex;
-                    }
-                }
-                ARMARX_CHECK_NOT_NULL(item);
-                if (!updateItemFn(element, item))
-                {
-                    break;
-                }
-            }
-        }
-
-        template <typename ParentT>
-        void updateTree(ParentT* parent, const ContainerT& elements,
-                        NameFn nameFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate)
-        {
-            auto compare = [nameFn](const ElementT & element, QTreeWidgetItem * item)
-            {
-                std::string name = nameFn(element);
-                return name.compare(item->text(0).toStdString());
-            };
-            updateTree(parent, elements, compare, makeItemFn, updateItemFn);
-        }
-
-    private:
-
-        int getItemCount(QTreeWidget* tree)
-        {
-            return tree->topLevelItemCount();
-        }
-        QTreeWidgetItem* getItem(QTreeWidget* tree, int index)
-        {
-            return tree->topLevelItem(index);
-        }
-        void insertItem(QTreeWidget* tree, int index, QTreeWidgetItem* item)
-        {
-            tree->insertTopLevelItem(index, item);
-        }
-        QTreeWidgetItem* takeItem(QTreeWidget* tree, int index)
-        {
-            return tree->takeTopLevelItem(index);
-        }
-
-        int getItemCount(QTreeWidgetItem* parent)
-        {
-            return parent->childCount();
-        }
-        QTreeWidgetItem* getItem(QTreeWidgetItem* parent, int index)
-        {
-            return parent->child(index);
-        }
-        QTreeWidgetItem* takeItem(QTreeWidgetItem* parent, int index)
-        {
-            return parent->takeChild(index);
-        }
-        void insertItem(QTreeWidgetItem* parent, int index, QTreeWidgetItem* item)
-        {
-            parent->insertChild(index, item);
-        }
-    };
-
-    template <class KeyT, class ValueT>
-    struct MapTreeVisitor : private TreeVisitor<std::map<KeyT, ValueT>>
-    {
-        using MapT = std::map<KeyT, ValueT>;
-        using Base = TreeVisitor<MapT>;
-        using ElementT = typename Base::ElementT;
-
-        using NameFn = std::function<std::string(const KeyT& key, const ValueT& value)>;
-
-        using MakeItemFn = std::function<QTreeWidgetItem*(const KeyT& key, const ValueT& value)>;
-        using UpdateItemFn = std::function<bool(const KeyT& key, const ValueT& value, QTreeWidgetItem* item)>;
-
-        MapTreeVisitor() = default;
-        MapTreeVisitor(const MapT&)
-        {}
-
-        static std::string KeyIsName(const KeyT& key, const ValueT& value)
-        {
-            (void) value;
-            if constexpr(std::is_same<KeyT, std::string>())
-            {
-                return key;
-            }
-            else
-            {
-                std::stringstream ss;
-                ss << key;
-                return ss.str();
-            }
-        }
-
-        static bool NoUpdate(const KeyT& key, const ValueT& value, QTreeWidgetItem* item)
-        {
-            (void) key, (void) value, (void) item;
-            return true;
-        }
-
-        void updateTree(QTreeWidget* tree, const MapT& elements,
-                        MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate)
-        {
-            updateTree(tree, elements, KeyIsName, makeItemFn, updateItemFn);
-        }
-
-        template <class ParentT>
-        void updateTree(ParentT* parent, const MapT& elements,
-                        NameFn nameFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate)
-        {
-            auto name = [nameFn](const ElementT & element)
-            {
-                const auto& [key, value] = element;
-                return nameFn(key, value);
-            };
-            auto makeItem = [makeItemFn](const ElementT & element)
-            {
-                const auto& [key, value] = element;
-                return makeItemFn(key, value);
-            };
-            auto updateItem = [updateItemFn](const ElementT & element, QTreeWidgetItem * item)
-            {
-                const auto& [key, value] = element;
-                return updateItemFn(key, value, item);
-            };
-
-            Base::updateTree(parent, elements, name, makeItem, updateItem);
-        }
-    };
-
 
     void ObjectPoseGuiWidgetController::updateObjectsTab()
     {
@@ -358,25 +172,28 @@ namespace armarx
         start = IceUtil::Time::now();
 
         QTreeWidget* tree = widget.objectsTree;
-        MapTreeVisitor visitor(objectPosesByProvider);
-        auto makeProviderItem = [](const std::string & provider, const objpose::ObjectPoseSeq&)
+
+        MapTreeWidgetBuilder builder(objectPosesByProvider);
+        builder.setMakeItemFn([](const std::string & provider, const objpose::ObjectPoseSeq&)
         {
             QTreeWidgetItem* item = new QTreeWidgetItem({QString::fromStdString(provider)});
-            item->setExpanded(true);
             return item;
-        };
-        auto updateItem = [](const std::string&, const objpose::ObjectPoseSeq & objectPoses, QTreeWidgetItem * item)
+        });
+        builder.setUpdateItemFn([](const std::string&, const objpose::ObjectPoseSeq & objectPoses, QTreeWidgetItem * item)
         {
-            auto name = [](const objpose::ObjectPose & pose)
+            bool expand = item->childCount() == 0;
+
+            TreeWidgetBuilder builder(objectPoses);
+            builder.setNameFn([](const objpose::ObjectPose & pose)
             {
                 return pose.objectID.str();
-            };
-            auto makeItem = [](const objpose::ObjectPose&)
+            });
+            builder.setMakeItemFn([](const objpose::ObjectPose&)
             {
                 QTreeWidgetItem* item = new QTreeWidgetItem(QStringList{});
                 return item;
-            };
-            auto updateItem = [](const objpose::ObjectPose & pose, QTreeWidgetItem * item)
+            });
+            builder.setUpdateItemFn([](const objpose::ObjectPose & pose, QTreeWidgetItem * item)
             {
                 int col = 0;
                 item->setText(col++, QString::fromStdString(pose.objectID.str()));
@@ -398,13 +215,17 @@ namespace armarx
                 item->setText(col++, QString::fromStdString(TimeUtil::toStringDateTime(pose.timestamp)));
 
                 return true;
-            };
-            TreeVisitor visitor(objectPoses);
-            visitor.updateTree(item, objectPoses, name, makeItem, updateItem);
+            });
+            builder.updateTree(item, objectPoses);
+
+            if (expand)
+            {
+                item->setExpanded(true);
+            }
 
             return true;
-        };
-        visitor.updateTree(tree, objectPosesByProvider, makeProviderItem, updateItem);
+        });
+        builder.updateTree(tree, objectPosesByProvider);
 
         ARMARX_INFO << "Gui update took " << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.";
     }
@@ -438,17 +259,18 @@ namespace armarx
 
         QTreeWidget* tree = widget.requestTree;
 
-        MapTreeVisitor visitor(data);
-        auto makeDatasetItem = [](const std::string & dataset, const auto&)
+        MapTreeWidgetBuilder builder(data);
+        builder.setMakeItemFn([](const std::string & dataset, const auto&)
         {
             QTreeWidgetItem* item = new QTreeWidgetItem({QString::fromStdString(dataset)});
             return item;
-        };
-        auto updateItem = [tree](const std::string & dataset, const auto & datasetData, QTreeWidgetItem * datasetItem)
+        });
+        builder.setUpdateItemFn([tree](const std::string & dataset, const auto & datasetData, QTreeWidgetItem * datasetItem)
         {
             (void) dataset;
 
-            auto compareFn = [](const std::pair<std::string, std::string>& lhs, QTreeWidgetItem * item)
+            TreeWidgetBuilder builder(datasetData);
+            builder.setCompareFn([](const std::pair<std::string, std::string>& lhs, QTreeWidgetItem * item)
             {
                 auto rhs = std::make_pair(item->text(0).toStdString(), item->text(1).toStdString());
                 if (lhs < rhs)
@@ -456,13 +278,13 @@ namespace armarx
                     return -1;
                 }
                 return lhs == rhs ? 0 : 1;
-            };
-            auto makeItemFn = [](const std::pair<std::string, std::string>& element)
+            });
+            builder.setMakeItemFn([](const std::pair<std::string, std::string>& element)
             {
                 QTreeWidgetItem* item = new QTreeWidgetItem({ QString::fromStdString(element.first), QString::fromStdString(element.second)});
                 return item;
-            };
-            auto updateItemFn = [tree](const std::pair<std::string, std::string>& element, QTreeWidgetItem * item)
+            });
+            builder.setUpdateItemFn([tree](const std::pair<std::string, std::string>& element, QTreeWidgetItem * item)
             {
                 (void) element;
                 if (!tree->itemWidget(item, 2))
@@ -471,14 +293,12 @@ namespace armarx
                     tree->setItemWidget(item, 2, requestCheckBox);
                 }
                 return true;
-            };
-
-            TreeVisitor visitor(datasetData);
-            visitor.updateTree(datasetItem, datasetData, compareFn, makeItemFn, updateItemFn);
+            });
+            builder.updateTree(datasetItem, datasetData);
 
             return true;
-        };
-        visitor.updateTree(tree, data, makeDatasetItem, updateItem);
+        });
+        builder.updateTree(tree, data);
 
         ARMARX_INFO << "Gui update took " << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.";
     }
diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/TreeWidgetBuilder.h b/source/RobotAPI/gui-plugins/ObjectPoseGui/TreeWidgetBuilder.h
new file mode 100644
index 000000000..2b7cdf0d8
--- /dev/null
+++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/TreeWidgetBuilder.h
@@ -0,0 +1,287 @@
+#pragma once
+
+#include <functional>
+#include <map>
+#include <sstream>
+
+#include <QTreeWidget>
+
+#include <ArmarXCore/core/exceptions/local/ExpressionException.h>
+
+
+namespace armarx
+{
+
+    /**
+     * A class to efficiently build and maintain sorted items of `QTreeWidget`
+     * or `QTreeWidgetItem` based on a sorted container matching the intended structure.
+     */
+    template <class ContainerT>
+    struct TreeWidgetBuilder
+    {
+        using ElementT = typename ContainerT::value_type;
+
+        /// Return < 0 if `element < item`, 0 if `element == item`, and > 0 if `element > item`.
+        using CompareFn = std::function<int(const ElementT& element, QTreeWidgetItem* item)>;
+        using NameFn = std::function<std::string(const ElementT& element)>;
+        using MakeItemFn = std::function<QTreeWidgetItem*(const ElementT& element)>;
+        using UpdateItemFn = std::function<bool(const ElementT& element, QTreeWidgetItem* item)>;
+
+
+        TreeWidgetBuilder() = default;
+        TreeWidgetBuilder(const ContainerT&)
+        {}
+
+        TreeWidgetBuilder(CompareFn compareFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate) :
+            compareFn(compareFn), makeItemFn(makeItemFn), updateItemFn(updateItemFn)
+        {}
+        TreeWidgetBuilder(NameFn nameFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate) :
+            compareFn(MakeCompareNameFn(nameFn)), makeItemFn(makeItemFn), updateItemFn(updateItemFn)
+        {}
+
+
+        void setCompareFn(CompareFn compareFn)
+        {
+            this->compareFn = compareFn;
+        }
+        void setNameFn(NameFn nameFn)
+        {
+            compareFn = MakeCompareNameFn(nameFn);
+        }
+        void setMakeItemFn(MakeItemFn makeItemFn)
+        {
+            this->makeItemFn = makeItemFn;
+        }
+        void setUpdateItemFn(UpdateItemFn updateItemFn)
+        {
+            this->updateItemFn = updateItemFn;
+        }
+
+
+        template <class ParentT>
+        void updateTree(ParentT* parent, const ContainerT& elements);
+
+
+        /// No update function (default).
+        static bool NoUpdate(const ElementT& element, QTreeWidgetItem* item)
+        {
+            (void) element, (void) item;
+            return true;
+        }
+
+        /// Use the name for comparison.
+        static CompareFn MakeCompareNameFn(NameFn nameFn)
+        {
+            return [nameFn](const ElementT & element, QTreeWidgetItem * item)
+            {
+                std::string name = nameFn(element);
+                return name.compare(item->text(0).toStdString());
+            };
+        }
+
+
+    private:
+
+        CompareFn compareFn;
+        MakeItemFn makeItemFn;
+        UpdateItemFn updateItemFn;
+
+    };
+
+
+    /**
+     * A class to efficiently build and maintain sorted items of `QTreeWidget`
+     * or `QTreeWidgetItem` based on a map matching the intended structure.
+     */
+    template <class KeyT, class ValueT>
+    struct MapTreeWidgetBuilder
+    {
+        using MapT = std::map<KeyT, ValueT>;
+        using Base = TreeWidgetBuilder<MapT>;
+        using ElementT = typename Base::ElementT;
+
+        using NameFn = std::function<std::string(const KeyT& key, const ValueT& value)>;
+
+        using MakeItemFn = std::function<QTreeWidgetItem*(const KeyT& key, const ValueT& value)>;
+        using UpdateItemFn = std::function<bool(const KeyT& key, const ValueT& value, QTreeWidgetItem* item)>;
+
+
+        MapTreeWidgetBuilder()
+        {
+            setNameFn(KeyAsName);
+        }
+        /// Allows declaring instance from container without explicit template arguments.
+        MapTreeWidgetBuilder(const MapT&) : MapTreeWidgetBuilder()
+        {}
+
+        MapTreeWidgetBuilder(MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate)  : MapTreeWidgetBuilder()
+        {
+            setMakeItemFn(makeItemFn);
+            setUpdateItemFn(updateItemFn);
+        }
+        MapTreeWidgetBuilder(NameFn nameFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate)
+        {
+            setNameFn(nameFn);
+            setMakeItemFn(makeItemFn);
+            setUpdateItemFn(updateItemFn);
+        }
+
+        void setNameFn(NameFn nameFn)
+        {
+            builder.setNameFn([nameFn](const ElementT & element)
+            {
+                const auto& [key, value] = element;
+                return nameFn(key, value);
+            });
+        }
+        void setMakeItemFn(MakeItemFn makeItemFn)
+        {
+            builder.setMakeItemFn([makeItemFn](const ElementT & element)
+            {
+                const auto& [key, value] = element;
+                return makeItemFn(key, value);
+            });
+        }
+        void setUpdateItemFn(UpdateItemFn updateItemFn)
+        {
+            builder.setUpdateItemFn([updateItemFn](const ElementT & element, QTreeWidgetItem * item)
+            {
+                const auto& [key, value] = element;
+                return updateItemFn(key, value, item);
+            });
+        }
+
+
+        template <class ParentT>
+        void updateTree(ParentT* tree, const MapT& elements)
+        {
+            builder.updateTree(tree, elements);
+        }
+
+
+        /// A name function using the key as name.
+        static std::string KeyAsName(const KeyT& key, const ValueT& value)
+        {
+            (void) value;
+            if constexpr(std::is_same<KeyT, std::string>())
+            {
+                return key;
+            }
+            else
+            {
+                std::stringstream ss;
+                ss << key;
+                return ss.str();
+            }
+        }
+
+        /// No update function (default).
+        static bool NoUpdate(const KeyT& key, const ValueT& value, QTreeWidgetItem* item)
+        {
+            (void) key, (void) value, (void) item;
+            return true;
+        }
+
+
+    private:
+
+        TreeWidgetBuilder<MapT> builder;
+
+    };
+
+
+
+    namespace detail
+    {
+        template <class ParentT> struct ParentAPI;
+
+        template <> struct ParentAPI<QTreeWidget>
+        {
+            static int getItemCount(QTreeWidget* tree)
+            {
+                return tree->topLevelItemCount();
+            }
+            static QTreeWidgetItem* getItem(QTreeWidget* tree, int index)
+            {
+                return tree->topLevelItem(index);
+            }
+            static void insertItem(QTreeWidget* tree, int index, QTreeWidgetItem* item)
+            {
+                tree->insertTopLevelItem(index, item);
+            }
+            static QTreeWidgetItem* takeItem(QTreeWidget* tree, int index)
+            {
+                return tree->takeTopLevelItem(index);
+            }
+        };
+
+        template <> struct ParentAPI<QTreeWidgetItem>
+        {
+            static int getItemCount(QTreeWidgetItem* parent)
+            {
+                return parent->childCount();
+            }
+            static QTreeWidgetItem* getItem(QTreeWidgetItem* parent, int index)
+            {
+                return parent->child(index);
+            }
+            static QTreeWidgetItem* takeItem(QTreeWidgetItem* parent, int index)
+            {
+                return parent->takeChild(index);
+            }
+            static void insertItem(QTreeWidgetItem* parent, int index, QTreeWidgetItem* item)
+            {
+                parent->insertChild(index, item);
+            }
+        };
+    }
+
+
+    template <class ContainerT>
+    template <typename ParentT>
+    void TreeWidgetBuilder<ContainerT>::updateTree(ParentT* parent, const ContainerT& elements)
+    {
+        using api = detail::ParentAPI<ParentT>;
+
+        int currentIndex = 0;
+        for (const auto& element : elements)
+        {
+            QTreeWidgetItem* item = nullptr;
+            if (currentIndex >= api::getItemCount(parent))
+            {
+                // Add elements to the end of the list.
+                item = makeItemFn(element);
+                api::insertItem(parent, api::getItemCount(parent), item);
+                ++currentIndex;
+            }
+            else
+            {
+                QTreeWidgetItem* currentItem = api::getItem(parent, currentIndex);
+                while (currentItem != nullptr && compareFn(element, currentItem) > 0)
+                {
+                    delete api::takeItem(parent, currentIndex);
+                    currentItem = api::getItem(parent, currentIndex);
+                }
+                if (currentItem == nullptr || compareFn(element, currentItem) < 0)
+                {
+                    // Insert new item before child.
+                    item = makeItemFn(element);
+                    api::insertItem(parent, currentIndex, item);
+                    ++currentIndex;
+                }
+                else if (currentItem != nullptr && compareFn(element, currentItem) == 0)
+                {
+                    // Already existing.
+                    item = currentItem;
+                    ++currentIndex;
+                }
+            }
+            ARMARX_CHECK_NOT_NULL(item);
+            if (!updateItemFn(element, item))
+            {
+                break;
+            }
+        }
+    }
+
+}
-- 
GitLab