From b55cc523ced64a08c3a9c85e0aa23c6c6aa76134 Mon Sep 17 00:00:00 2001
From: Pos <pierpaolo.mancini@mail.polimi.it>
Date: Sat, 12 Dec 2020 15:28:12 +0100
Subject: [PATCH] Added tree viewer module

---
 Core/xmlobject.cpp                      |   2 +-
 Core/xmlobject.h                        |   2 +-
 Modules/TreeViewer/treevieweritem.cpp   |  46 +++++++
 Modules/TreeViewer/treevieweritem.h     |  22 ++++
 Modules/TreeViewer/treeviewermodule.cpp | 155 +++++++++++++++++++++++-
 Modules/TreeViewer/treeviewermodule.h   |  25 ++++
 Modules/TreeViewer/treeviewermodule.ui  |  33 ++++-
 Modules/moduleslist.cpp                 |   1 -
 Modules/skywardhubstrings.cpp           |   1 +
 Modules/skywardhubstrings.h             |   1 +
 SkywardHub.pro                          |   2 +
 11 files changed, 280 insertions(+), 10 deletions(-)
 create mode 100644 Modules/TreeViewer/treevieweritem.cpp
 create mode 100644 Modules/TreeViewer/treevieweritem.h

diff --git a/Core/xmlobject.cpp b/Core/xmlobject.cpp
index 503a261e..d85f2367 100644
--- a/Core/xmlobject.cpp
+++ b/Core/xmlobject.cpp
@@ -225,7 +225,7 @@ int XmlObject::attributeCount() const
     return attributes.count();
 }
 
-QMapIterator<QString, QString> XmlObject::attributesIterator()
+QMapIterator<QString, QString> XmlObject::attributesIterator() const
 {
     return QMapIterator<QString,QString>(attributes);
 }
diff --git a/Core/xmlobject.h b/Core/xmlobject.h
index 73505c8d..f075cf94 100644
--- a/Core/xmlobject.h
+++ b/Core/xmlobject.h
@@ -24,7 +24,7 @@ public:
     bool hasAttribute(const QString &name) const;
     int attributeCount() const;
 
-    QMapIterator<QString, QString> attributesIterator();
+    QMapIterator<QString, QString> attributesIterator() const;
 
     /*
      * Set the value of "value" if the attribute with name "name" exist and is an integer
diff --git a/Modules/TreeViewer/treevieweritem.cpp b/Modules/TreeViewer/treevieweritem.cpp
new file mode 100644
index 00000000..211277c3
--- /dev/null
+++ b/Modules/TreeViewer/treevieweritem.cpp
@@ -0,0 +1,46 @@
+#include "treevieweritem.h"
+
+
+TreeViewerItem::TreeViewerItem() : QObject(), QTreeWidgetItem()
+{
+
+}
+
+TreeViewerItem::TreeViewerItem(const XmlObject &xml) : TreeViewerItem()
+{
+    this->xml = xml;
+    setText(0, xml.getObjectName());
+
+    int count = 1;
+    auto i = xml.attributesIterator();
+    while(i.hasNext()){
+        i.next();
+        // line = line + ", " + i.value() + " ("+i.key()+")";
+        setText(count, i.value());
+    }
+}
+
+TreeViewerItem::~TreeViewerItem()
+{
+
+}
+
+QList<QString> TreeViewerItem::getAttributesNameList() const
+{
+    QList<QString> list;
+    auto i = xml.attributesIterator();
+    while(i.hasNext()){
+        i.next();
+        list.append(i.key());
+    }
+    return list;
+}
+
+void TreeViewerItem::setAttributesColumn(QList<QString> headers)
+{
+    for(int column = 0; column < headers.count(); column++){
+        if(xml.hasAttribute(headers[column])){
+            setText(column, xml.getAttribute(headers[column]));
+        }
+    }
+}
diff --git a/Modules/TreeViewer/treevieweritem.h b/Modules/TreeViewer/treevieweritem.h
new file mode 100644
index 00000000..c9a90cd0
--- /dev/null
+++ b/Modules/TreeViewer/treevieweritem.h
@@ -0,0 +1,22 @@
+#ifndef TREEVIEWERITEM_H
+#define TREEVIEWERITEM_H
+
+#include <QObject>
+#include <QTreeWidgetItem>
+#include "Core/xmlobject.h"
+
+class TreeViewerItem : public QObject, public QTreeWidgetItem
+{
+    Q_OBJECT
+public:
+    TreeViewerItem();
+    TreeViewerItem(const XmlObject &xml);
+    ~TreeViewerItem();
+
+    QList<QString> getAttributesNameList() const;
+    void setAttributesColumn(QList<QString> headers);
+private:
+    XmlObject xml;
+};
+
+#endif // TREEVIEWERITEM_H
diff --git a/Modules/TreeViewer/treeviewermodule.cpp b/Modules/TreeViewer/treeviewermodule.cpp
index 30f061cb..0c4e6d37 100644
--- a/Modules/TreeViewer/treeviewermodule.cpp
+++ b/Modules/TreeViewer/treeviewermodule.cpp
@@ -1,17 +1,139 @@
 #include "treeviewermodule.h"
 #include "ui_treeviewermodule.h"
 
+#include <QLabel>
+#include <QDebug>
+#include <QTreeWidget>
+#include "Core/modulemessagesbroker.h"
+#include "treevieweritem.h"
+
 TreeViewerModule::TreeViewerModule(QWidget *parent) : DefaultModule(parent), ui(new Ui::TreeViewerModule)
 {
     ui->setupUi(this);
     defaultContextMenuSetup();
+    setupUI();
 }
 
 TreeViewerModule::~TreeViewerModule()
 {
     delete ui;
+    // The tree viewer items are deleted automatically with the ui QTreeWidget
+}
+
+void TreeViewerModule::setupUI()
+{
+    connect(ui->lineEdit_receivingTopic, &QLineEdit::textChanged, this, &TreeViewerModule::onLineEditTopicTextChanged);
+//    XmlObject test;
+//    test.fromXml("<Root>"
+//                     "<Child1 name='aaaa'>"
+//                         "<Child1.1/>"
+//                         "<Child1.2 name='boo1.2' attr1='provac1.2'/>"
+//                         "<Child1.3/>"
+//                     "</Child1>"
+//                     "<Child2 name='boo' attr1='provac2'/>"
+//                     "<Child3/>"
+//                 "</Root>"
+//                 "");
+
+    //showXmlTree(test);
+
+}
+
+void TreeViewerModule::showXmlTree(const XmlObject &xml)
+{
+    clearTree();
+    TreeViewerItem* root = createTreeItemFromXml(xml);
+    addItem(root);
+    headers.append("Object Name");
+    updateHeaders(root->getAttributesNameList());
+
+    for(int i = 0; i < xml.childCount(); i++){
+        addXmlItem(xml.childAt(i), root);
+    }
+
+    updateItemAttributesColumn();
+}
+
+void TreeViewerModule::addXmlItem(const XmlObject &xml, TreeViewerItem* parent)
+{
+    TreeViewerItem* item = createTreeItemFromXml(xml);
+    addItem(parent, item);
+    updateHeaders(item->getAttributesNameList());
+
+    for(int i = 0; i < xml.childCount(); i++){
+        addXmlItem(xml.childAt(i), item);
+    }
 }
 
+TreeViewerItem *TreeViewerModule::createTreeItemFromXml(const XmlObject &xml)
+{
+    return new TreeViewerItem(xml);
+}
+
+void TreeViewerModule::updateHeaders(const QList<QString> itemAttributes)
+{
+    bool updateHeader = false;
+    for(int i = 0; i < itemAttributes.count(); i++){
+        if(!headers.contains(itemAttributes[i])){
+            headers.append(itemAttributes[i]);
+            updateHeader = true;
+        }
+    }
+
+    if(updateHeader){
+        tree()->setColumnCount(headers.count());
+        tree()->setHeaderLabels(headers);
+    }
+}
+
+void TreeViewerModule::updateItemAttributesColumn()
+{
+    for(TreeViewerItem* item : items){
+        item->setAttributesColumn(headers);
+    }
+}
+
+void TreeViewerModule::setReceivingTopic(const QString &txt)
+{
+    getCore()->getModuleMessagesBroker()->unsubscribe(receivingTopic, this);
+    getCore()->getModuleMessagesBroker()->subscribe(txt, this, [this](const ModuleMessage &msg){
+        onMsgReceived(msg);
+    });
+
+    receivingTopic = txt;
+    if(ui->lineEdit_receivingTopic->text() != txt){
+        ui->lineEdit_receivingTopic->setText(txt);
+    }
+}
+
+void TreeViewerModule::onLineEditTopicTextChanged(const QString &txt)
+{
+    setReceivingTopic(txt);
+}
+
+void TreeViewerModule::clearTree()
+{
+    for(int i = 0; i < items.count(); i++){
+        delete items[i];
+    }
+    items.clear();
+
+    headers.clear();
+}
+
+QTreeWidget *TreeViewerModule::tree()
+{
+    return ui->treeWidget;
+}
+
+void TreeViewerModule::onMsgReceived(const ModuleMessage &msg)
+{
+    XmlObject xml;
+    xml.fromXml(msg.payload());
+    if(!xml.isEmpty()){
+        showXmlTree(xml);
+    }
+}
 
 QWidget *TreeViewerModule::toWidget()
 {
@@ -20,10 +142,39 @@ QWidget *TreeViewerModule::toWidget()
 
 XmlObject TreeViewerModule::toXmlObject()
 {
-    return XmlObject(getName(ModuleId::TREEVIEWER));
+    XmlObject xml(getName(ModuleId::TREEVIEWER));
+
+    if(receivingTopic != SkywardHubStrings::treeViewerReceivingBaseTopic){
+        xml.addAttribute("ReceivingTopic", receivingTopic);
+    }
+
+    return xml;
 }
 
 void TreeViewerModule::fromXmlObject(const XmlObject &xmlObject)
 {
-    Q_UNUSED(xmlObject);
+    QString attrName = "ReceivingTopic";
+    if(xmlObject.hasAttribute(attrName)){
+        setReceivingTopic(xmlObject.getAttribute(attrName));
+    }else{
+        setReceivingTopic(SkywardHubStrings::treeViewerReceivingBaseTopic);
+    }
+}
+
+void TreeViewerModule::addItem(TreeViewerItem *rootItem)
+{
+    tree()->addTopLevelItem(rootItem);
+    //tree()->setItemWidget(rootItem, 1, new QLabel(QString::number(i)));
+    items.insert(0,rootItem);
+}
+
+void TreeViewerModule::addItem(TreeViewerItem *parentItem, TreeViewerItem *childItem)
+{
+    if(parentItem != nullptr && childItem != nullptr){
+        parentItem->addChild(childItem);
+//        tree()->setItemWidget(item2, 1, new QLabel(QString::number(i)+"."+QString::number(c)));
+        items.insert(0,childItem);
+    }
 }
+
+
diff --git a/Modules/TreeViewer/treeviewermodule.h b/Modules/TreeViewer/treeviewermodule.h
index fdcd4bf5..7d9c2133 100644
--- a/Modules/TreeViewer/treeviewermodule.h
+++ b/Modules/TreeViewer/treeviewermodule.h
@@ -2,6 +2,11 @@
 #define TREEVIEWERMODULE_H
 
 #include "Modules/DefaultModule/defaultmodule.h"
+#include "Core/xmlobject.h"
+#include "Core/modulemessage.h"
+
+class QTreeWidget;
+class TreeViewerItem;
 
 namespace Ui {
 class TreeViewerModule;
@@ -20,8 +25,28 @@ public:
     XmlObject toXmlObject() override;
     void fromXmlObject(const XmlObject &xmlObject) override;
 
+protected:
+    void setupUI();
+    QTreeWidget *tree();
+
+    void onMsgReceived(const ModuleMessage &msg);
+    void addItem(TreeViewerItem* rootItem);
+    void addItem(TreeViewerItem *parentItem, TreeViewerItem* childItem);
+    void showXmlTree(const XmlObject &xml);
+    void addXmlItem(const XmlObject &xml, TreeViewerItem *parent);
+    TreeViewerItem* createTreeItemFromXml(const XmlObject &xml);
+    void updateHeaders(const QList<QString> itemAttributes);
+    void updateItemAttributesColumn();
+    void setReceivingTopic(const QString &txt);
+    void onLineEditTopicTextChanged(const QString &txt);
+    void clearTree();
+
 private:
     Ui::TreeViewerModule *ui;
+    QList<TreeViewerItem*> items;
+    QList<QString> headers;
+    QString receivingTopic = SkywardHubStrings::treeViewerReceivingBaseTopic;
 };
 
+
 #endif // TREEVIEWERMODULE_H
diff --git a/Modules/TreeViewer/treeviewermodule.ui b/Modules/TreeViewer/treeviewermodule.ui
index c36baefa..77bbe0df 100644
--- a/Modules/TreeViewer/treeviewermodule.ui
+++ b/Modules/TreeViewer/treeviewermodule.ui
@@ -1,9 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <ui version="4.0">
- <author/>
- <comment/>
- <exportmacro/>
  <class>TreeViewerModule</class>
- <widget name="TreeViewerModule" class="QWidget">
+ <widget class="QWidget" name="TreeViewerModule">
   <property name="geometry">
    <rect>
     <x>0</x>
@@ -15,7 +13,32 @@
   <property name="windowTitle">
    <string>Form</string>
   </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTreeWidget" name="treeWidget">
+     <column>
+      <property name="text">
+       <string notr="true">1</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Receiving Topic</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="lineEdit_receivingTopic"/>
+     </item>
+    </layout>
+   </item>
+  </layout>
  </widget>
- <pixmapfunction/>
+ <resources/>
  <connections/>
 </ui>
diff --git a/Modules/moduleslist.cpp b/Modules/moduleslist.cpp
index 26b63a01..7885ea3c 100644
--- a/Modules/moduleslist.cpp
+++ b/Modules/moduleslist.cpp
@@ -121,7 +121,6 @@ void ModulesList::createModuleList()
     treeViewer.addModuleSourceFiles("Modules/TreeViewer/");
     addModuleInfo(treeViewer);
     #endif
-
 }
 
 
diff --git a/Modules/skywardhubstrings.cpp b/Modules/skywardhubstrings.cpp
index d05c98ca..9c12891b 100644
--- a/Modules/skywardhubstrings.cpp
+++ b/Modules/skywardhubstrings.cpp
@@ -27,6 +27,7 @@ const QString SkywardHubStrings::imageViewerInfo = "Dobule click on a label to o
 // Topics
 const QString SkywardHubStrings::commandsTopic = "TelemetryCommand";
 const QString SkywardHubStrings::telemetryRequestTopic = "TelemetryRequest";
+const QString SkywardHubStrings::treeViewerReceivingBaseTopic = "TreeViewer";
 
 const QString SkywardHubStrings::logCommandsTopic = "LogCommands";
 const QString SkywardHubStrings::raw_event_id = "event_id";
diff --git a/Modules/skywardhubstrings.h b/Modules/skywardhubstrings.h
index fd394387..562e7139 100644
--- a/Modules/skywardhubstrings.h
+++ b/Modules/skywardhubstrings.h
@@ -33,6 +33,7 @@ public:
     // Topics
     static const QString commandsTopic;
     static const QString telemetryRequestTopic;
+    static const QString treeViewerReceivingBaseTopic;
 
     static const QString logCommandsTopic;
     static const QString raw_event_id;
diff --git a/SkywardHub.pro b/SkywardHub.pro
index 89fb8906..c0364dbf 100644
--- a/SkywardHub.pro
+++ b/SkywardHub.pro
@@ -55,6 +55,7 @@ SOURCES += \
     Modules/Splitter/splittermodule.cpp \
     Modules/Table/tablemodule.cpp \
     Modules/Test/testmodule.cpp \
+    Modules/TreeViewer/treevieweritem.cpp \
     Modules/TreeViewer/treeviewermodule.cpp \
     Modules/Utility/contextmenuseparator.cpp \
     Modules/Utility/modulespicker.cpp \
@@ -113,6 +114,7 @@ HEADERS += \
     Modules/Splitter/splittermodule.h \
     Modules/Table/tablemodule.h \
     Modules/Test/testmodule.h \
+    Modules/TreeViewer/treevieweritem.h \
     Modules/TreeViewer/treeviewermodule.h \
     Modules/Utility/contextmenuseparator.h \
     Modules/Utility/modulespicker.h \
-- 
GitLab