From 1dfa11061499299ad5c70ba3e5c2d0a249919d63 Mon Sep 17 00:00:00 2001
From: Joseph Mirabel <jmirabel@laas.fr>
Date: Wed, 23 May 2018 11:42:24 +0200
Subject: [PATCH] In BodyTreeWidget, show properties of nodes.

---
 include/gepetto/gui/bodytreewidget.hh |   8 +-
 include/gepetto/gui/tree-item.hh      |  23 +++-
 include/gepetto/gui/ui/mainwindow.ui  |  32 +----
 src/gui/bodytreewidget.cc             |  30 +++--
 src/gui/mainwindow.cc                 |   2 +-
 src/gui/tree-item.cc                  | 177 ++++++++++++++++++++++----
 src/gui/windows-manager.cc            |   2 +
 7 files changed, 200 insertions(+), 74 deletions(-)

diff --git a/include/gepetto/gui/bodytreewidget.hh b/include/gepetto/gui/bodytreewidget.hh
index be9c6f9..20b0bcc 100644
--- a/include/gepetto/gui/bodytreewidget.hh
+++ b/include/gepetto/gui/bodytreewidget.hh
@@ -58,8 +58,8 @@ namespace gepetto {
 
       /// Init the widget.
       /// \param view tree view to display.
-      /// \param toolBox menu in the window
-      void init(QTreeView *view, QToolBox* toolBox);
+      /// \param propertyArea menu in the window
+      void init(QTreeView *view, QWidget *propertyArea);
 
       virtual ~BodyTreeWidget () {}
 
@@ -132,10 +132,12 @@ namespace gepetto {
       /// is updated.
       void handleSelectionEvent (const SelectionEvent* event);
 
+      void updatePropertyArea (BodyTreeItem* item);
+
       QTreeView* view_;
       QStandardItemModel* model_;
       WindowsManagerPtr_t osg_;
-      QToolBox* toolBox_;
+      QWidget* propertyArea_;
     };
   }
 }
diff --git a/include/gepetto/gui/tree-item.hh b/include/gepetto/gui/tree-item.hh
index af0cc5a..2f40e41 100644
--- a/include/gepetto/gui/tree-item.hh
+++ b/include/gepetto/gui/tree-item.hh
@@ -36,6 +36,7 @@ namespace gepetto {
 
       public:
         BodyTreeItem (QObject* parent, graphics::NodePtr_t node);
+        void initialize();
 
         virtual QStandardItem* clone () const;
 
@@ -49,7 +50,12 @@ namespace gepetto {
 
         void setParentGroup (const std::string& parent);
 
-        virtual ~BodyTreeItem() {};
+        QWidget* propertyEditors () const
+        {
+          return propertyEditors_;
+        }
+
+        virtual ~BodyTreeItem();
 
       public:
         void attachToWindow (unsigned int windowID);
@@ -64,12 +70,23 @@ namespace gepetto {
         void deleteLandmark ();
         QString text () const { return QStandardItem::text(); }
 
+    signals:
+        void requestInitialize();
+    private slots:
+        void doInitialize();
+
+        void setBoolProperty (bool value) const;
+        void setIntProperty (int value) const;
+        void setStringProperty (const QString& value) const;
+        void setFloatProperty (const double& value) const;
+
       private:
+        template <typename T> void setProperty(const QObject* sender, const T& value) const;
+
         graphics::NodePtr_t node_;
         std::string parentGroup_;
 
-        QSignalMapper vmMapper_;
-        QSignalMapper vizMapper_;
+        QWidget* propertyEditors_;
 
         friend class VisibilityItem;
     };
diff --git a/include/gepetto/gui/ui/mainwindow.ui b/include/gepetto/gui/ui/mainwindow.ui
index 5fafe64..605eaed 100644
--- a/include/gepetto/gui/ui/mainwindow.ui
+++ b/include/gepetto/gui/ui/mainwindow.ui
@@ -37,7 +37,6 @@
    <property name="styleSheet">
     <string notr="true">image: url(:/img/gepetto.png);</string>
    </property>
-   <layout class="QVBoxLayout" name="verticalLayout_5"/>
   </widget>
   <widget class="QMenuBar" name="menuBar">
    <property name="geometry">
@@ -45,7 +44,7 @@
      <x>0</x>
      <y>0</y>
      <width>754</width>
-     <height>20</height>
+     <height>27</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuFile">
@@ -146,38 +145,15 @@
         <property name="widgetResizable">
          <bool>true</bool>
         </property>
-        <widget class="QWidget" name="scrollAreaWidgetContents">
+        <widget class="QWidget" name="propertyArea">
          <property name="geometry">
           <rect>
            <x>0</x>
            <y>0</y>
-           <width>254</width>
-           <height>127</height>
+           <width>252</width>
+           <height>133</height>
           </rect>
          </property>
-         <layout class="QVBoxLayout" name="verticalLayout_2">
-          <item>
-           <widget class="QToolBox" name="toolBox">
-            <property name="currentIndex">
-             <number>0</number>
-            </property>
-            <widget class="QWidget" name="Info">
-             <property name="geometry">
-              <rect>
-               <x>0</x>
-               <y>0</y>
-               <width>238</width>
-               <height>83</height>
-              </rect>
-             </property>
-             <attribute name="label">
-              <string>Transparency</string>
-             </attribute>
-             <layout class="QHBoxLayout" name="horizontalLayout"/>
-            </widget>
-           </widget>
-          </item>
-         </layout>
         </widget>
        </widget>
       </widget>
diff --git a/src/gui/bodytreewidget.cc b/src/gui/bodytreewidget.cc
index dd62091..1acd499 100644
--- a/src/gui/bodytreewidget.cc
+++ b/src/gui/bodytreewidget.cc
@@ -82,12 +82,12 @@ static void addSlider (QToolBox* tb, QString title, QObject* receiver, const cha
 
 namespace gepetto {
   namespace gui {
-    void BodyTreeWidget::init(QTreeView* view, QToolBox *toolBox)
+    void BodyTreeWidget::init(QTreeView* view, QWidget *propertyArea)
     {
       MainWindow* main = MainWindow::instance();
       osg_ = main->osg();
       view_ = view;
-      toolBox_ = toolBox;
+      propertyArea_ = propertyArea;
       model_  = new QStandardItemModel (this);
       view_->setModel(model_);
       view_->setSelectionMode(QAbstractItemView::ExtendedSelection);
@@ -97,18 +97,12 @@ namespace gepetto {
           SIGNAL (currentChanged(QModelIndex,QModelIndex)),
           SLOT (currentChanged(QModelIndex,QModelIndex)));
 
-      toolBox_->removeItem(0);
+/*
       addSlider(toolBox_, "Transparency", this, SLOT(setTransparency(int)));
-      addSelector (toolBox_, "Visibility",
-                   QStringList () << "On" << "Always on top" << "Off",
-                   QStringList () << "ON" << "ALWAYS_ON_TOP" << "OFF",
-                   this, SLOT(setVisibilityMode(QString)));
-      addSelector (toolBox_, "Wireframe mode",
-                   QStringList () << "Fill" << "Both" << "Wireframe",
-                   QStringList () << "FILL" << "FILL_AND_WIREFRAME" << "WIREFRAME",
-                   this, SLOT(setWireFrameMode(QString)));
       addColorSelector(toolBox_, "Color", this, SLOT(setColor(QColor)));
       addSlider(toolBox_, "Scale", this, SLOT(setScale(int)));
+*/
+      propertyArea_->setLayout (new QVBoxLayout);
     }
 
     QTreeView* BodyTreeWidget::view ()
@@ -177,6 +171,19 @@ namespace gepetto {
       event->done();
     }
 
+    void BodyTreeWidget::updatePropertyArea (BodyTreeItem* item)
+    {
+      QLayoutItem *child;
+      while ((child = propertyArea_->layout()->takeAt(0)) != 0) {
+        if (child->widget() != NULL) {
+          child->widget()->setParent(NULL);
+        }
+      }
+      if (item != NULL) {
+        propertyArea_->layout()->addWidget(item->propertyEditors());
+      }
+    }
+
     void BodyTreeWidget::currentChanged (const QModelIndex &current,
         const QModelIndex &/*previous*/)
     {
@@ -188,6 +195,7 @@ namespace gepetto {
           qobject_cast <const QStandardItemModel*>
           (view_->model())->itemFromIndex(current)
          );
+      updatePropertyArea(item);
       if (item) {
         SelectionEvent *event = new SelectionEvent(SelectionEvent::FromBodyTree, item->node(), QApplication::keyboardModifiers());
         emitBodySelected(event);
diff --git a/src/gui/mainwindow.cc b/src/gui/mainwindow.cc
index 6907c93..dfb3676 100644
--- a/src/gui/mainwindow.cc
+++ b/src/gui/mainwindow.cc
@@ -61,7 +61,7 @@ namespace gepetto {
 
       // Setup the body tree view
       osgViewerManagers_ = WindowsManager::create(ui_->bodyTreeContent);
-      ui_->bodyTreeContent->init(ui_->bodyTree, ui_->toolBox);
+      ui_->bodyTreeContent->init(ui_->bodyTree, ui_->propertyArea);
 
       if (settings_->startGepettoCorbaServer) {
         osgServer_ = new CorbaServer (new ViewerServerProcess (
diff --git a/src/gui/tree-item.cc b/src/gui/tree-item.cc
index 47031ec..0ac9781 100644
--- a/src/gui/tree-item.cc
+++ b/src/gui/tree-item.cc
@@ -26,21 +26,164 @@
 
 namespace gepetto {
   namespace gui {
+    QWidget* boolPropertyEditor (BodyTreeItem* bti, const graphics::PropertyPtr_t prop)
+    {
+      QCheckBox* cb = new QCheckBox;
+      bool value;
+      /* bool success = */ prop->get(value);
+      cb->setChecked(value);
+      if (prop->hasWriteAccess())
+        bti->connect(cb, SIGNAL(toggled(bool)), SLOT(setBoolProperty(bool)));
+      else
+        cb->setEnabled(false);
+      return cb;
+    }
+
+    QWidget* enumPropertyEditor (BodyTreeItem* bti, const graphics::PropertyPtr_t prop)
+    {
+      const graphics::EnumProperty::Ptr_t enumProp = boost::dynamic_pointer_cast<graphics::EnumProperty> (prop);
+      const graphics::MetaEnum* enumMeta = enumProp->metaEnum();
+
+      QComboBox* cb = new QComboBox;
+      int value;
+      /* bool success = */ enumProp->get(value);
+      int indexSelected = 0;
+      for (std::size_t i = 0; i < enumMeta->values.size(); ++i)
+      {
+        qDebug() << enumMeta->names[i].c_str() << ":" << enumMeta->values[i];
+        cb->addItem(enumMeta->names[i].c_str(), enumMeta->values[i]);
+        if (value == enumMeta->values[i]) indexSelected = i;
+      }
+      cb->setCurrentIndex(indexSelected);
+      if (prop->hasWriteAccess())
+        bti->connect(cb, SIGNAL(currentIndexChanged(int)), SLOT(setIntProperty(int)));
+      else
+        cb->setEnabled(false);
+      return cb;
+    }
+
+    QWidget* stringPropertyEditor (BodyTreeItem* bti, const graphics::PropertyPtr_t prop)
+    {
+      QLineEdit* le = new QLineEdit;
+      std::string value;
+      /* bool success = */ prop->get(value);
+      le->setText(QString::fromStdString(value));
+      if (prop->hasWriteAccess())
+        bti->connect(le, SIGNAL(textChanged(QString)), SLOT(setStringProperty(QString)));
+      else
+        le->setReadOnly(true);
+      return le;
+    }
+
+    QWidget* floatPropertyEditor (BodyTreeItem* bti, const graphics::PropertyPtr_t prop)
+    {
+      QDoubleSpinBox* dsb = new QDoubleSpinBox;
+      float value;
+      /* bool success = */ prop->get(value);
+      dsb->setValue(value);
+      if (prop->hasWriteAccess())
+        bti->connect(dsb, SIGNAL(valueChanged(double)), SLOT(setFloatProperty(double)));
+      else
+        dsb->setEnabled(false);
+      return dsb;
+    }
+
     BodyTreeItem::BodyTreeItem(QObject *parent, graphics::NodePtr_t node) :
       QObject (parent),
       QStandardItem (QString (node->getID().c_str())),
-      node_ (node),
-      vmMapper_ (),
-      vizMapper_ ()
+      node_ (node)
     {
       setEditable(false);
-      connect (&vmMapper_, SIGNAL (mapped (QString)), SLOT(setViewingMode(QString)));
-      connect (&vizMapper_, SIGNAL (mapped (QString)), SLOT(setVisibilityMode(QString)));
-      
+
       const std::string & name = node->getID();
       QStandardItem::setText(name.substr(name.find_last_of("/") + 1).c_str());
     }
 
+    void BodyTreeItem::initialize ()
+    {
+      connect(this, SIGNAL(requestInitialize()), SLOT(doInitialize()));
+      emit requestInitialize();
+    }
+
+    void BodyTreeItem::doInitialize ()
+    {
+      propertyEditors_ = new QWidget();
+      BodyTreeWidget* bt = MainWindow::instance()->bodyTree();
+      if (propertyEditors_->thread() != bt->thread())
+        propertyEditors_->moveToThread(bt->thread());
+      QFormLayout* l = new QFormLayout(propertyEditors_);
+
+      l->addRow("Node name:", new QLabel (node_->getID().c_str()));
+
+      const graphics::PropertyMap_t& props = node_->properties();
+      for (graphics::PropertyMap_t::const_iterator _prop = props.begin();
+           _prop != props.end(); ++_prop)
+      {
+        const graphics::PropertyPtr_t prop = _prop->second;
+        if (!prop->hasReadAccess()) continue;
+
+        QString name = _prop->first.c_str();
+        QWidget* field = NULL;
+        if (prop->type() == "enum") {
+          field = enumPropertyEditor(this, prop);
+        } else if (prop->type() == "bool") {
+          field = boolPropertyEditor(this, prop);
+        } else if (prop->type() == "string") {
+          field = stringPropertyEditor(this, prop);
+        } else if (prop->type() == "float") {
+          field = floatPropertyEditor(this, prop);
+        } else {
+          qDebug() << "Unhandled property" << name << "of type" << prop->type().c_str() << ".";
+        }
+        if (field != NULL) {
+          field->setProperty("propertyName", name);
+          l->addRow(name + ':', field);
+        }
+      }
+      disconnect(SIGNAL(requestInitialize()));
+    }
+
+    template <typename T>
+    void BodyTreeItem::setProperty (const QObject* sender, const T& value) const
+    {
+      if (sender != NULL) {
+        QVariant nameVariant = sender->property("propertyName");
+        if (nameVariant.isValid()) {
+          qDebug() << "Set property" << nameVariant;
+          std::string name = nameVariant.toString().toStdString();
+          boost::mutex::scoped_lock lock (MainWindow::instance()->osg()->osgFrameMutex());
+          node_->setProperty<T>(name, value);
+        }
+      }
+    }
+
+    void BodyTreeItem::setBoolProperty (bool value) const
+    {
+      setProperty (QObject::sender(), value);
+    }
+
+    void BodyTreeItem::setIntProperty (int value) const
+    {
+      setProperty (QObject::sender(), value);
+    }
+
+    void BodyTreeItem::setStringProperty (const QString& value) const
+    {
+      setProperty (QObject::sender(), value.toStdString());
+    }
+
+    void BodyTreeItem::setFloatProperty (const double& value) const
+    {
+      qDebug() << "Set property" << float(value);
+      setProperty (QObject::sender(), float(value));
+    }
+
+    BodyTreeItem::~BodyTreeItem()
+    {
+      if (propertyEditors_->parent() != NULL)
+        delete propertyEditors_;
+    }
+
     QStandardItem* BodyTreeItem::clone() const
     {
       return new BodyTreeItem (QObject::parent(), node_);
@@ -68,28 +211,6 @@ namespace gepetto {
         QAction* rfg = contextMenu->addAction (tr("Remove from group"));
         connect (rfg, SIGNAL (triggered()), SLOT (removeFromGroup ()));
       }
-      /// Viewing mode
-      QMenu* viewmode = contextMenu->addMenu(tr("Viewing mode"));
-      QAction* f  = viewmode->addAction ("Fill");
-      QAction* w  = viewmode->addAction ("Wireframe");
-      QAction* fw = viewmode->addAction ("Fill and Wireframe");
-      vmMapper_.setMapping (f , QString ("FILL"));
-      vmMapper_.setMapping (w , QString ("WIREFRAME"));
-      vmMapper_.setMapping (fw, QString ("FILL_AND_WIREFRAME"));
-      connect (f , SIGNAL(triggered()), &vmMapper_, SLOT (map()));
-      connect (w , SIGNAL(triggered()), &vmMapper_, SLOT (map()));
-      connect (fw, SIGNAL(triggered()), &vmMapper_, SLOT (map()));
-      /// Visibility mode
-      QMenu* vizmode = contextMenu->addMenu(tr("Visibility mode"));
-      QAction* on  = vizmode->addAction ("On");
-      QAction* aot = vizmode->addAction ("Always on top");
-      QAction* off = vizmode->addAction ("Off");
-      vizMapper_.setMapping (on , QString ("ON"));
-      vizMapper_.setMapping (aot, QString ("ALWAYS_ON_TOP"));
-      vizMapper_.setMapping (off, QString ("OFF"));
-      connect (on , SIGNAL(triggered()), &vizMapper_, SLOT (map()));
-      connect (aot, SIGNAL(triggered()), &vizMapper_, SLOT (map()));
-      connect (off, SIGNAL(triggered()), &vizMapper_, SLOT (map()));
     }
 
     void BodyTreeItem::setParentGroup(const std::string &parent)
diff --git a/src/gui/windows-manager.cc b/src/gui/windows-manager.cc
index 7e1b0f0..7d3dac2 100644
--- a/src/gui/windows-manager.cc
+++ b/src/gui/windows-manager.cc
@@ -73,6 +73,7 @@ namespace gepetto {
         nodeItemMap_[groupName].second = true;
         if (bti->thread() != bodyTree_->thread())
           bti->moveToThread(bodyTree_->thread());
+        bti->initialize();
         bodyTree_->model()->appendRow(bti);
       }
     }
@@ -87,6 +88,7 @@ namespace gepetto {
         bti->setParentGroup(groupName);
         if (bti->thread() != bodyTree_->thread())
           bti->moveToThread(bodyTree_->thread());
+        bti->initialize();
         groups[i]->appendRow(bti);
       }
     }
-- 
GitLab