From c65338f3d7965ccfe145cbac9ebaa4342d8d51df Mon Sep 17 00:00:00 2001
From: Gennady Pospelov <g.pospelov@fz-juelich.de>
Date: Wed, 4 Feb 2015 18:11:26 +0100
Subject: [PATCH] New UniversalPropertyEditor to replace all scattered property
 editors

---
 GUI/coregui/Models/NJobItem.cpp               |  24 +-
 GUI/coregui/Models/NJobItem.h                 |   4 +-
 GUI/coregui/Models/NJobModel.cpp              |  13 +-
 GUI/coregui/Models/PropertyAttribute.h        |   3 +-
 .../JobQueueWidgets/JobPropertiesWidget.cpp   | 237 ++++++++++----
 .../JobQueueWidgets/JobPropertiesWidget.h     |  30 +-
 .../UniversalPropertyEditor.cpp               | 295 ++++++++++++++++++
 .../JobQueueWidgets/UniversalPropertyEditor.h |  95 ++++++
 8 files changed, 609 insertions(+), 92 deletions(-)
 create mode 100644 GUI/coregui/Views/Components/JobQueueWidgets/UniversalPropertyEditor.cpp
 create mode 100644 GUI/coregui/Views/Components/JobQueueWidgets/UniversalPropertyEditor.h

diff --git a/GUI/coregui/Models/NJobItem.cpp b/GUI/coregui/Models/NJobItem.cpp
index 427113002cd..e19f4ae4a9d 100644
--- a/GUI/coregui/Models/NJobItem.cpp
+++ b/GUI/coregui/Models/NJobItem.cpp
@@ -37,8 +37,8 @@ QMap<QString, QString> NJobItem::m_run_policies = initializeRunPolicies();
 
 
 const QString NJobItem::P_IDENTIFIER = "Identifier";
-const QString NJobItem::P_BEGIN_TYPE = "Begin Time";
-const QString NJobItem::P_END_TYPE = "End Time";
+const QString NJobItem::P_BEGIN_TIME = "Begin Time";
+const QString NJobItem::P_END_TIME = "End Time";
 const QString NJobItem::P_COMMENTS = "Comments";
 const QString NJobItem::P_STATUS = "Status";
 const QString NJobItem::P_PROGRESS = "Progress";
@@ -52,25 +52,29 @@ NJobItem::NJobItem(ParameterizedItem *parent)
     , m_instrumentModel(0)
 {
     setItemName(Constants::JobItemType);
-    registerProperty(P_IDENTIFIER, QString());
-    registerProperty(P_BEGIN_TYPE, QString());
-    registerProperty(P_END_TYPE, QString());
-    registerProperty(P_COMMENTS, QString());
+    registerProperty(P_IDENTIFIER, QString(), PropertyAttribute(PropertyAttribute::HIDDEN));
 
     ComboProperty status;
     status << Constants::STATUS_IDLE << Constants::STATUS_RUNNING << Constants::STATUS_COMPLETED
            << Constants::STATUS_CANCELED << Constants::STATUS_FAILED;
-    registerProperty(P_STATUS, status.getVariant());
+    registerProperty(P_STATUS, status.getVariant(), PropertyAttribute(PropertyAttribute::READONLY));
 
-    registerProperty(P_PROGRESS, 0);
-    registerProperty(P_NTHREADS, -1);
+    registerProperty(P_BEGIN_TIME, QString(), PropertyAttribute(PropertyAttribute::READONLY));
+    registerProperty(P_END_TIME, QString(), PropertyAttribute(PropertyAttribute::READONLY));
+    registerProperty(P_COMMENTS, QString(), PropertyAttribute(PropertyAttribute::HIDDEN));
+
+    registerProperty(P_PROGRESS, 0, PropertyAttribute(PropertyAttribute::HIDDEN));
+    registerProperty(P_NTHREADS, -1, PropertyAttribute(PropertyAttribute::HIDDEN));
 
     ComboProperty policy;
     policy << QString("Immediately") << QString("In background") << QString("Submit only");
-    registerProperty(P_RUN_POLICY, policy.getVariant());
+    registerProperty(P_RUN_POLICY, policy.getVariant(), PropertyAttribute(PropertyAttribute::HIDDEN));
 
     addToValidChildren(Constants::IntensityDataType);
 
+    setPropertyAppearance(ParameterizedItem::P_NAME, PropertyAttribute::VISIBLE);
+
+
 }
 
 
diff --git a/GUI/coregui/Models/NJobItem.h b/GUI/coregui/Models/NJobItem.h
index 6363f4dc50a..981a6884c07 100644
--- a/GUI/coregui/Models/NJobItem.h
+++ b/GUI/coregui/Models/NJobItem.h
@@ -26,8 +26,8 @@ class BA_CORE_API_ NJobItem : public ParameterizedItem
     Q_OBJECT
 public:
     static const QString P_IDENTIFIER;
-    static const QString P_BEGIN_TYPE;
-    static const QString P_END_TYPE;
+    static const QString P_BEGIN_TIME;
+    static const QString P_END_TIME;
     static const QString P_COMMENTS;
     static const QString P_STATUS;
     static const QString P_PROGRESS;
diff --git a/GUI/coregui/Models/NJobModel.cpp b/GUI/coregui/Models/NJobModel.cpp
index a90ae570e79..4e09f7a7223 100644
--- a/GUI/coregui/Models/NJobModel.cpp
+++ b/GUI/coregui/Models/NJobModel.cpp
@@ -101,10 +101,17 @@ void NJobModel::onSelectionChanged(const QItemSelection &selected, const QItemSe
 {
     Q_UNUSED(deselected);
     qDebug() << "NJobModel::onSelectionChanged" << selected;
-    if(!selected.empty() &&  !selected.first().indexes().empty()) {
-        QModelIndex index = selected.first().indexes().at(0);
-        emit selectionChanged(getJobItemForIndex(index));
+    QModelIndexList indices = selected.indexes();
+    if(indices.size()) {
+        emit selectionChanged(getJobItemForIndex(indices.back()));
+    } else {
+        emit selectionChanged(0);
     }
+
+//    if(!selected.empty() &&  !selected.first().indexes().empty()) {
+//        QModelIndex index = selected.first().indexes().at(0);
+//        emit selectionChanged(getJobItemForIndex(index));
+//    }
 }
 
 //! generates job name
diff --git a/GUI/coregui/Models/PropertyAttribute.h b/GUI/coregui/Models/PropertyAttribute.h
index 5298a2cd2a9..7b1ba056124 100644
--- a/GUI/coregui/Models/PropertyAttribute.h
+++ b/GUI/coregui/Models/PropertyAttribute.h
@@ -30,7 +30,8 @@ public:
     enum EAppearance {
         VISIBLE = 0x0000,
         HIDDEN = 0x0001,
-        DISABLED = 0x0002
+        DISABLED = 0x0002,
+        READONLY = 0x0004
     };
 
     explicit PropertyAttribute(EAppearance appearance=VISIBLE, const AttLimits &limits=AttLimits::lowerLimited(0.0), int decimals=2, const QString &label = QString())
diff --git a/GUI/coregui/Views/Components/JobQueueWidgets/JobPropertiesWidget.cpp b/GUI/coregui/Views/Components/JobQueueWidgets/JobPropertiesWidget.cpp
index 1592fafbf99..a5a9608a8e6 100644
--- a/GUI/coregui/Views/Components/JobQueueWidgets/JobPropertiesWidget.cpp
+++ b/GUI/coregui/Views/Components/JobQueueWidgets/JobPropertiesWidget.cpp
@@ -14,6 +14,7 @@
 // ************************************************************************** //
 
 #include "JobPropertiesWidget.h"
+#include "UniversalPropertyEditor.h"
 //#include "JobQueueModel.h"
 //#include "JobItem.h"
 #include "NJobModel.h"
@@ -31,10 +32,10 @@
 JobPropertiesWidget::JobPropertiesWidget(QWidget *parent)
     : QWidget(parent)
     , m_jobModel(0)
-    , m_variantManager(new QtVariantPropertyManager(this))
-    , m_propertyBrowser(new QtTreePropertyBrowser(this))
     , m_currentItem(0)
     , m_tabWidget(new QTabWidget)
+    , m_propertyEditor(0)
+    , m_commentsEditor(0)
 {
 //    setMinimumSize(128, 128);
     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
@@ -42,18 +43,21 @@ JobPropertiesWidget::JobPropertiesWidget(QWidget *parent)
     setObjectName(QLatin1String("Job Properties"));
 //    setStyleSheet("background-color:white;");
 
-    m_variantManager = new QtVariantPropertyManager(this);
-    m_readonlyManager = new QtVariantPropertyManager(this);
-    connect(m_variantManager, SIGNAL(valueChanged(QtProperty *, const QVariant &)),
-                this, SLOT(valueChanged(QtProperty *, const QVariant &)));
+//    m_variantManager = new QtVariantPropertyManager(this);
+//    m_readonlyManager = new QtVariantPropertyManager(this);
+//    connect(m_variantManager, SIGNAL(valueChanged(QtProperty *, const QVariant &)),
+//                this, SLOT(valueChanged(QtProperty *, const QVariant &)));
 
-    QtVariantEditorFactory *variantFactory = new QtVariantEditorFactory(this);
-    m_propertyBrowser->setFactoryForManager(m_variantManager, variantFactory);
+//    QtVariantEditorFactory *variantFactory = new QtVariantEditorFactory(this);
+//    m_propertyBrowser->setFactoryForManager(m_variantManager, variantFactory);
 
     QVBoxLayout *mainLayout = new QVBoxLayout;
     mainLayout->setMargin(0);
     mainLayout->setSpacing(0);
 
+
+    m_propertyEditor = new UniversalPropertyEditor(0, this);
+
     m_commentsEditor = new QTextEdit();
 
     QWidget *commentsWidget = new QWidget();
@@ -63,7 +67,7 @@ JobPropertiesWidget::JobPropertiesWidget(QWidget *parent)
     commentsWidget->setLayout(vlayout);
 
     m_tabWidget->setTabPosition(QTabWidget::South);
-    m_tabWidget->insertTab(JOB_PROPERTIES, m_propertyBrowser, "Job Properties");
+    m_tabWidget->insertTab(JOB_PROPERTIES, m_propertyEditor, "Job Properties");
     m_tabWidget->insertTab(JOB_COMMENTS, commentsWidget, "Details");
 
     mainLayout->addWidget(m_tabWidget);
@@ -77,35 +81,142 @@ void JobPropertiesWidget::setModel(NJobModel *model)
 {
     Q_ASSERT(model);
     if(model != m_jobModel) {
+        if(m_jobModel)
+            disconnect(m_jobModel,
+                SIGNAL( selectionChanged(NJobItem *) ),
+                this,
+                SLOT( setItem(NJobItem *) )
+                );
+
         m_jobModel = model;
         connect(m_jobModel,
             SIGNAL( selectionChanged(NJobItem *) ),
             this,
-            SLOT( itemClicked(NJobItem *) )
+            SLOT( setItem(NJobItem *) )
             );
 
-        connect(m_jobModel, SIGNAL(dataChanged(QModelIndex, QModelIndex))
-                , this, SLOT(dataChanged(QModelIndex, QModelIndex)));
+//        connect(m_jobModel, SIGNAL(dataChanged(QModelIndex, QModelIndex))
+//                , this, SLOT(dataChanged(QModelIndex, QModelIndex)));
     }
-}
 
 
-void JobPropertiesWidget::updateExpandState()
-{
-    QList<QtBrowserItem *> list = m_propertyBrowser->topLevelItems();
-    QListIterator<QtBrowserItem *> it(list);
-    while (it.hasNext()) {
-        QtBrowserItem *item = it.next();
-        QtProperty *prop = item->property();
-        idToExpanded[propertyToId[prop]] = m_propertyBrowser->isExpanded(item);
-    }
 }
 
 
-void JobPropertiesWidget::itemClicked(NJobItem *jobItem)
+//void JobPropertiesWidget::updateExpandState()
+//{
+//    QList<QtBrowserItem *> list = m_propertyBrowser->topLevelItems();
+//    QListIterator<QtBrowserItem *> it(list);
+//    while (it.hasNext()) {
+//        QtBrowserItem *item = it.next();
+//        QtProperty *prop = item->property();
+//        idToExpanded[propertyToId[prop]] = m_propertyBrowser->isExpanded(item);
+//    }
+//}
+
+
+void JobPropertiesWidget::setItem(NJobItem *jobItem)
 {
     qDebug() << "JobPropertiesWidget::itemClicked" << jobItem->itemName();
-    Q_ASSERT(0);
+
+    m_propertyEditor->setItem(jobItem);
+//    Q_ASSERT(0);
+
+//    if (m_currentItem == jobItem) return;
+
+//    if (m_currentItem) {
+//        clearEditor();
+
+//        disconnect(m_currentItem, SIGNAL(propertyItemChanged(QString)),
+//                this, SLOT(updateSubItems(QString)));
+//    }
+
+//    m_currentItem = jobItem;
+
+//    if (!m_currentItem) return;
+
+//    addItemProperties(m_currentItem);
+//    connect(m_currentItem, SIGNAL(propertyItemChanged(QString)),
+//            this, SLOT(updateSubItems(QString)));
+//    connect(m_currentItem, SIGNAL(propertyChanged(QString)),
+//            this, SLOT(onPropertyChanged(QString)));
+
+}
+
+
+//void JobPropertiesWidget::addItemProperties(const ParameterizedItem *item)
+//{
+//    QString item_type = item->modelType();
+//    QtProperty *item_property = m_manager->addProperty(
+//                QtVariantPropertyManager::groupTypeId(), item_type);
+
+//    addSubProperties(item_property, item);
+//    m_browser->addProperty(item_property);
+//}
+
+
+//void JobPropertiesWidget::addSubProperties(QtProperty *item_property,
+//                                            const ParameterizedItem *item)
+//{
+//    QList<QByteArray> property_names = item->dynamicPropertyNames();
+//    for (int i = 0; i < property_names.length(); ++i) {
+//        QString prop_name = QString(property_names[i]);
+//        PropertyAttribute prop_attribute = item->getPropertyAttribute(prop_name);
+
+//        if(prop_attribute.getAppearance() & PropertyAttribute::HIDDEN) continue;
+
+//        QVariant prop_value = item->property(prop_name.toUtf8().data());
+//        int type = GUIHelpers::getVariantType(prop_value);
+
+//        QtVariantProperty *subProperty = 0;
+//        if (m_manager->isPropertyTypeSupported(type)) {
+
+//            if(prop_attribute.getLabel().isEmpty()) {
+//                subProperty = m_manager->addProperty(type, prop_name);
+//            } else {
+//                subProperty = m_manager->addProperty(type, prop_attribute.getLabel());
+//            }
+
+//            subProperty->setValue(prop_value);
+
+//            if(type == QVariant::Double) {
+//                subProperty->setAttribute(QLatin1String("decimals"), prop_attribute.getDecimals());
+//                 AttLimits limits = prop_attribute.getLimits();
+//                 if(limits.hasLowerLimit()) subProperty->setAttribute(QLatin1String("minimum"), limits.getLowerLimit());
+//                 if(limits.hasUpperLimit()) subProperty->setAttribute(QLatin1String("maximum"), limits.getUpperLimit());
+//            }
+
+//            QString toolTip = ToolTipDataBase::getSampleViewToolTip(item->modelType(), prop_name);
+//            if(!toolTip.isEmpty()) subProperty->setToolTip(toolTip);
+
+//            if(prop_attribute.getAppearance() & PropertyAttribute::DISABLED) {
+//                subProperty->setEnabled(false);
+//            }
+
+//            if (item->getSubItems().contains(prop_name)) {
+//                ParameterizedItem *subitem = item->getSubItems()[prop_name];
+//                if (subitem) {
+//                    addSubProperties(subProperty, subitem);
+//                }
+//            }
+
+//        } else {
+//            subProperty = m_read_only_manager->addProperty(QVariant::String,
+//                                                         prop_name);
+//            subProperty->setValue(QLatin1String("< Unknown Type >"));
+//            subProperty->setEnabled(false);
+//        }
+
+//        item_property->addSubProperty(subProperty);
+//        ParameterizedItem *non_const_item =
+//                const_cast<ParameterizedItem *>(item);
+//        ItemIndexPair item_index_pair(non_const_item, i);
+//        m_property_to_item_index_pair[subProperty] = item_index_pair;
+//        m_item_to_index_to_property[item][i] = subProperty;
+//        m_item_to_propertyname_to_qtvariantproperty[item][prop_name] = subProperty;
+//    }
+//}
+
 
 //    updateExpandState();
 
@@ -158,48 +269,48 @@ void JobPropertiesWidget::itemClicked(NJobItem *jobItem)
 //    }
 //    m_commentsEditor->setText(jobItem->getComments());
 
-}
+//}
 
 
-void JobPropertiesWidget::addProperty(QtVariantProperty *property, const QString &id)
-{
-    propertyToId[property] = id;
-    idToProperty[id] = property;
-    QtBrowserItem *item = m_propertyBrowser->addProperty(property);
-    if (idToExpanded.contains(id))
-        m_propertyBrowser->setExpanded(item, idToExpanded[id]);
-}
+//void JobPropertiesWidget::addProperty(QtVariantProperty *property, const QString &id)
+//{
+//    propertyToId[property] = id;
+//    idToProperty[id] = property;
+//    QtBrowserItem *item = m_propertyBrowser->addProperty(property);
+//    if (idToExpanded.contains(id))
+//        m_propertyBrowser->setExpanded(item, idToExpanded[id]);
+//}
 
 
-void JobPropertiesWidget::valueChanged(QtProperty *property, const QVariant &value)
-{
-    Q_ASSERT(0);
-    Q_UNUSED(property);
-    Q_UNUSED(value);
-//    if (!propertyToId.contains(property))
-//        return;
-
-//    if (!m_currentItem)
-//        return;
-
-//    QString id = propertyToId[property];
-//    if (id == JobQueueXML::JobNameAttribute) {
-//        m_currentItem->setName(value.value<QString>());
-//    }
-}
+//void JobPropertiesWidget::valueChanged(QtProperty *property, const QVariant &value)
+//{
+//    Q_ASSERT(0);
+//    Q_UNUSED(property);
+//    Q_UNUSED(value);
+////    if (!propertyToId.contains(property))
+////        return;
 
+////    if (!m_currentItem)
+////        return;
 
-//! to update properties of currently selected item if they were changed from outside
-void JobPropertiesWidget::dataChanged(const QModelIndex &index, const QModelIndex &)
-{
-    qDebug() << "JobPropertiesWidget::dataChanged(const QModelIndex &index, const QModelIndex &)";
-//    Q_UNUSED(index);
-    NJobItem *jobItem = m_jobModel->getJobItemForIndex(index);
-    if(jobItem == m_currentItem) {
-          Q_ASSERT(0);
-//        idToProperty[JobQueueXML::JobNameAttribute]->setValue(jobItem->getName());
-//        idToProperty[JobQueueXML::JobStatusAttribute]->setValue(jobItem->getStatusString());
-//        idToProperty[JobQueueXML::JobBeginTimeAttribute]->setValue(jobItem->getBeginTime());
-//        idToProperty[JobQueueXML::JobEndTimeAttribute]->setValue(jobItem->getEndTime());
-    }
-}
+////    QString id = propertyToId[property];
+////    if (id == JobQueueXML::JobNameAttribute) {
+////        m_currentItem->setName(value.value<QString>());
+////    }
+//}
+
+
+////! to update properties of currently selected item if they were changed from outside
+//void JobPropertiesWidget::dataChanged(const QModelIndex &index, const QModelIndex &)
+//{
+//    qDebug() << "JobPropertiesWidget::dataChanged(const QModelIndex &index, const QModelIndex &)";
+////    Q_UNUSED(index);
+//    NJobItem *jobItem = m_jobModel->getJobItemForIndex(index);
+//    if(jobItem == m_currentItem) {
+//          Q_ASSERT(0);
+////        idToProperty[JobQueueXML::JobNameAttribute]->setValue(jobItem->getName());
+////        idToProperty[JobQueueXML::JobStatusAttribute]->setValue(jobItem->getStatusString());
+////        idToProperty[JobQueueXML::JobBeginTimeAttribute]->setValue(jobItem->getBeginTime());
+////        idToProperty[JobQueueXML::JobEndTimeAttribute]->setValue(jobItem->getEndTime());
+//    }
+//}
diff --git a/GUI/coregui/Views/Components/JobQueueWidgets/JobPropertiesWidget.h b/GUI/coregui/Views/Components/JobQueueWidgets/JobPropertiesWidget.h
index 27138d614f1..639484cf8e6 100644
--- a/GUI/coregui/Views/Components/JobQueueWidgets/JobPropertiesWidget.h
+++ b/GUI/coregui/Views/Components/JobQueueWidgets/JobPropertiesWidget.h
@@ -28,6 +28,8 @@ class QtProperty;
 class QtVariantProperty;
 class QTextEdit;
 class QTabWidget;
+class ParameterizedItem;
+class UniversalPropertyEditor;
 
 //! Widget to show and change properties of currently selected JobItem
 //! Left buttom corner of JobQueueView
@@ -43,29 +45,31 @@ public:
     QSize sizeHint() const { return QSize(64, 256); }
     QSize minimumSizeHint() const { return QSize(64, 64); }
 
-public slots:
-    void itemClicked(NJobItem *item);
-    void dataChanged(const QModelIndex &, const QModelIndex &);
+public slots:    
+    void setItem(NJobItem *item);
+//    void dataChanged(const QModelIndex &, const QModelIndex &);
 
 private slots:
-    void valueChanged(QtProperty *property, const QVariant &value);
+//    void valueChanged(QtProperty *property, const QVariant &value);
 
 private:
-    void updateExpandState();
-    void addProperty(QtVariantProperty *property, const QString &id);
+//    void updateExpandState();
+//    void addProperty(QtVariantProperty *property, const QString &id);
 
 //    JobQueueModel *m_jobQueueModel;
+
+    //! clear editor
+
+
     NJobModel *m_jobModel;
-    class QtVariantPropertyManager *m_variantManager;
-    class QtVariantPropertyManager *m_readonlyManager;
-    class QtTreePropertyBrowser *m_propertyBrowser;
-    QMap<QtProperty *, QString> propertyToId;
-    QMap<QString, QtVariantProperty *> idToProperty;
-    QMap<QString, bool> idToExpanded;
 
-    NJobItem *m_currentItem;
+//    QMap<QtProperty *, QString> propertyToId;
+//    QMap<QString, QtVariantProperty *> idToProperty;
+//    QMap<QString, bool> idToExpanded;
 
+    NJobItem *m_currentItem;
     QTabWidget *m_tabWidget;
+    UniversalPropertyEditor *m_propertyEditor;
     QTextEdit *m_commentsEditor;
 };
 
diff --git a/GUI/coregui/Views/Components/JobQueueWidgets/UniversalPropertyEditor.cpp b/GUI/coregui/Views/Components/JobQueueWidgets/UniversalPropertyEditor.cpp
new file mode 100644
index 00000000000..29642ee9675
--- /dev/null
+++ b/GUI/coregui/Views/Components/JobQueueWidgets/UniversalPropertyEditor.cpp
@@ -0,0 +1,295 @@
+// ************************************************************************** //
+//
+//  BornAgain: simulate and fit scattering at grazing incidence
+//
+//! @file      coregui/Views/Components/JobQueueWidgets/UniversalPropertyEditor.cpp
+//! @brief     Implements class UniversalPropertyEditor
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2015
+//! @authors   Scientific Computing Group at MLZ Garching
+//! @authors   C. Durniak, M. Ganeva, G. Pospelov, W. Van Herck, J. Wuttke
+//
+// ************************************************************************** //
+
+#include "UniversalPropertyEditor.h"
+#include "PropertyVariantManager.h"
+#include "PropertyVariantFactory.h"
+#include "ParameterizedItem.h"
+#include "tooltipdatabase.h"
+#include "GUIHelpers.h"
+
+#include "qttreepropertybrowser.h"
+#include "qtgroupboxpropertybrowser.h"
+#include "qtbuttonpropertybrowser.h"
+
+#include <QtProperty>
+#include <QItemSelectionModel>
+#include <QVBoxLayout>
+#include <QMetaProperty>
+#include <QDebug>
+
+UniversalPropertyEditor::UniversalPropertyEditor(QItemSelectionModel *selection_model,
+                                           QWidget *parent)
+    : QWidget(parent)
+    , m_item(0)
+    , m_selection_model(0)
+{
+    setSelectionModel(selection_model);
+
+    setWindowTitle(QLatin1String("Property Editor"));
+    setObjectName(QLatin1String("PropertyEditor"));
+
+//    QtAbstractPropertyBrowser *browser = new QtGroupBoxPropertyBrowser();
+//    QtAbstractPropertyBrowser *browser = new QtButtonPropertyBrowser();
+
+    QtTreePropertyBrowser *browser = new QtTreePropertyBrowser(this);
+    browser->setRootIsDecorated(false);
+    m_browser = browser;
+    QVBoxLayout *layout = new QVBoxLayout(this);
+    layout->setMargin(0);
+    layout->addWidget(m_browser);
+
+    m_read_only_manager = new PropertyVariantManager(this);
+
+    m_manager = new PropertyVariantManager(this);
+
+    QtVariantEditorFactory *factory = new PropertyVariantFactory();
+    m_browser->setFactoryForManager(m_manager, factory);
+
+    connect(m_manager, SIGNAL(valueChanged(QtProperty *, const QVariant &)),
+                this, SLOT(slotValueChanged(QtProperty *, const QVariant &)));
+
+}
+
+void UniversalPropertyEditor::setSelectionModel(QItemSelectionModel *selection_model)
+{
+    if(selection_model != m_selection_model) {
+        if(m_selection_model)
+            disconnect(m_selection_model,
+                    SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
+                    this,
+                    SLOT(selectionChanged(QItemSelection,QItemSelection)) );
+
+        m_selection_model = selection_model;
+        connect(m_selection_model,
+                SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
+                this,
+                SLOT(selectionChanged(QItemSelection,QItemSelection)) );
+
+    }
+}
+
+// show property of currently selected object (triggered by the graphics scene)
+// if more than one object is selected, show only last selected
+void UniversalPropertyEditor::selectionChanged(const QItemSelection & selected,
+                                            const QItemSelection & deselected)
+{
+    (void)deselected;
+    QModelIndexList indices = selected.indexes();
+    if(indices.size()) {
+        ParameterizedItem *item = static_cast<ParameterizedItem *>(
+                indices.back().internalPointer());
+        setItem(item);
+    } else {
+        setItem(0);
+    }
+}
+
+
+void UniversalPropertyEditor::slotValueChanged(QtProperty *property,
+                                            const QVariant &value)
+{
+    qDebug() << "UniversalPropertyEditor::slotValueChanged()" << value;
+    if (!m_property_to_item_index_pair.contains(property))
+        return;
+
+    ItemIndexPair item_index_pair =
+            m_property_to_item_index_pair.value(property);
+
+    if (item_index_pair.m_item) {
+        QList<QByteArray> prop_list =
+                item_index_pair.m_item->dynamicPropertyNames();
+        if (item_index_pair.m_index > prop_list.length()) {
+            return;
+        }
+        qDebug() << "setting ..." << prop_list[item_index_pair.m_index].constData();
+        item_index_pair.m_item->setProperty(
+            prop_list[item_index_pair.m_index].constData(), value);
+    }
+}
+
+
+void UniversalPropertyEditor::clearEditor()
+{
+    qDebug() << "UniversalPropertyEditor::clearEditor()";
+    //updateExpandState(SaveExpandState);
+
+    QListIterator<QtProperty *> it(m_browser->properties());
+    while (it.hasNext()) {
+        m_browser->removeProperty(it.next());
+    }
+    m_property_to_item_index_pair.clear();
+    m_item_to_index_to_property.clear();
+}
+
+
+void UniversalPropertyEditor::updateSubItems(const QString &name)
+{
+    qDebug() << "UniversalPropertyEditor::updateSubItems()";
+    (void)name;
+    if (!m_item) return;
+
+//    QListIterator<QtProperty *> it(m_browser->properties());
+//    while (it.hasNext()) {
+//        m_browser->removeProperty(it.next());
+//    }
+    clearEditor();
+
+    disconnect(m_item, SIGNAL(propertyItemChanged(QString)),
+               this, SLOT(updateSubItems(QString)));
+    addItemProperties(m_item);
+    connect(m_item, SIGNAL(propertyItemChanged(QString)),
+            this, SLOT(updateSubItems(QString)));
+    connect(m_item, SIGNAL(propertyChanged(QString)),
+            this, SLOT(onPropertyChanged(QString)));
+}
+
+void UniversalPropertyEditor::onPropertyChanged(const QString &property_name)
+{
+    qDebug() << "UniversalPropertyEditor::onPropertyChanged() " << property_name ;
+    if(!m_item) return;
+
+    QtVariantProperty *variant_property = m_item_to_propertyname_to_qtvariantproperty[m_item][property_name];
+    if(variant_property) {
+        QVariant property_value = m_item->getRegisteredProperty(property_name);
+
+        disconnect(m_item, SIGNAL(propertyChanged(QString)),
+               this, SLOT(onPropertyChanged(QString)));
+        disconnect(m_item, SIGNAL(propertyItemChanged(QString)),
+            this, SLOT(updateSubItems(QString)));
+
+        variant_property->setValue(property_value);
+
+        PropertyAttribute prop_attribute = m_item->getPropertyAttribute(property_name);
+        if(prop_attribute.getAppearance() & PropertyAttribute::DISABLED) {
+            variant_property->setEnabled(false);
+        } else {
+            variant_property->setEnabled(true);
+        }
+
+        connect(m_item, SIGNAL(propertyChanged(QString)),
+               this, SLOT(onPropertyChanged(QString)));
+        connect(m_item, SIGNAL(propertyItemChanged(QString)),
+            this, SLOT(updateSubItems(QString)));
+    }
+}
+
+
+// assigns item to the property editor
+void UniversalPropertyEditor::setItem(ParameterizedItem *item)
+{
+    if (m_item == item) return;
+
+    if (m_item) {
+//        QListIterator<QtProperty *> it(m_browser->properties());
+//        while (it.hasNext()) {
+//            m_browser->removeProperty(it.next());
+//        }
+        clearEditor();
+
+        disconnect(m_item, SIGNAL(propertyItemChanged(QString)),
+                this, SLOT(updateSubItems(QString)));
+    }
+
+    m_item = item;
+
+    if (!m_item) return;
+
+    addItemProperties(m_item);
+    connect(m_item, SIGNAL(propertyItemChanged(QString)),
+            this, SLOT(updateSubItems(QString)));
+    connect(m_item, SIGNAL(propertyChanged(QString)),
+            this, SLOT(onPropertyChanged(QString)));
+
+}
+
+
+void UniversalPropertyEditor::addItemProperties(const ParameterizedItem *item)
+{
+    QString item_type = item->modelType();
+    QtProperty *item_property = m_manager->addProperty(
+                QtVariantPropertyManager::groupTypeId(), item_type);
+
+    addSubProperties(item_property, item);
+
+    m_browser->addProperty(item_property);
+}
+
+
+void UniversalPropertyEditor::addSubProperties(QtProperty *item_property,
+                                            const ParameterizedItem *item)
+{
+    QList<QByteArray> property_names = item->dynamicPropertyNames();
+    for (int i = 0; i < property_names.length(); ++i) {
+        QString prop_name = QString(property_names[i]);
+        PropertyAttribute prop_attribute = item->getPropertyAttribute(prop_name);
+
+        if(prop_attribute.getAppearance() & PropertyAttribute::HIDDEN) continue;
+
+        QVariant prop_value = item->property(prop_name.toUtf8().data());
+        int type = GUIHelpers::getVariantType(prop_value);
+
+        QtVariantPropertyManager *manager = m_manager;
+        if(prop_attribute.getAppearance() & PropertyAttribute::READONLY) manager = m_read_only_manager;
+
+        QtVariantProperty *subProperty = 0;
+        if (m_manager->isPropertyTypeSupported(type)) {
+
+            if(prop_attribute.getLabel().isEmpty()) {
+                subProperty = manager->addProperty(type, prop_name);
+            } else {
+                subProperty = manager->addProperty(type, prop_attribute.getLabel());
+            }
+
+            subProperty->setValue(prop_value);
+
+            if(type == QVariant::Double) {
+                subProperty->setAttribute(QLatin1String("decimals"), prop_attribute.getDecimals());
+                 AttLimits limits = prop_attribute.getLimits();
+                 if(limits.hasLowerLimit()) subProperty->setAttribute(QLatin1String("minimum"), limits.getLowerLimit());
+                 if(limits.hasUpperLimit()) subProperty->setAttribute(QLatin1String("maximum"), limits.getUpperLimit());
+            }
+
+            QString toolTip = ToolTipDataBase::getSampleViewToolTip(item->modelType(), prop_name);
+            if(!toolTip.isEmpty()) subProperty->setToolTip(toolTip);
+
+            if(prop_attribute.getAppearance() & PropertyAttribute::DISABLED) {
+                subProperty->setEnabled(false);
+            }
+
+            if (item->getSubItems().contains(prop_name)) {
+                ParameterizedItem *subitem = item->getSubItems()[prop_name];
+                if (subitem) {
+                    addSubProperties(subProperty, subitem);
+                }
+            }
+
+        } else {
+            subProperty = m_read_only_manager->addProperty(QVariant::String,
+                                                         prop_name);
+            subProperty->setValue(QLatin1String("< Unknown Type >"));
+            subProperty->setEnabled(false);
+        }
+
+        item_property->addSubProperty(subProperty);
+        ParameterizedItem *non_const_item =
+                const_cast<ParameterizedItem *>(item);
+        ItemIndexPair item_index_pair(non_const_item, i);
+        m_property_to_item_index_pair[subProperty] = item_index_pair;
+        m_item_to_index_to_property[item][i] = subProperty;
+        m_item_to_propertyname_to_qtvariantproperty[item][prop_name] = subProperty;
+    }
+}
+
diff --git a/GUI/coregui/Views/Components/JobQueueWidgets/UniversalPropertyEditor.h b/GUI/coregui/Views/Components/JobQueueWidgets/UniversalPropertyEditor.h
new file mode 100644
index 00000000000..cf5e7c3a027
--- /dev/null
+++ b/GUI/coregui/Views/Components/JobQueueWidgets/UniversalPropertyEditor.h
@@ -0,0 +1,95 @@
+// ************************************************************************** //
+//
+//  BornAgain: simulate and fit scattering at grazing incidence
+//
+//! @file      coregui/Views/Components/JobQueueWidgets/UniversalPropertyEditor.h
+//! @brief     Defines class UniversalPropertyEditor
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2015
+//! @authors   Scientific Computing Group at MLZ Garching
+//! @authors   C. Durniak, M. Ganeva, G. Pospelov, W. Van Herck, J. Wuttke
+//
+// ************************************************************************** //
+
+#ifndef UNIVERSALPROPERTYEDITOR_H
+#define UNIVERSALPROPERTYEDITOR_H
+
+#include "WinDllMacros.h"
+#include <QWidget>
+#include <QMap>
+#include <QString>
+
+class SampleDesignerInterface;
+class QItemSelectionModel;
+class QItemSelection;
+class QtVariantPropertyManager;
+class QtTreePropertyBrowser;
+class QtProperty;
+class QtVariantProperty;
+class QVariant;
+class QtAbstractPropertyBrowser;
+class ParameterizedItem;
+
+
+//! property editor to display and modify properties of currently selected ParameterizedItem
+class BA_CORE_API_ UniversalPropertyEditor : public QWidget
+{
+    Q_OBJECT
+
+public:
+    UniversalPropertyEditor(QItemSelectionModel *selection_model,
+                         QWidget *parent = 0);
+    virtual ~UniversalPropertyEditor(){}
+
+    QObject *getObject() const;
+    struct ItemIndexPair {
+        ItemIndexPair(ParameterizedItem *item=0, int index=0)
+            : m_item(item), m_index(index) {}
+        ParameterizedItem *m_item;
+        int m_index;
+    };
+
+    void setSelectionModel(QItemSelectionModel *selection_model);
+
+    //! assigns item to the property editor
+    void setItem(ParameterizedItem *item);
+
+public slots:
+    //! show property of currently selected object (triggered by graphics scene)
+    void selectionChanged(const QItemSelection & selected,
+                          const QItemSelection & deselected);
+
+private slots:
+    void slotValueChanged(QtProperty *property, const QVariant &value);
+    void updateSubItems(const QString &name);
+    void onPropertyChanged(const QString &property_name);
+
+private:
+    //! clear editor
+    void clearEditor();
+
+    ParameterizedItem *m_item; //! object to modify
+
+    QItemSelectionModel *m_selection_model;
+
+    QMap<QtProperty *, ItemIndexPair>     m_property_to_item_index_pair;
+    QMap<const ParameterizedItem *, QMap<int, QtVariantProperty *> >
+        m_item_to_index_to_property;
+
+    QMap<const ParameterizedItem *, QMap<QString, QtVariantProperty *> >
+        m_item_to_propertyname_to_qtvariantproperty;
+
+
+    QtAbstractPropertyBrowser    *m_browser;
+    QtVariantPropertyManager *m_manager;
+    QtVariantPropertyManager *m_read_only_manager;
+
+    void addItemProperties(const ParameterizedItem *item);
+    void addSubProperties(QtProperty *item_property,
+                          const ParameterizedItem *item);
+};
+
+
+#endif // SAMPLEPROPERTYEDITOR_H
-- 
GitLab