From 134feb52ae91718e7f21b845dde36ad5bb54bca5 Mon Sep 17 00:00:00 2001
From: Roman Tolchenov <roman.tolchenov@stfc.ac.uk>
Date: Mon, 25 Mar 2019 09:10:20 +0000
Subject: [PATCH] Added FunctionTreeView class. Re #22055.

---
 .../Indirect/IndirectFitPropertyBrowser.cpp   |    5 +
 .../Indirect/IndirectFitPropertyBrowser.h     |    1 +
 qt/widgets/common/CMakeLists.txt              |    4 +
 .../MantidQtWidgets/Common/FunctionTreeView.h |  359 ++++
 qt/widgets/common/src/FunctionTreeView.cpp    | 1823 +++++++++++++++++
 5 files changed, 2192 insertions(+)
 create mode 100644 qt/widgets/common/inc/MantidQtWidgets/Common/FunctionTreeView.h
 create mode 100644 qt/widgets/common/src/FunctionTreeView.cpp

diff --git a/qt/scientific_interfaces/Indirect/IndirectFitPropertyBrowser.cpp b/qt/scientific_interfaces/Indirect/IndirectFitPropertyBrowser.cpp
index 1eddea64b7f..7dc7490838c 100644
--- a/qt/scientific_interfaces/Indirect/IndirectFitPropertyBrowser.cpp
+++ b/qt/scientific_interfaces/Indirect/IndirectFitPropertyBrowser.cpp
@@ -76,6 +76,11 @@ void IndirectFitPropertyBrowser::initFitOptionsBrowser() {
   m_fitOptionsBrowser->setCurrentFittingType(FitOptionsBrowser::Sequential);
 }
 
+bool IndirectFitPropertyBrowser::isFullFunctionBrowserActive() const
+{
+  return m_functionWidget->currentIndex() == 1;
+}
+
 void IndirectFitPropertyBrowser::init() {
   initFunctionBrowser();
   initFitOptionsBrowser();
diff --git a/qt/scientific_interfaces/Indirect/IndirectFitPropertyBrowser.h b/qt/scientific_interfaces/Indirect/IndirectFitPropertyBrowser.h
index 70247eabab5..1c88c2f6d14 100644
--- a/qt/scientific_interfaces/Indirect/IndirectFitPropertyBrowser.h
+++ b/qt/scientific_interfaces/Indirect/IndirectFitPropertyBrowser.h
@@ -100,6 +100,7 @@ signals:
 private:
   void initFunctionBrowser();
   void initFitOptionsBrowser();
+  bool isFullFunctionBrowserActive() const;
 
   QVBoxLayout *m_mainLayout;
   MantidWidgets::FunctionBrowser *m_functionBrowser;
diff --git a/qt/widgets/common/CMakeLists.txt b/qt/widgets/common/CMakeLists.txt
index 72e49403906..a2e551fab27 100644
--- a/qt/widgets/common/CMakeLists.txt
+++ b/qt/widgets/common/CMakeLists.txt
@@ -19,6 +19,7 @@ set ( QT5_SRC_FILES
   src/FitPropertyBrowser.cpp
   src/FlowLayout.cpp
   src/FunctionBrowser.cpp
+  src/FunctionTreeView.cpp
   src/GenericDialog.cpp
   src/HelpWindow.cpp
   # todo: move this to the instrument view library when the slice
@@ -106,6 +107,7 @@ set ( QT5_MOC_FILES
   inc/MantidQtWidgets/Common/FitOptionsBrowser.h
   inc/MantidQtWidgets/Common/FitPropertyBrowser.h
   inc/MantidQtWidgets/Common/FunctionBrowser.h
+  inc/MantidQtWidgets/Common/FunctionTreeView.h
   inc/MantidQtWidgets/Common/GenericDialog.h
   inc/MantidQtWidgets/Common/InputController.h
   inc/MantidQtWidgets/Common/InterfaceManager.h
@@ -317,6 +319,7 @@ set ( SRC_FILES
   src/FitOptionsBrowser.cpp
   src/FitPropertyBrowser.cpp
   src/FunctionBrowser.cpp
+  src/FunctionTreeView.cpp
   src/HintingLineEdit.cpp
   src/IndirectInstrumentConfig.cpp
   src/InputController.cpp
@@ -428,6 +431,7 @@ set ( MOC_FILES
   inc/MantidQtWidgets/Common/FitPropertyBrowser.h
   inc/MantidQtWidgets/Common/FitOptionsBrowser.h
   inc/MantidQtWidgets/Common/FunctionBrowser.h
+  inc/MantidQtWidgets/Common/FunctionTreeView.h
   inc/MantidQtWidgets/Common/HintingLineEdit.h
   inc/MantidQtWidgets/Common/CatalogSearch.h
   inc/MantidQtWidgets/Common/CatalogSelector.h
diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionTreeView.h b/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionTreeView.h
new file mode 100644
index 00000000000..db2edaca5d3
--- /dev/null
+++ b/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionTreeView.h
@@ -0,0 +1,359 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#ifndef MANTIDWIDGETS_FUNCTIONTREEVIEW_H_
+#define MANTIDWIDGETS_FUNCTIONTREEVIEW_H_
+
+#include "DllOption.h"
+
+#include "MantidAPI/IFunction.h"
+//#include "MantidQtWidgets/Common/IFunctionBrowser.h"
+
+#include <QMap>
+#include <QWidget>
+
+#include <boost/optional.hpp>
+
+/* Forward declarations */
+
+class QtTreePropertyBrowser;
+class QtGroupPropertyManager;
+class QtDoublePropertyManager;
+class QtIntPropertyManager;
+class QtBoolPropertyManager;
+class QtStringPropertyManager;
+class QtEnumPropertyManager;
+class QtProperty;
+class QtBrowserItem;
+class ParameterPropertyManager;
+
+class QPushButton;
+class QLabel;
+class QLineEdit;
+class QComboBox;
+class QSignalMapper;
+class QMenu;
+class QAction;
+class QTreeWidget;
+
+namespace Mantid {
+namespace API {
+class CompositeFunction;
+class Workspace;
+class ParameterTie;
+} // namespace API
+} // namespace Mantid
+
+namespace MantidQt {
+namespace MantidWidgets {
+
+class CreateAttributePropertyForFunctionTreeView;
+
+/**
+ * Class FitPropertyBrowser implements QtPropertyBrowser to display
+ * and control fitting function parameters and settings.
+ *
+ * @date 18/04/2012
+ */
+class EXPORT_OPT_MANTIDQT_COMMON FunctionTreeView : public QWidget {
+  Q_OBJECT
+public:
+  /// To keep QtProperty and its QtBrowserItem in one place
+  struct AProperty {
+    QtProperty *prop;
+    QtBrowserItem *item;
+    QtProperty *parent;
+  };
+  /// Tie structure
+  struct ATie {
+    QtProperty *paramProp; ///< Parameter property
+    QString paramName;     ///< Parameter name
+    QtProperty *tieProp;   ///< Tie property
+  };
+  /// Constraint structure
+  struct AConstraint {
+    QtProperty *paramProp; ///< Parameter property
+    QtProperty *lower;     ///< Constraint property
+    QtProperty *upper;     ///< Constraint property
+  };
+
+  /// Constructor
+  FunctionTreeView(QWidget *parent = nullptr, bool multi = false);
+  /// Destructor
+  virtual ~FunctionTreeView() override;
+  /// Clear the contents
+  void clear();
+  /// Set the function in the browser
+  void setFunction(Mantid::API::IFunction_sptr fun);
+  /// Return the function
+  Mantid::API::IFunction_sptr getFunction(QtProperty *prop = nullptr,
+                                          bool attributesOnly = false);
+  /// Check if a function is set
+  bool hasFunction() const;
+
+  /// Update the function parameter value
+  void setParameter(const QString &funcIndex, const QString &paramName,
+                    double value);
+  /// Update the function parameter error
+  void setParamError(const QString &funcIndex, const QString &paramName,
+                     double error);
+  /// Get a value of a parameter
+  double getParameter(const QString &funcIndex, const QString &paramName) const;
+
+  /// Resize the browser's columns
+  void setColumnSizes(int s0, int s1, int s2 = -1);
+
+  /// Set error display on/off
+  void setErrorsEnabled(bool enabled);
+  /// Clear all errors
+  void clearErrors();
+
+signals:
+  /// User selects a different function (or one of it's sub-properties)
+  void currentFunctionChanged();
+
+  /// Function parameter gets changed
+  /// @param funcIndex :: Index of the changed function
+  /// @param paramName :: Name of the changed parameter
+  void parameterChanged(const QString &funcIndex,
+                        const QString &paramName);
+
+  /// In multi-dataset context a button value editor was clicked
+  void localParameterButtonClicked(const QString &parName);
+
+  void functionStructureChanged();
+  void globalsChanged();
+  void constraintsChanged();
+  void tiesChanged();
+
+protected:
+  /// Create the Qt property browser
+  void createBrowser();
+  /// Create and connect actions
+  void createActions();
+  /// Add a sub-property
+  AProperty addProperty(QtProperty *parent, QtProperty *subproperty);
+  /// Remove and delete property
+  void removeProperty(QtProperty *prop);
+  /// Set a function
+  void setFunction(QtProperty *prop, Mantid::API::IFunction_sptr fun);
+  /// Add a function
+  void addFunction(QtProperty *prop, Mantid::API::IFunction_sptr fun);
+  /// Add a function property
+  AProperty addFunctionProperty(QtProperty *parent, QString funName);
+  /// Add a parameter property
+  AProperty addParameterProperty(QtProperty *parent, QString paramName,
+                                 QString paramDesc, double paramValue);
+  /// Add a attribute property
+  AProperty addAttributeProperty(QtProperty *parent, QString attName,
+                                 const Mantid::API::IFunction::Attribute &att);
+  /// Add attribute and parameter properties to a function property
+  void addAttributeAndParameterProperties(QtProperty *prop,
+                                          Mantid::API::IFunction_sptr fun);
+  /// Add property showing function's index in the composite function
+  AProperty addIndexProperty(QtProperty *prop);
+  /// Update function index properties
+  void updateFunctionIndices(QtProperty *prop = nullptr, QString index = "");
+  /// Get property of the overall function
+  AProperty getFunctionProperty() const;
+  /// Check if property is a function group
+  bool isFunction(QtProperty *prop) const;
+  /// Check if property is a function attribute
+  bool isAttribute(QtProperty *prop) const;
+  /// Check if property is a string attribute
+  bool isStringAttribute(QtProperty *prop) const;
+  /// Check if property is a double attribute
+  bool isDoubleAttribute(QtProperty *prop) const;
+  /// Check if property is a int attribute
+  bool isIntAttribute(QtProperty *prop) const;
+  /// Check if property is a bool attribute
+  bool isBoolAttribute(QtProperty *prop) const;
+  /// Check if property is a vector attribute
+  bool isVectorAttribute(QtProperty *prop) const;
+  /// Check if property is a function paramater
+  bool isParameter(QtProperty *prop) const;
+  /// Get attribute as a string
+  double getParameter(QtProperty *prop) const;
+  /// Check if a property is an index
+  bool isIndex(QtProperty *prop) const;
+  /// Get the function index for a property
+  QString getIndex(QtProperty *prop) const;
+  /// Get name of the parameter for a property
+  QString getParameterName(QtProperty *prop);
+  /// Get function property for the index
+  QtProperty *getFunctionProperty(const QString &index) const;
+  /// Split a qualified parameter name into function index and local parameter
+  /// name.
+  QStringList splitParameterName(const QString &paramName) const;
+  /// Get a property for a parameter
+  QtProperty *getParameterProperty(const QString &paramName) const;
+  /// Get a property for a parameter
+  QtProperty *getParameterProperty(const QString &funcIndex,
+                                   const QString &paramName) const;
+  /// Get a property for a parameter which is a parent of a given
+  /// property (tie or constraint).
+  QtProperty *getParentParameterProperty(QtProperty *prop) const;
+  /// Get a tie property attached to a parameter property
+  QtProperty *getTieProperty(QtProperty *prop) const;
+
+  /// Add a tie property
+  void addTieProperty(QtProperty *prop, QString tie);
+  /// Check if a parameter property has a tie
+  bool hasTie(QtProperty *prop) const;
+  /// Check if a property is a tie
+  bool isTie(QtProperty *prop) const;
+  /// Get a tie for a paramater
+  std::string getTie(QtProperty *prop) const;
+
+  /// Add a constraint property
+  QList<AProperty> addConstraintProperties(QtProperty *prop,
+                                           QString constraint);
+  /// Check if a property is a constraint
+  bool isConstraint(QtProperty *prop) const;
+  /// Check if a parameter property has a constraint
+  bool hasConstraint(QtProperty *prop) const;
+  /// Check if a parameter property has a lower bound
+  bool hasLowerBound(QtProperty *prop) const;
+  /// Check if a parameter property has a upper bound
+  bool hasUpperBound(QtProperty *prop) const;
+  /// Get a constraint string
+  QString getConstraint(const QString &paramName, const QString &lowerBound,
+                        const QString &upperBound) const;
+
+  /// Ask user for function type
+  virtual QString getUserFunctionFromDialog();
+
+protected slots:
+  /// Show the context menu
+  void popupMenu(const QPoint &);
+  /// Add a function
+  void addFunction();
+  /// Remove a function
+  void removeFunction();
+  /// Fix a parameter
+  void fixParameter();
+  /// Unfix a parameter
+  void removeTie();
+  /// Add a tie to a parameter
+  void addTie();
+  /// Copy function from the clipboard
+  void copyFromClipboard();
+  /// Copy the function to the clipboard
+  void copyToClipboard();
+  /// Add both constraints to current parameter
+  void addConstraints();
+  /// Remove both constraints from current parameter
+  void removeConstraints();
+  /// Add both constraints to current parameter
+  void addConstraints10();
+  /// Add both constraints to current parameter
+  void addConstraints50();
+  /// Remove one of the constraints
+  void removeConstraint();
+  /// Update current function index depending on currently selected item
+  void updateCurrentFunctionIndex();
+
+  //   Property change slots
+
+  /// Called when a function attribute property is changed
+  void attributeChanged(QtProperty *);
+  /// Called when a member of a vector attribute is changed
+  void attributeVectorDoubleChanged(QtProperty *);
+  /// Called when the size of a vector attribute is changed
+  void attributeVectorSizeChanged(QtProperty *);
+  /// Called when a function parameter property is changed
+  void parameterChanged(QtProperty *);
+  /// Called when button in local parameter editor was clicked
+  void parameterButtonClicked(QtProperty *);
+  /// Called when a tie property changes
+  void tieChanged(QtProperty *);
+  /// Called when a constraint property changes
+  void constraintChanged(QtProperty *);
+  /// Called when "Global" check-box was clicked
+  void globalChanged(QtProperty *, const QString &, bool);
+  /// Set value of an attribute (as a property) to a function
+  void setAttributeToFunction(Mantid::API::IFunction &fun, QtProperty *prop);
+
+protected:
+  /// Manager for function group properties
+  QtGroupPropertyManager *m_functionManager;
+  /// Manager for function parameter properties
+  ParameterPropertyManager *m_parameterManager;
+  /// Manager for function string attribute properties
+  QtStringPropertyManager *m_attributeStringManager;
+  /// Manager for function double attribute properties
+  QtDoublePropertyManager *m_attributeDoubleManager;
+  /// Manager for function int attribute properties
+  QtIntPropertyManager *m_attributeIntManager;
+  /// Manager for function bool attribute properties
+  QtBoolPropertyManager *m_attributeBoolManager;
+  /// Manager for function index properties
+  QtStringPropertyManager *m_indexManager;
+  /// Manager for function tie properties
+  QtStringPropertyManager *m_tieManager;
+  /// Manager for parameter constraint properties
+  QtStringPropertyManager *m_constraintManager;
+  /// Manager for file name attributes
+  QtStringPropertyManager *m_filenameManager;
+  /// Manager for Formula attributes
+  QtStringPropertyManager *m_formulaManager;
+  /// Manager for Workspace attributes
+  QtStringPropertyManager *m_workspaceManager;
+  /// Manager for vector attribute properties
+  QtGroupPropertyManager *m_attributeVectorManager;
+  /// Manager for vector attribute member properties
+  QtDoublePropertyManager *m_attributeVectorDoubleManager;
+  /// Manager for vector attribute size properties
+  QtIntPropertyManager *m_attributeSizeManager;
+
+  /// Qt property browser which displays properties
+  QtTreePropertyBrowser *m_browser;
+  /// Store all properties in a map for easy access
+  QMap<QtProperty *, AProperty> m_properties;
+  /// Store parameter ties. Keys are function properties.
+  QMultiMap<QtProperty *, ATie> m_ties;
+  /// Store parameter constraints. Keys are function properties.
+  QMultiMap<QtProperty *, AConstraint> m_constraints;
+
+  //   Actions
+
+  /// Add a function
+  QAction *m_actionAddFunction;
+  /// Remove a function
+  QAction *m_actionRemoveFunction;
+  /// Fix a parameter
+  QAction *m_actionFixParameter;
+  /// Unfix a parameter
+  QAction *m_actionRemoveTie;
+  /// Add a custom tie to a parameter
+  QAction *m_actionAddTie;
+  /// Copy a function from the clipboard
+  QAction *m_actionFromClipboard;
+  /// Copy a function to the clipboard
+  QAction *m_actionToClipboard;
+  /// Add both constraints to current parameter with 10% spread
+  QAction *m_actionConstraints10;
+  /// Add both constraints to current parameter with 50% spread
+  QAction *m_actionConstraints50;
+  /// Add both constraints to current parameter
+  QAction *m_actionConstraints;
+  /// Remove both constraints from current parameter
+  QAction *m_actionRemoveConstraints;
+  /// Remove one constraints from current parameter
+  QAction *m_actionRemoveConstraint;
+
+  /// Set true if the constructed function is intended to be used in a
+  /// multi-dataset fit
+  bool m_multiDataset;
+
+  friend class CreateAttributePropertyForFunctionTreeView;
+  friend class SetAttributeFromProperty;
+};
+
+} // namespace MantidWidgets
+} // namespace MantidQt
+
+#endif /*MANTIDWIDGETS_FUNCTIONTREEVIEW_H_*/
diff --git a/qt/widgets/common/src/FunctionTreeView.cpp b/qt/widgets/common/src/FunctionTreeView.cpp
new file mode 100644
index 00000000000..e16710dc976
--- /dev/null
+++ b/qt/widgets/common/src/FunctionTreeView.cpp
@@ -0,0 +1,1823 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+#include "MantidQtWidgets/Common/FunctionTreeView.h"
+
+#include "MantidAPI/CompositeFunction.h"
+#include "MantidAPI/Expression.h"
+#include "MantidAPI/FunctionFactory.h"
+#include "MantidAPI/IConstraint.h"
+#include "MantidAPI/MultiDomainFunction.h"
+#include "MantidAPI/ParameterTie.h"
+
+#include "MantidKernel/Logger.h"
+
+#include "MantidQtWidgets/Common/QtPropertyBrowser/DoubleDialogEditor.h"
+#include "MantidQtWidgets/Common/QtPropertyBrowser/FilenameDialogEditor.h"
+#include "MantidQtWidgets/Common/QtPropertyBrowser/FormulaDialogEditor.h"
+#include "MantidQtWidgets/Common/QtPropertyBrowser/WorkspaceEditorFactory.h"
+#include "MantidQtWidgets/Common/EditLocalParameterDialog.h"
+#include "MantidQtWidgets/Common/SelectFunctionDialog.h"
+#include "MantidQtWidgets/Common/UserFunctionDialog.h"
+
+#include "MantidQtWidgets/Common/QtPropertyBrowser/CompositeEditorFactory.h"
+#include "MantidQtWidgets/Common/QtPropertyBrowser/DoubleEditorFactory.h"
+#include "MantidQtWidgets/Common/QtPropertyBrowser/qteditorfactory.h"
+#include "MantidQtWidgets/Common/QtPropertyBrowser/qtpropertymanager.h"
+#include "MantidQtWidgets/Common/QtPropertyBrowser/qttreepropertybrowser.h"
+
+#include <QApplication>
+#include <QClipboard>
+#include <QFileInfo>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QInputDialog>
+#include <QMenu>
+#include <QMessageBox>
+#include <QMetaMethod>
+#include <QPushButton>
+#include <QSettings>
+#include <QSignalMapper>
+#include <QTreeWidget>
+#include <QVBoxLayout>
+
+#include <algorithm>
+#include <boost/lexical_cast.hpp>
+
+namespace {
+const char *globalOptionName = "Global";
+Mantid::Kernel::Logger g_log("Function Browser");
+} // namespace
+
+namespace MantidQt {
+namespace MantidWidgets {
+
+/**
+ * Constructor
+ * @param parent :: The parent widget.
+ * @param multi  :: Option to use the browser for multi-dataset fitting.
+ */
+FunctionTreeView::FunctionTreeView(QWidget *parent, bool multi)
+    : QWidget(parent), m_multiDataset(multi)//, m_numberOfDatasets(0),
+      //m_currentDataset(0)
+
+{
+  // create m_browser
+  createBrowser();
+  createActions();
+
+  QVBoxLayout *layout = new QVBoxLayout(this);
+  layout->addWidget(m_browser);
+  layout->setContentsMargins(0, 0, 0, 0);
+}
+
+/**
+ * Destructor
+ */
+FunctionTreeView::~FunctionTreeView() {}
+
+/**
+ * Create the Qt property browser and set up property managers.
+ */
+void FunctionTreeView::createBrowser() {
+  QStringList options;
+  if (m_multiDataset) {
+    options << globalOptionName;
+  }
+  m_browser = new QtTreePropertyBrowser(nullptr, options);
+
+  /* Create property managers: they create, own properties, get and set values
+   */
+  m_functionManager = new QtGroupPropertyManager(this);
+  m_parameterManager = new ParameterPropertyManager(this);
+  m_attributeStringManager = new QtStringPropertyManager(this);
+  m_attributeDoubleManager = new QtDoublePropertyManager(this);
+  m_attributeIntManager = new QtIntPropertyManager(this);
+  m_attributeBoolManager = new QtBoolPropertyManager(this);
+  m_indexManager = new QtStringPropertyManager(this);
+  m_tieManager = new QtStringPropertyManager(this);
+  m_constraintManager = new QtStringPropertyManager(this);
+  m_filenameManager = new QtStringPropertyManager(this);
+  m_formulaManager = new QtStringPropertyManager(this);
+  m_workspaceManager = new QtStringPropertyManager(this);
+  m_attributeVectorManager = new QtGroupPropertyManager(this);
+  m_attributeSizeManager = new QtIntPropertyManager(this);
+  m_attributeVectorDoubleManager = new QtDoublePropertyManager(this);
+
+  // create editor factories
+  QtSpinBoxFactory *spinBoxFactory = new QtSpinBoxFactory(this);
+  DoubleEditorFactory *doubleEditorFactory = new DoubleEditorFactory(this);
+  ParameterEditorFactory *paramEditorFactory = new ParameterEditorFactory(this);
+
+  QtAbstractEditorFactory<ParameterPropertyManager> *parameterEditorFactory(
+      nullptr);
+  if (m_multiDataset) {
+    auto buttonFactory = new DoubleDialogEditorFactory(this);
+    auto compositeFactory =
+        new CompositeEditorFactory<ParameterPropertyManager>(this,
+                                                             buttonFactory);
+    compositeFactory->setSecondaryFactory(globalOptionName, paramEditorFactory);
+    parameterEditorFactory = compositeFactory;
+    connect(buttonFactory, SIGNAL(buttonClicked(QtProperty *)), this,
+            SLOT(parameterButtonClicked(QtProperty *)));
+    connect(buttonFactory, SIGNAL(closeEditor()), m_browser,
+            SLOT(closeEditor()));
+  } else {
+    parameterEditorFactory = paramEditorFactory;
+  }
+
+  QtLineEditFactory *lineEditFactory = new QtLineEditFactory(this);
+  QtCheckBoxFactory *checkBoxFactory = new QtCheckBoxFactory(this);
+  FilenameDialogEditorFactory *filenameDialogEditorFactory =
+      new FilenameDialogEditorFactory(this);
+  FormulaDialogEditorFactory *formulaDialogEditFactory =
+      new FormulaDialogEditorFactory(this);
+  WorkspaceEditorFactory *workspaceEditorFactory =
+      new WorkspaceEditorFactory(this);
+
+  // assign factories to property managers
+  m_browser->setFactoryForManager(m_parameterManager, parameterEditorFactory);
+  m_browser->setFactoryForManager(m_attributeStringManager, lineEditFactory);
+  m_browser->setFactoryForManager(m_attributeDoubleManager,
+                                  doubleEditorFactory);
+  m_browser->setFactoryForManager(m_attributeIntManager, spinBoxFactory);
+  m_browser->setFactoryForManager(m_attributeBoolManager, checkBoxFactory);
+  m_browser->setFactoryForManager(m_indexManager, lineEditFactory);
+  m_browser->setFactoryForManager(m_tieManager, lineEditFactory);
+  m_browser->setFactoryForManager(m_constraintManager, lineEditFactory);
+  m_browser->setFactoryForManager(m_filenameManager,
+                                  filenameDialogEditorFactory);
+  m_browser->setFactoryForManager(m_formulaManager, formulaDialogEditFactory);
+  m_browser->setFactoryForManager(m_workspaceManager, workspaceEditorFactory);
+  m_browser->setFactoryForManager(m_attributeSizeManager, spinBoxFactory);
+  m_browser->setFactoryForManager(m_attributeVectorDoubleManager,
+                                  doubleEditorFactory);
+
+  m_browser->setContextMenuPolicy(Qt::CustomContextMenu);
+  connect(m_browser, SIGNAL(customContextMenuRequested(const QPoint &)), this,
+          SLOT(popupMenu(const QPoint &)));
+  connect(m_browser, SIGNAL(optionChanged(QtProperty *, const QString &, bool)),
+          this, SLOT(globalChanged(QtProperty *, const QString &, bool)));
+
+  connect(m_attributeStringManager, SIGNAL(propertyChanged(QtProperty *)), this,
+          SLOT(attributeChanged(QtProperty *)));
+  connect(m_attributeDoubleManager, SIGNAL(propertyChanged(QtProperty *)), this,
+          SLOT(attributeChanged(QtProperty *)));
+  connect(m_attributeIntManager, SIGNAL(propertyChanged(QtProperty *)), this,
+          SLOT(attributeChanged(QtProperty *)));
+  connect(m_attributeBoolManager, SIGNAL(propertyChanged(QtProperty *)), this,
+          SLOT(attributeChanged(QtProperty *)));
+  connect(m_formulaManager, SIGNAL(propertyChanged(QtProperty *)), this,
+          SLOT(attributeChanged(QtProperty *)));
+  connect(m_filenameManager, SIGNAL(propertyChanged(QtProperty *)), this,
+          SLOT(attributeChanged(QtProperty *)));
+  connect(m_workspaceManager, SIGNAL(propertyChanged(QtProperty *)), this,
+          SLOT(attributeChanged(QtProperty *)));
+  connect(m_attributeVectorDoubleManager, SIGNAL(propertyChanged(QtProperty *)),
+          this, SLOT(attributeVectorDoubleChanged(QtProperty *)));
+  connect(m_attributeSizeManager, SIGNAL(propertyChanged(QtProperty *)), this,
+          SLOT(attributeVectorSizeChanged(QtProperty *)));
+  connect(m_tieManager, SIGNAL(propertyChanged(QtProperty *)), this,
+          SLOT(tieChanged(QtProperty *)));
+  connect(m_constraintManager, SIGNAL(propertyChanged(QtProperty *)), this,
+          SLOT(constraintChanged(QtProperty *)));
+  connect(m_parameterManager, SIGNAL(valueChanged(QtProperty *, double)),
+          SLOT(parameterChanged(QtProperty *)));
+
+  connect(m_browser, SIGNAL(currentItemChanged(QtBrowserItem *)),
+          SLOT(updateCurrentFunctionIndex()));
+
+  m_browser->setFocusPolicy(Qt::StrongFocus);
+}
+
+/**
+ * Create and connect actions
+ */
+void FunctionTreeView::createActions() {
+  m_actionAddFunction = new QAction("Add function", this);
+  connect(m_actionAddFunction, SIGNAL(triggered()), this, SLOT(addFunction()));
+
+  m_actionRemoveFunction = new QAction("Remove function", this);
+  connect(m_actionRemoveFunction, SIGNAL(triggered()), this,
+          SLOT(removeFunction()));
+
+  m_actionFixParameter = new QAction("Fix", this);
+  connect(m_actionFixParameter, SIGNAL(triggered()), this,
+          SLOT(fixParameter()));
+
+  m_actionRemoveTie = new QAction("Remove tie", this);
+  connect(m_actionRemoveTie, SIGNAL(triggered()), this, SLOT(removeTie()));
+
+  m_actionAddTie = new QAction("Add tie", this);
+  connect(m_actionAddTie, SIGNAL(triggered()), this, SLOT(addTie()));
+
+  m_actionFromClipboard = new QAction("Copy from clipboard", this);
+  connect(m_actionFromClipboard, SIGNAL(triggered()), this,
+          SLOT(copyFromClipboard()));
+
+  m_actionToClipboard = new QAction("Copy to clipboard", this);
+  connect(m_actionToClipboard, SIGNAL(triggered()), this,
+          SLOT(copyToClipboard()));
+
+  m_actionConstraints = new QAction("Custom", this);
+  connect(m_actionConstraints, SIGNAL(triggered()), this,
+          SLOT(addConstraints()));
+
+  m_actionConstraints10 = new QAction("10%", this);
+  connect(m_actionConstraints10, SIGNAL(triggered()), this,
+          SLOT(addConstraints10()));
+
+  m_actionConstraints50 = new QAction("50%", this);
+  connect(m_actionConstraints50, SIGNAL(triggered()), this,
+          SLOT(addConstraints50()));
+
+  m_actionRemoveConstraints = new QAction("Remove constraints", this);
+  connect(m_actionRemoveConstraints, SIGNAL(triggered()), this,
+          SLOT(removeConstraints()));
+
+  m_actionRemoveConstraint = new QAction("Remove", this);
+  connect(m_actionRemoveConstraint, SIGNAL(triggered()), this,
+          SLOT(removeConstraint()));
+
+  setErrorsEnabled(true);
+}
+
+/**
+ * Clear the contents
+ */
+void FunctionTreeView::clear() {
+  m_browser->clear();
+  m_properties.clear();
+}
+
+/**
+ * Set the function in the browser
+ * @param fun :: A function
+ */
+void FunctionTreeView::setFunction(Mantid::API::IFunction_sptr fun) {
+  clear();
+  addFunction(nullptr, fun);
+  //emit functionStructureChanged();
+}
+
+/**
+ * Add a sub-property to a parent property
+ * @param parent :: The parent property
+ * @param subproperty :: New sub-property
+ */
+FunctionTreeView::AProperty
+FunctionTreeView::addProperty(QtProperty *parent, QtProperty *subproperty) {
+  AProperty ap;
+  ap.prop = subproperty;
+  if (parent == nullptr) {
+    ap.item = m_browser->addProperty(subproperty);
+  } else {
+    parent->addSubProperty(subproperty);
+    auto items = m_browser->items(subproperty);
+    if (items.isEmpty()) {
+      throw std::runtime_error("Unexpected error in FunctionTreeView [1]");
+    }
+    ap.item = items[0];
+  }
+  ap.parent = parent;
+  m_properties[subproperty] = ap;
+  return ap;
+}
+
+/**
+ * Remove and delete property
+ * @param prop :: Property to remove.
+ */
+void FunctionTreeView::removeProperty(QtProperty *prop) {
+  auto p = m_properties.find(prop);
+  if (p == m_properties.end())
+    return;
+  AProperty ap = *p;
+
+  // remove references to the children
+  auto children = prop->subProperties();
+  foreach (QtProperty *child, children) { removeProperty(child); }
+  m_properties.erase(p);
+
+  if (isFunction(prop)) {
+    m_ties.remove(prop);
+  }
+
+  if (isTie(prop)) { //
+    for (auto it = m_ties.begin(); it != m_ties.end(); ++it) {
+      if (it.value().tieProp == prop) {
+        m_ties.erase(it);
+        break;
+      }
+    }
+  }
+
+  if (isConstraint(prop)) {
+    for (auto it = m_constraints.begin(); it != m_constraints.end(); ++it) {
+      auto &cp = it.value();
+      if (cp.lower == prop) {
+        if (!cp.upper) {
+          m_constraints.erase(it);
+        } else {
+          cp.lower = nullptr;
+        }
+        break;
+      } else if (cp.upper == prop) {
+        if (!cp.lower) {
+          m_constraints.erase(it);
+        } else {
+          cp.upper = nullptr;
+        }
+        break;
+      }
+    }
+  }
+
+  // remove property from Qt browser
+  if (ap.parent) {
+    ap.parent->removeSubProperty(prop);
+  } else {
+    m_browser->removeProperty(prop);
+  }
+  delete prop;
+}
+
+/**
+ * Add a function property
+ * @param parent :: Parent function property or NULL
+ * @param funName :: Function name
+ * @return :: A set AProperty struct
+ */
+FunctionTreeView::AProperty
+FunctionTreeView::addFunctionProperty(QtProperty *parent, QString funName) {
+  // check that parent is a function property
+  if (parent && dynamic_cast<QtAbstractPropertyManager *>(m_functionManager) !=
+                    parent->propertyManager()) {
+    throw std::runtime_error("Unexpected error in FunctionTreeView [2]");
+  }
+  QtProperty *prop = m_functionManager->addProperty(funName);
+  return addProperty(parent, prop);
+}
+
+/**
+ * Add a parameter property
+ * @param parent :: Parent function property
+ * @param paramName :: Parameter name
+ * @param paramDesc :: Parameter description
+ * @param paramValue :: Parameter value
+ */
+FunctionTreeView::AProperty
+FunctionTreeView::addParameterProperty(QtProperty *parent, QString paramName,
+                                      QString paramDesc, double paramValue) {
+  // check that parent is a function property
+  if (!parent || dynamic_cast<QtAbstractPropertyManager *>(m_functionManager) !=
+                     parent->propertyManager()) {
+    throw std::runtime_error("Unexpected error in FunctionTreeView [3]");
+  }
+  QtProperty *prop = m_parameterManager->addProperty(paramName);
+  m_parameterManager->blockSignals(true);
+  m_parameterManager->setDecimals(prop, 6);
+  m_parameterManager->setValue(prop, paramValue);
+  m_parameterManager->setDescription(prop, paramDesc.toStdString());
+  m_parameterManager->blockSignals(false);
+
+  if (m_multiDataset) {
+    prop->setOption(globalOptionName, false);
+  }
+  return addProperty(parent, prop);
+}
+
+/**
+ * Set a function.
+ * @param prop :: Property of the function or NULL
+ * @param fun :: A function
+ */
+void FunctionTreeView::setFunction(QtProperty *prop,
+                                  Mantid::API::IFunction_sptr fun) {
+  auto children = prop->subProperties();
+  foreach (QtProperty *child, children) { removeProperty(child); }
+  //m_localParameterValues.clear();
+  addAttributeAndParameterProperties(prop, fun);
+}
+
+/**
+ * Add a function.
+ * @param prop :: Property of the parent composite function or NULL
+ * @param fun :: FunctionFactory function creation string
+ */
+void FunctionTreeView::addFunction(QtProperty *prop,
+                                  Mantid::API::IFunction_sptr fun) {
+  if (!prop) {
+    AProperty ap =
+        addFunctionProperty(nullptr, QString::fromStdString(fun->name()));
+    setFunction(ap.prop, fun);
+  } else {
+    Mantid::API::IFunction_sptr parentFun = getFunction(prop);
+    if (!parentFun)
+      return;
+    auto cf =
+        boost::dynamic_pointer_cast<Mantid::API::CompositeFunction>(parentFun);
+    if (!cf) {
+      throw std::runtime_error(
+          "FunctionTreeView: CompositeFunction is expected for addFunction");
+    }
+    cf->addFunction(fun);
+    setFunction(prop, cf);
+  }
+  updateFunctionIndices();
+}
+
+/**
+ * Attribute visitor to create a QtProperty. Depending on the attribute type
+ * the appropriate apply() method is used.
+ */
+class CreateAttributePropertyForFunctionTreeView
+    : public Mantid::API::IFunction::ConstAttributeVisitor<
+          FunctionTreeView::AProperty> {
+public:
+  CreateAttributePropertyForFunctionTreeView(FunctionTreeView *browser,
+                                            QtProperty *parent, QString attName)
+      : m_browser(browser), m_parent(parent), m_attName(attName) {
+    // check that parent is a function property
+    if (!m_parent ||
+        dynamic_cast<QtAbstractPropertyManager *>(
+            m_browser->m_functionManager) != m_parent->propertyManager()) {
+      throw std::runtime_error("Unexpected error in FunctionTreeView [4]");
+    }
+  }
+
+protected:
+  /// Create string property
+  FunctionTreeView::AProperty apply(const std::string &str) const override {
+    QtProperty *prop = nullptr;
+    if (m_attName == "FileName") {
+      prop = m_browser->m_filenameManager->addProperty(m_attName);
+      m_browser->m_filenameManager->setValue(prop, QString::fromStdString(str));
+    } else if (m_attName == "Formula") {
+      prop = m_browser->m_formulaManager->addProperty(m_attName);
+      m_browser->m_formulaManager->setValue(prop, QString::fromStdString(str));
+    } else if (m_attName == "Workspace") {
+      prop = m_browser->m_workspaceManager->addProperty(m_attName);
+      m_browser->m_workspaceManager->setValue(prop,
+                                              QString::fromStdString(str));
+    } else {
+      prop = m_browser->m_attributeStringManager->addProperty(m_attName);
+      m_browser->m_attributeStringManager->setValue(
+          prop, QString::fromStdString(str));
+    }
+    return m_browser->addProperty(m_parent, prop);
+  }
+  /// Create double property
+  FunctionTreeView::AProperty apply(const double &d) const override {
+    QtProperty *prop =
+        m_browser->m_attributeDoubleManager->addProperty(m_attName);
+    m_browser->m_attributeDoubleManager->setValue(prop, d);
+    return m_browser->addProperty(m_parent, prop);
+  }
+  /// Create int property
+  FunctionTreeView::AProperty apply(const int &i) const override {
+    QtProperty *prop = m_browser->m_attributeIntManager->addProperty(m_attName);
+    m_browser->m_attributeIntManager->setValue(prop, i);
+    return m_browser->addProperty(m_parent, prop);
+  }
+  /// Create bool property
+  FunctionTreeView::AProperty apply(const bool &b) const override {
+    QtProperty *prop =
+        m_browser->m_attributeBoolManager->addProperty(m_attName);
+    m_browser->m_attributeBoolManager->setValue(prop, b);
+    return m_browser->addProperty(m_parent, prop);
+  }
+  /// Create vector property
+  FunctionTreeView::AProperty
+  apply(const std::vector<double> &v) const override {
+    QtProperty *prop =
+        m_browser->m_attributeVectorManager->addProperty(m_attName);
+    FunctionTreeView::AProperty aprop = m_browser->addProperty(m_parent, prop);
+
+    m_browser->m_attributeSizeManager->blockSignals(true);
+    QtProperty *sizeProp =
+        m_browser->m_attributeSizeManager->addProperty("Size");
+    m_browser->m_attributeSizeManager->setValue(sizeProp,
+                                                static_cast<int>(v.size()));
+    m_browser->addProperty(prop, sizeProp);
+    m_browser->m_attributeSizeManager->blockSignals(false);
+    sizeProp->setEnabled(false);
+
+    m_browser->m_attributeVectorDoubleManager->blockSignals(true);
+    QString parName = "value[%1]";
+    for (size_t i = 0; i < v.size(); ++i) {
+      QtProperty *dprop =
+          m_browser->m_attributeVectorDoubleManager->addProperty(
+              parName.arg(i));
+      m_browser->m_attributeVectorDoubleManager->setValue(dprop, v[i]);
+      m_browser->addProperty(prop, dprop);
+    }
+    m_browser->m_attributeVectorDoubleManager->blockSignals(false);
+
+    m_browser->m_browser->setExpanded(aprop.item, false);
+    return aprop;
+  }
+
+private:
+  FunctionTreeView *m_browser;
+  QtProperty *m_parent;
+  QString m_attName;
+};
+
+/**
+ * Attribute visitor to set an attribute from a QtProperty. Depending on the
+ * attribute type
+ * the appropriate apply() method is used.
+ */
+class SetAttributeFromProperty
+    : public Mantid::API::IFunction::AttributeVisitor<> {
+public:
+  SetAttributeFromProperty(FunctionTreeView *browser, QtProperty *prop)
+      : m_browser(browser), m_prop(prop) {}
+
+protected:
+  /// Set string attribute
+  void apply(std::string &str) const override {
+    QString attName = m_prop->propertyName();
+    if (attName == "FileName") {
+      str = m_browser->m_filenameManager->value(m_prop).toStdString();
+    } else if (attName == "Formula") {
+      str = m_browser->m_formulaManager->value(m_prop).toStdString();
+    } else if (attName == "Workspace") {
+      str = m_browser->m_workspaceManager->value(m_prop).toStdString();
+    } else {
+      str = m_browser->m_attributeStringManager->value(m_prop).toStdString();
+    }
+  }
+  /// Set double attribute
+  void apply(double &d) const override {
+    d = m_browser->m_attributeDoubleManager->value(m_prop);
+  }
+  /// Set int attribute
+  void apply(int &i) const override {
+    i = m_browser->m_attributeIntManager->value(m_prop);
+  }
+  /// Set bool attribute
+  void apply(bool &b) const override {
+    b = m_browser->m_attributeBoolManager->value(m_prop);
+  }
+  /// Set vector attribute
+  void apply(std::vector<double> &v) const override {
+    // throw std::runtime_error("Vector setter not implemented.");
+    QList<QtProperty *> members = m_prop->subProperties();
+    if (members.empty())
+      throw std::runtime_error(
+          "FunctionTreeView: empty vector attribute group.");
+    int n = members.size() - 1;
+    if (n == 0) {
+      v.clear();
+      return;
+    }
+    v.resize(n);
+    for (int i = 0; i < n; ++i) {
+      v[i] = m_browser->m_attributeVectorDoubleManager->value(members[i + 1]);
+    }
+  }
+
+private:
+  FunctionTreeView *m_browser;
+  QtProperty *m_prop;
+};
+
+/**
+ * Add a attribute property
+ * @param parent :: Parent function property
+ * @param attName :: Attribute name
+ * @param att :: Attribute value
+ */
+FunctionTreeView::AProperty FunctionTreeView::addAttributeProperty(
+    QtProperty *parent, QString attName,
+    const Mantid::API::IFunction::Attribute &att) {
+  CreateAttributePropertyForFunctionTreeView cap(this, parent, attName);
+  return att.apply(cap);
+}
+
+/**
+ * Add attribute and parameter properties to a function property. For a
+ * composite function
+ *  adds all member functions' properties
+ * @param prop :: A function property
+ * @param fun :: Shared pointer to a created function
+ */
+void FunctionTreeView::addAttributeAndParameterProperties(
+    QtProperty *prop, Mantid::API::IFunction_sptr fun) {
+  // add the function index property
+  addIndexProperty(prop);
+
+  // add attribute properties
+  auto attributeNames = fun->getAttributeNames();
+  for (auto att = attributeNames.begin(); att != attributeNames.end(); ++att) {
+    QString attName = QString::fromStdString(*att);
+    addAttributeProperty(prop, attName, fun->getAttribute(*att));
+  }
+
+  auto cf = boost::dynamic_pointer_cast<Mantid::API::CompositeFunction>(fun);
+  if (cf) { // if composite add members
+    for (size_t i = 0; i < cf->nFunctions(); ++i) {
+      AProperty ap = addFunctionProperty(
+          prop, QString::fromStdString(cf->getFunction(i)->name()));
+      addAttributeAndParameterProperties(ap.prop, cf->getFunction(i));
+    }
+  } else { // if simple add parameters
+    for (size_t i = 0; i < fun->nParams(); ++i) {
+      QString name = QString::fromStdString(fun->parameterName(i));
+      QString desc = QString::fromStdString(fun->parameterDescription(i));
+      double value = fun->getParameter(i);
+      AProperty ap = addParameterProperty(prop, name, desc, value);
+      // if parameter has a tie
+      if (!fun->isActive(i)) {
+        auto tie = fun->getTie(i);
+        if (tie) {
+          addTieProperty(ap.prop, QString::fromStdString(tie->asString()));
+        } else {
+          addTieProperty(ap.prop, QString::number(fun->getParameter(i)));
+        }
+      }
+      auto c = fun->getConstraint(i);
+      if (c) {
+        addConstraintProperties(ap.prop, QString::fromStdString(c->asString()));
+      }
+    }
+  }
+}
+
+/**
+ * Add property showing function's index in the composite function
+ * @param prop :: A function property
+ * @return :: AProperty struct for added property. If all fields are NULL -
+ * property wasn't added
+ *  because it is the top function
+ */
+FunctionTreeView::AProperty FunctionTreeView::addIndexProperty(QtProperty *prop) {
+  AProperty ap;
+  ap.item = nullptr;
+  ap.parent = nullptr;
+  ap.prop = nullptr;
+  if (!prop)
+    return ap;
+  if (!isFunction(prop))
+    return ap;
+  if (!m_properties[prop].parent)
+    return ap;
+
+  QString index = "fff";
+  QtProperty *ip = m_indexManager->addProperty("Index");
+  ip->setEnabled(false);
+  m_indexManager->setValue(ip, index);
+  auto retval = addProperty(prop, ip);
+  updateFunctionIndices();
+  return retval;
+}
+
+/**
+ * Update function index properties
+ * @param prop :: A function property
+ * @param index :: The parent function's index
+ */
+void FunctionTreeView::updateFunctionIndices(QtProperty *prop, QString index) {
+  if (prop == nullptr) {
+    auto top = m_browser->properties();
+    if (top.isEmpty())
+      return;
+    prop = top[0];
+  }
+  auto children = prop->subProperties();
+  size_t i = 0;
+  foreach (QtProperty *child, children) {
+    if (isFunction(child)) {
+      updateFunctionIndices(child, index + "f" + QString::number(i) + ".");
+      ++i;
+    } else if (isIndex(child)) {
+      m_indexManager->setValue(child, index);
+    }
+  }
+}
+
+/**
+ * Get property of the overall function.
+ */
+FunctionTreeView::AProperty FunctionTreeView::getFunctionProperty() const {
+  auto props = m_browser->properties();
+  if (props.isEmpty()) {
+    AProperty ap;
+    ap.item = nullptr;
+    ap.parent = nullptr;
+    ap.prop = nullptr;
+    return ap;
+  }
+  QtProperty *prop = props[0];
+  return m_properties[prop];
+}
+
+/**
+ * Check if property is a function group
+ * @param prop :: Property to check
+ */
+bool FunctionTreeView::isFunction(QtProperty *prop) const {
+  return prop && dynamic_cast<QtAbstractPropertyManager *>(m_functionManager) ==
+                     prop->propertyManager();
+}
+
+/**
+ * Check if property is any of the string attributes
+ * @param prop :: Property to check
+ */
+bool FunctionTreeView::isStringAttribute(QtProperty *prop) const {
+  return prop &&
+         (dynamic_cast<QtAbstractPropertyManager *>(m_attributeStringManager) ==
+              prop->propertyManager() ||
+          dynamic_cast<QtAbstractPropertyManager *>(m_formulaManager) ==
+              prop->propertyManager() ||
+          dynamic_cast<QtAbstractPropertyManager *>(m_filenameManager) ==
+              prop->propertyManager() ||
+          dynamic_cast<QtAbstractPropertyManager *>(m_workspaceManager) ==
+              prop->propertyManager());
+}
+
+/**
+ * Check if property is a function attribute
+ * @param prop :: Property to check
+ */
+bool FunctionTreeView::isDoubleAttribute(QtProperty *prop) const {
+  return prop && dynamic_cast<QtAbstractPropertyManager *>(
+                     m_attributeDoubleManager) == prop->propertyManager();
+}
+
+/**
+ * Check if property is a function attribute
+ * @param prop :: Property to check
+ */
+bool FunctionTreeView::isIntAttribute(QtProperty *prop) const {
+  return prop && dynamic_cast<QtAbstractPropertyManager *>(
+                     m_attributeIntManager) == prop->propertyManager();
+}
+
+/**
+ * Check if property is a function bool attribute
+ * @param prop :: Property to check
+ */
+bool FunctionTreeView::isBoolAttribute(QtProperty *prop) const {
+  return prop && dynamic_cast<QtAbstractPropertyManager *>(
+                     m_attributeBoolManager) == prop->propertyManager();
+}
+
+/**
+ * Check if property is a function vector attribute
+ * @param prop :: Property to check
+ */
+bool FunctionTreeView::isVectorAttribute(QtProperty *prop) const {
+  return prop && dynamic_cast<QtAbstractPropertyManager *>(
+                     m_attributeVectorManager) == prop->propertyManager();
+}
+
+/**
+ * Check if property is a function attribute
+ * @param prop :: Property to check
+ */
+bool FunctionTreeView::isAttribute(QtProperty *prop) const {
+  return isStringAttribute(prop) || isDoubleAttribute(prop) ||
+         isIntAttribute(prop) || isBoolAttribute(prop) ||
+         isVectorAttribute(prop);
+}
+
+/**
+ * Check if property is a function parameter
+ * @param prop :: Property to check
+ */
+bool FunctionTreeView::isParameter(QtProperty *prop) const {
+  return prop && dynamic_cast<QtAbstractPropertyManager *>(
+                     m_parameterManager) == prop->propertyManager();
+}
+
+/**
+ * Get parameter value as a string
+ * @param prop :: A parameter property
+ */
+double FunctionTreeView::getParameter(QtProperty *prop) const {
+  return m_parameterManager->value(prop);
+}
+
+/**
+ * Check if a property is an index
+ * @param prop :: A property
+ */
+bool FunctionTreeView::isIndex(QtProperty *prop) const {
+  return prop && dynamic_cast<QtAbstractPropertyManager *>(m_indexManager) ==
+                     prop->propertyManager();
+}
+
+/**
+ * Get the function index for a property
+ * @param prop :: A property
+ */
+QString FunctionTreeView::getIndex(QtProperty *prop) const {
+  if (!prop)
+    return "";
+  if (isFunction(prop)) {
+    auto props = prop->subProperties();
+    if (props.isEmpty())
+      return "";
+    for (auto it = props.begin(); it != props.end(); ++it) {
+      if (isIndex(*it)) {
+        return m_indexManager->value(*it);
+      }
+    }
+    return "";
+  }
+
+  auto ap = m_properties[prop];
+  return getIndex(ap.parent);
+}
+
+/**
+ * Get name of the parameter for a property
+ * @param prop :: A property
+ */
+QString FunctionTreeView::getParameterName(QtProperty *prop) {
+  return getIndex(prop) + prop->propertyName();
+}
+
+/**
+ * Return function property for a function with given index.
+ * @param index :: Function index to search, or empty string for top-level
+ * function
+ * @return Function property, or NULL if not found
+ */
+QtProperty *FunctionTreeView::getFunctionProperty(const QString &index) const {
+  // Might not be the most efficient way to do it. m_functionManager might be
+  // searched instead,
+  // but it is not being kept up-to-date at the moment (is not cleared).
+  foreach (auto property, m_properties.keys()) {
+    if (isFunction(property) && getIndex(property) == index) {
+      return property;
+    }
+  }
+
+  // No function with such index
+  return nullptr;
+}
+
+/**
+ * Add a tie property
+ * @param prop :: Parent parameter property
+ * @param tie :: A tie string
+ */
+void FunctionTreeView::addTieProperty(QtProperty *prop, QString tie) {
+  if (!prop) {
+    throw std::runtime_error("FunctionTreeView: null property pointer");
+  }
+  AProperty ap;
+  ap.item = nullptr;
+  ap.prop = nullptr;
+  ap.parent = nullptr;
+
+  if (!isParameter(prop))
+    return;
+
+  QtProperty *funProp = getFunctionProperty().prop;
+
+  // Create and add a QtProperty for the tie.
+  m_tieManager->blockSignals(true);
+  QtProperty *tieProp = m_tieManager->addProperty("Tie");
+  m_tieManager->setValue(tieProp, tie);
+  ap = addProperty(prop, tieProp);
+  m_tieManager->blockSignals(false);
+
+  const auto parName = getParameterName(prop);
+  // Store tie information for easier access
+  ATie atie;
+  atie.paramProp = prop;
+  atie.paramName = parName;
+  atie.tieProp = tieProp;
+  m_ties.insert(funProp, atie);
+}
+
+/**
+ * Check if a parameter property has a tie
+ * @param prop :: A parameter property
+ */
+bool FunctionTreeView::hasTie(QtProperty *prop) const {
+  if (!prop)
+    return false;
+  auto children = prop->subProperties();
+  foreach (QtProperty *child, children) {
+    if (child->propertyName() == "Tie") {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * Check if a property is a tie
+ * @param prop :: A property
+ */
+bool FunctionTreeView::isTie(QtProperty *prop) const {
+  return prop && dynamic_cast<QtAbstractPropertyManager *>(m_tieManager) ==
+                     prop->propertyManager();
+}
+
+/**
+ * Get a tie for a parameter
+ * @param prop :: A parameter property
+ */
+std::string FunctionTreeView::getTie(QtProperty *prop) const {
+  if (!prop)
+    return "";
+  auto children = prop->subProperties();
+  foreach (QtProperty *child, children) {
+    if (child->propertyName() == "Tie") {
+      return m_tieManager->value(child).toStdString();
+    }
+  }
+  return "";
+}
+
+/**
+ * Add a constraint property
+ * @param prop :: Parent parameter property
+ * @param constraint :: A constraint string
+ */
+QList<FunctionTreeView::AProperty>
+FunctionTreeView::addConstraintProperties(QtProperty *prop, QString constraint) {
+  if (!isParameter(prop))
+    return QList<FunctionTreeView::AProperty>();
+  QString lowerBoundStr = "";
+  QString upperBoundStr = "";
+  Mantid::API::Expression expr;
+  expr.parse(constraint.toStdString());
+  if (expr.name() != "==")
+    return QList<FunctionTreeView::AProperty>();
+  if (expr.size() == 3) { // lower < param < upper
+    try {
+      // check that the first and third terms are numbers
+      double d1 = boost::lexical_cast<double>(expr[0].name());
+      (void)d1;
+      double d2 = boost::lexical_cast<double>(expr[2].name());
+      (void)d2;
+      if (expr[1].operator_name() == "<" && expr[2].operator_name() == "<") {
+        lowerBoundStr = QString::fromStdString(expr[0].name());
+        upperBoundStr = QString::fromStdString(expr[2].name());
+      } else // assume that the operators are ">"
+      {
+        lowerBoundStr = QString::fromStdString(expr[2].name());
+        upperBoundStr = QString::fromStdString(expr[0].name());
+      }
+    } catch (...) { // error in constraint
+      return QList<FunctionTreeView::AProperty>();
+    }
+  } else if (expr.size() == 2) { // lower < param or param > lower etc
+    size_t paramPos = 0;
+    try // find position of the parameter name in expression
+    {
+      double d = boost::lexical_cast<double>(expr[1].name());
+      (void)d;
+    } catch (...) {
+      paramPos = 1;
+    }
+    std::string op = expr[1].operator_name();
+    if (paramPos == 0) { // parameter goes first
+      if (op == "<") {   // param < number
+        upperBoundStr = QString::fromStdString(expr[1].name());
+      } else { // param > number
+        lowerBoundStr = QString::fromStdString(expr[1].name());
+      }
+    } else {           // parameter is second
+      if (op == "<") { // number < param
+        lowerBoundStr = QString::fromStdString(expr[0].name());
+      } else { // number > param
+        upperBoundStr = QString::fromStdString(expr[0].name());
+      }
+    }
+  }
+
+  // add properties
+  QList<FunctionTreeView::AProperty> plist;
+  AConstraint ac;
+  ac.paramProp = prop;
+  ac.lower = ac.upper = nullptr;
+  if (!lowerBoundStr.isEmpty()) {
+    auto ap = addProperty(prop, m_constraintManager->addProperty("LowerBound"));
+    plist << ap;
+    ac.lower = ap.prop;
+    m_constraintManager->setValue(ac.lower, lowerBoundStr);
+  }
+  if (!upperBoundStr.isEmpty()) {
+    auto ap = addProperty(prop, m_constraintManager->addProperty("UpperBound"));
+    plist << ap;
+    ac.upper = ap.prop;
+    m_constraintManager->setValue(ac.upper, upperBoundStr);
+  }
+  if (ac.lower || ac.upper) {
+    m_constraints.insert(m_properties[prop].parent, ac);
+  }
+  return plist;
+}
+
+/**
+ * Check if a property is a constraint
+ * @param prop :: Property to check.
+ */
+bool FunctionTreeView::isConstraint(QtProperty *prop) const {
+  return prop && dynamic_cast<QtAbstractPropertyManager *>(
+                     m_constraintManager) == prop->propertyManager();
+}
+
+/**
+ * Check if a parameter property has a constraint
+ * @param prop :: A parameter property.
+ */
+bool FunctionTreeView::hasConstraint(QtProperty *prop) const {
+  return hasLowerBound(prop) || hasUpperBound(prop);
+}
+
+/**
+ * Check if a parameter property has a lower bound
+ * @param prop :: A parameter property.
+ */
+bool FunctionTreeView::hasLowerBound(QtProperty *prop) const {
+  if (!isParameter(prop))
+    return false;
+  auto props = prop->subProperties();
+  if (props.isEmpty())
+    return false;
+  foreach (QtProperty *p, props) {
+    if (dynamic_cast<QtAbstractPropertyManager *>(m_constraintManager) ==
+            p->propertyManager() &&
+        p->propertyName() == "LowerBound")
+      return true;
+  }
+  return false;
+}
+
+/**
+ * Check if a parameter property has a upper bound
+ * @param prop :: A parameter property.
+ */
+bool FunctionTreeView::hasUpperBound(QtProperty *prop) const {
+  if (!isParameter(prop))
+    return false;
+  auto props = prop->subProperties();
+  if (props.isEmpty())
+    return false;
+  foreach (QtProperty *p, props) {
+    if (dynamic_cast<QtAbstractPropertyManager *>(m_constraintManager) ==
+            p->propertyManager() &&
+        p->propertyName() == "UpperBound")
+      return true;
+  }
+  return false;
+}
+
+/// Get a constraint string
+QString FunctionTreeView::getConstraint(const QString &paramName,
+                                       const QString &lowerBound,
+                                       const QString &upperBound) const {
+  QString constraint;
+  if (!lowerBound.isEmpty()) {
+    constraint += lowerBound + "<";
+  }
+  constraint += paramName;
+  if (!upperBound.isEmpty()) {
+    constraint += "<" + upperBound;
+  }
+  return constraint;
+}
+
+/**
+ * Show a pop up menu.
+ */
+void FunctionTreeView::popupMenu(const QPoint &) {
+  auto item = m_browser->currentItem();
+  if (!item) {
+    QMenu context(this);
+    context.addAction(m_actionAddFunction);
+    if (!QApplication::clipboard()->text().isEmpty()) {
+      context.addAction(m_actionFromClipboard);
+    }
+    if (!m_browser->properties().isEmpty()) {
+      context.addAction(m_actionToClipboard);
+    }
+    context.exec(QCursor::pos());
+    return;
+  }
+  QtProperty *prop = item->property();
+  if (isFunction(prop)) { // functions
+    QMenu context(this);
+    Mantid::API::IFunction_sptr fun =
+        Mantid::API::FunctionFactory::Instance().createFunction(
+            prop->propertyName().toStdString());
+    auto cf = boost::dynamic_pointer_cast<Mantid::API::CompositeFunction>(fun);
+    if (cf || m_properties[prop].parent == nullptr) {
+      context.addAction(m_actionAddFunction);
+    }
+    context.addAction(m_actionRemoveFunction);
+    if (!QApplication::clipboard()->text().isEmpty()) {
+      context.addAction(m_actionFromClipboard);
+    }
+    if (!m_browser->properties().isEmpty()) {
+      context.addAction(m_actionToClipboard);
+    }
+    context.exec(QCursor::pos());
+  } else if (isParameter(prop)) { // parameters
+    QMenu context(this);
+    if (hasTie(prop)) {
+      context.addAction(m_actionRemoveTie);
+    } else {
+      context.addAction(m_actionFixParameter);
+      context.addAction(m_actionAddTie);
+    }
+    bool hasLower = hasLowerBound(prop);
+    bool hasUpper = hasUpperBound(prop);
+    if (!hasLower && !hasUpper) {
+      QMenu *constraintMenu = new QMenu("Constraints", this);
+      constraintMenu->addAction(m_actionConstraints10);
+      constraintMenu->addAction(m_actionConstraints50);
+      constraintMenu->addAction(m_actionConstraints);
+      context.addMenu(constraintMenu);
+    } else {
+      context.addAction(m_actionRemoveConstraints);
+    }
+    context.exec(QCursor::pos());
+  } else if (isConstraint(prop)) { // constraints
+    QMenu context(this);
+    context.addAction(m_actionRemoveConstraint);
+    context.exec(QCursor::pos());
+  }
+}
+
+/**
+ * Add a function to currently selected composite function property
+ */
+void FunctionTreeView::addFunction() {
+  QString newFunction;
+
+  auto item = m_browser->currentItem();
+  QtProperty *prop = nullptr;
+  if (item) {
+    prop = item->property();
+    if (!isFunction(prop))
+      return;
+  }
+
+  // check if the browser is empty
+  if (!prop) {
+    auto top = m_browser->properties();
+    if (!top.isEmpty()) {
+      prop = top[0];
+      if (!isFunction(prop))
+        return;
+    }
+  }
+
+  // Get new function type
+  newFunction = getUserFunctionFromDialog();
+  if (newFunction.isEmpty())
+    return;
+
+  // create new function
+  auto f = Mantid::API::FunctionFactory::Instance().createFunction(
+      newFunction.toStdString());
+
+  if (prop) { // there are other functions defined
+    Mantid::API::IFunction_sptr fun =
+        Mantid::API::FunctionFactory::Instance().createFunction(
+            prop->propertyName().toStdString());
+    auto cf = boost::dynamic_pointer_cast<Mantid::API::CompositeFunction>(fun);
+    if (cf) {
+      addFunction(prop, f);
+    } else {
+      cf.reset(new Mantid::API::CompositeFunction);
+      auto f0 = getFunction(prop);
+      if (f0) {
+        cf->addFunction(f0);
+      }
+      cf->addFunction(f);
+      setFunction(cf);
+    }
+  } else { // the browser is empty - add first function
+    addFunction(nullptr, f);
+  }
+  //emit functionStructureChanged();
+}
+
+/**
+ * Ask user to select a function and return it
+ * @returns :: function string
+ */
+QString FunctionTreeView::getUserFunctionFromDialog() {
+  SelectFunctionDialog dlg(this);
+  if (dlg.exec() == QDialog::Accepted) {
+    return dlg.getFunction();
+  } else {
+    return QString();
+  }
+}
+
+/**
+ * Set value of an attribute (as a property) to a function.
+ * @param fun :: Function to which attribute is set.
+ * @param prop :: A property with the name and value of the attribute.
+ */
+void FunctionTreeView::setAttributeToFunction(Mantid::API::IFunction &fun,
+                                             QtProperty *prop) {
+  std::string attName = prop->propertyName().toStdString();
+  SetAttributeFromProperty setter(this, prop);
+  Mantid::API::IFunction::Attribute attr = fun.getAttribute(attName);
+  attr.apply(setter);
+  try {
+    fun.setAttribute(attName, attr);
+  } catch (std::exception &expt) {
+    QMessageBox::critical(this, "MantidPlot - Error",
+                          "Cannot set attribute " +
+                              QString::fromStdString(attName) +
+                              " of function " + prop->propertyName() + ":\n\n" +
+                              QString::fromStdString(expt.what()));
+  }
+}
+
+/**
+ * Return the function
+ * @param prop :: Function property
+ * @param attributesOnly :: Only set attributes
+ */
+Mantid::API::IFunction_sptr FunctionTreeView::getFunction(QtProperty *prop,
+                                                         bool attributesOnly) {
+  if (prop == nullptr) { // get overall function
+    auto props = m_browser->properties();
+    if (props.isEmpty())
+      return Mantid::API::IFunction_sptr();
+    prop = props[0];
+  }
+  if (!isFunction(prop))
+    return Mantid::API::IFunction_sptr();
+
+  // construct the function
+  auto fun = Mantid::API::FunctionFactory::Instance().createFunction(
+      prop->propertyName().toStdString());
+  auto cf = boost::dynamic_pointer_cast<Mantid::API::CompositeFunction>(fun);
+  if (cf) {
+    auto children = prop->subProperties();
+    foreach (QtProperty *child, children) {
+      if (isFunction(child)) {
+        auto f = getFunction(child);
+        // if f is null ignore that function
+        if (f) {
+          cf->addFunction(f);
+        }
+      } else if (isAttribute(child)) {
+        setAttributeToFunction(*fun, child);
+      }
+    }
+  } else {
+    // loop over the children properties and set parameters and attributes
+    auto children = prop->subProperties();
+    foreach (QtProperty *child, children) {
+      if (isAttribute(child)) {
+        setAttributeToFunction(*fun, child);
+      } else if (!attributesOnly && isParameter(child)) {
+        fun->setParameter(child->propertyName().toStdString(),
+                          getParameter(child));
+      }
+    }
+  }
+
+  // if this flag is set the function requires attributes only
+  // attempts to set other properties may result in exceptions
+  if (attributesOnly)
+    return fun;
+
+  // add ties
+  {
+    auto from = m_ties.lowerBound(prop);
+    auto to = m_ties.upperBound(prop);
+    // ties can become invalid after some editing
+    QList<QtProperty *> failedTies;
+    for (auto it = from; it != to; ++it) {
+      auto const tie =
+          (it->paramName + "=" + m_tieManager->value(it.value().tieProp))
+              .toStdString();
+      try {
+        fun->addTies(tie);
+      } catch (...) {
+        failedTies << it.value().tieProp;
+        g_log.warning() << "Invalid tie has been removed: " << tie << std::endl;
+      }
+    }
+    // remove failed ties from the browser
+    foreach (QtProperty *p, failedTies) {
+      removeProperty(p);
+    }
+  }
+
+  // add constraints
+  {
+    auto from = m_constraints.lowerBound(prop);
+    auto to = m_constraints.upperBound(prop);
+    for (auto it = from; it != to; ++it) {
+      try {
+        const auto &localParam = it.value();
+        const auto lower = m_constraintManager->value(localParam.lower);
+        const auto upper = m_constraintManager->value(localParam.upper);
+        const auto constraint =
+            getConstraint(localParam.paramProp->propertyName(), lower, upper);
+        fun->addConstraints(constraint.toStdString());
+      } catch (...) {
+      }
+    }
+  }
+
+  return fun;
+}
+
+/**
+ * Updates the function parameter value
+ * @param funcIndex :: Index of the function
+ * @param paramName :: Parameter name
+ * @param value :: New value
+ */
+void FunctionTreeView::setParameter(const QString &funcIndex,
+                                   const QString &paramName, double value) {
+  auto prop = getParameterProperty(funcIndex, paramName);
+  m_parameterManager->setValue(prop, value);
+}
+
+/**
+ * Updates the function parameter error
+ * @param funcIndex :: Index of the function
+ * @param paramName :: Parameter name
+ * @param error :: New error
+ */
+void FunctionTreeView::setParamError(const QString &funcIndex,
+                                    const QString &paramName, double error) {
+  if (auto prop = getFunctionProperty(funcIndex)) {
+    auto children = prop->subProperties();
+    foreach (QtProperty *child, children) {
+      if (isParameter(child) && child->propertyName() == paramName) {
+        m_parameterManager->setError(child, error);
+        break;
+      }
+    }
+  }
+}
+
+/**
+ * Get a value of a parameter
+ * @param funcIndex :: Index of the function
+ * @param paramName :: Parameter name
+ */
+double FunctionTreeView::getParameter(const QString &funcIndex,
+                                     const QString &paramName) const {
+  auto prop = getParameterProperty(funcIndex, paramName);
+  return m_parameterManager->value(prop);
+}
+
+/**
+ * Split a qualified parameter name into function index and local parameter
+ * name.
+ * @param paramName :: Fully qualified parameter name (includes function index)
+ * @return :: A string list with the first item is the function index and the
+ * second
+ *   item is the param local name.
+ */
+QStringList
+FunctionTreeView::splitParameterName(const QString &paramName) const {
+  QString functionIndex;
+  QString parameterName = paramName;
+  int j = paramName.lastIndexOf('.');
+  if (j > 0) {
+    ++j;
+    functionIndex = paramName.mid(0, j);
+    parameterName = paramName.mid(j);
+  }
+  QStringList res;
+  res << functionIndex << parameterName;
+  return res;
+}
+
+/// Get a property for a parameter
+QtProperty *
+FunctionTreeView::getParameterProperty(const QString &paramName) const {
+  QStringList name = splitParameterName(paramName);
+  return getParameterProperty(name[0], name[1]);
+}
+
+/// Get a property for a parameter
+QtProperty *
+FunctionTreeView::getParameterProperty(const QString &funcIndex,
+                                      const QString &paramName) const {
+  if (auto prop = getFunctionProperty(funcIndex)) {
+    auto children = prop->subProperties();
+    foreach (QtProperty *child, children) {
+      if (isParameter(child) && child->propertyName() == paramName) {
+        return child;
+      }
+    }
+  }
+  std::string message = "Unknown function parameter " +
+                        (funcIndex + paramName).toStdString() +
+                        "\n\n This may happen if there is a CompositeFunction "
+                        "containing only one function.";
+  throw std::runtime_error(message);
+}
+
+/// Get a property for a parameter which is a parent of a given
+/// property (tie or constraint).
+QtProperty *
+FunctionTreeView::getParentParameterProperty(QtProperty *prop) const {
+  for (auto &tie : m_ties) {
+    if (tie.tieProp == prop) {
+      return tie.paramProp;
+    }
+  }
+
+  for (auto &constraint : m_constraints) {
+    if (constraint.lower == prop || constraint.upper == prop) {
+      return constraint.paramProp;
+    }
+  }
+
+  throw std::logic_error(
+      "QtProperty " + prop->propertyName().toStdString() +
+      " is not a child of a property for any function parameter.");
+}
+
+/**
+ * Remove the function under currently selected property
+ */
+void FunctionTreeView::removeFunction() {
+  auto item = m_browser->currentItem();
+  if (!item)
+    return;
+  QtProperty *prop = item->property();
+  if (!isFunction(prop))
+    return;
+
+  removeProperty(prop);
+  updateFunctionIndices();
+
+  // After removing a function we could end up with
+  // a CompositeFunction with only one function
+  // In this case, the function should be kept but
+  // the composite function should be removed
+  auto props = m_browser->properties();
+  if (!props.isEmpty()) {
+    // The function browser is not empty
+
+    // Check if the current function in the browser is a
+    // composite function
+    auto topProp = props[0];
+    auto fun = Mantid::API::FunctionFactory::Instance().createFunction(
+        topProp->propertyName().toStdString());
+    auto cf = boost::dynamic_pointer_cast<Mantid::API::CompositeFunction>(fun);
+    if (cf) {
+      // If it is a composite function
+      // check that there are more than one function
+      // which means more than two subproperties
+      size_t nFunctions = props[0]->subProperties().size() - 1;
+
+      if (nFunctions == 1) {
+        // If only one function remains, remove the composite function:
+        // Temporary copy the remaining function
+        auto func = getFunction(m_browser->properties()[0]->subProperties()[1]);
+        // Remove the composite function
+        m_browser->removeProperty(topProp);
+        // Add the temporary stored function
+        setFunction(func);
+      }
+    }
+  }
+}
+
+/**
+ * Fix currently selected parameter
+ */
+void FunctionTreeView::fixParameter() {
+  auto item = m_browser->currentItem();
+  if (!item)
+    return;
+  QtProperty *prop = item->property();
+  if (!isParameter(prop))
+    return;
+  QString tie = QString::number(getParameter(prop));
+  addTieProperty(prop, tie);
+  emit tiesChanged();
+}
+
+/// Get a tie property attached to a parameter property
+QtProperty *FunctionTreeView::getTieProperty(QtProperty *prop) const {
+  auto children = prop->subProperties();
+  foreach (QtProperty *child, children) {
+    if (child->propertyName() == "Tie") {
+      return child;
+    }
+  }
+  return nullptr;
+}
+
+/**
+ * Unfix currently selected parameter
+ */
+void FunctionTreeView::removeTie() {
+  auto item = m_browser->currentItem();
+  if (!item)
+    return;
+  QtProperty *prop = item->property();
+  if (!isParameter(prop))
+    return;
+  auto tieProp = getTieProperty(prop);
+  if (tieProp) {
+    removeProperty(tieProp);
+  }
+  emit tiesChanged();
+}
+
+/**
+ * Add a custom tie to currently selected parameter
+ */
+void FunctionTreeView::addTie() {
+  auto item = m_browser->currentItem();
+  if (!item)
+    return;
+  QtProperty *prop = item->property();
+  if (!isParameter(prop))
+    return;
+
+  bool ok;
+  QString tie = QInputDialog::getText(this, "Add a tie",
+                                      "Tie:", QLineEdit::Normal, "", &ok);
+  if (ok && !tie.isEmpty()) {
+    try {
+      addTieProperty(prop, tie);
+    } catch (Mantid::API::Expression::ParsingError &) {
+      QMessageBox::critical(this, "MantidPlot - Error",
+                            "Syntax errors found in tie: " + tie);
+    }
+  }
+  emit tiesChanged();
+}
+
+/**
+ * Copy function from the clipboard
+ */
+void FunctionTreeView::copyFromClipboard() {
+  QString funStr = QApplication::clipboard()->text();
+  if (funStr.isEmpty())
+    return;
+  try {
+    auto fun = Mantid::API::FunctionFactory::Instance().createInitialized(
+        funStr.toStdString());
+    if (!fun)
+      return;
+    this->setFunction(fun);
+  } catch (...) {
+    // text in the clipboard isn't a function definition
+    QMessageBox::warning(this, "MantidPlot - Warning",
+                         "Text in the clipboard isn't a function definition"
+                         " or contains errors.");
+  }
+}
+
+/**
+ * Copy function to the clipboard
+ */
+void FunctionTreeView::copyToClipboard() {
+  auto fun = getFunction();
+  if (fun) {
+    QApplication::clipboard()->setText(QString::fromStdString(fun->asString()));
+  }
+}
+
+/**
+ * Add both constraints to current parameter
+ */
+void FunctionTreeView::addConstraints() {
+  auto item = m_browser->currentItem();
+  if (!item)
+    return;
+  QtProperty *prop = item->property();
+  if (!isParameter(prop))
+    return;
+  addConstraintProperties(prop, "0<" + prop->propertyName() + "<0");
+  emit constraintsChanged();
+}
+
+/**
+ * Add both constraints to current parameter
+ */
+void FunctionTreeView::addConstraints10() {
+  auto item = m_browser->currentItem();
+  if (!item)
+    return;
+  QtProperty *prop = item->property();
+  if (!isParameter(prop))
+    return;
+  double val = getParameter(prop);
+  addConstraintProperties(prop, QString::number(val * 0.9) + "<" +
+                                    prop->propertyName() + "<" +
+                                    QString::number(val * 1.1));
+  emit constraintsChanged();
+}
+
+/**
+ * Add both constraints to current parameter
+ */
+void FunctionTreeView::addConstraints50() {
+  auto item = m_browser->currentItem();
+  if (!item)
+    return;
+  QtProperty *prop = item->property();
+  if (!isParameter(prop))
+    return;
+  double val = getParameter(prop);
+  addConstraintProperties(prop, QString::number(val * 0.5) + "<" +
+                                    prop->propertyName() + "<" +
+                                    QString::number(val * 1.5));
+  emit constraintsChanged();
+}
+
+/**
+ * Remove both constraints from current parameter
+ */
+void FunctionTreeView::removeConstraints() {
+  auto item = m_browser->currentItem();
+  if (!item)
+    return;
+  QtProperty *prop = item->property();
+  if (!isParameter(prop))
+    return;
+  //const bool isLocal = isLocalParameterProperty(prop);
+  auto props = prop->subProperties();
+  foreach (QtProperty *p, props) {
+    if (isConstraint(p)) {
+      removeProperty(p);
+    }
+  }
+  emit constraintsChanged();
+}
+
+/**
+ * Remove one constraint from current parameter
+ */
+void FunctionTreeView::removeConstraint() {
+  auto item = m_browser->currentItem();
+  if (!item)
+    return;
+  QtProperty *prop = item->property();
+  if (!isConstraint(prop))
+    return;
+  auto paramProp = getParentParameterProperty(prop);
+  removeProperty(prop);
+  emit constraintsChanged();
+}
+
+/**
+ * Slot connected to all function attribute managers. Update the corresponding
+ * function.
+ * @param prop :: An attribute property that was changed
+ */
+void FunctionTreeView::attributeChanged(QtProperty *prop) {
+  auto funProp = m_properties[prop].parent;
+  if (!funProp)
+    return;
+  // get function with the changed attribute (it is set from prop's value)
+  auto fun = getFunction(funProp, true);
+
+  // delete and recreate all function's properties (attributes, parameters, etc)
+  setFunction(funProp, fun);
+  updateFunctionIndices();
+  emit functionStructureChanged();
+}
+
+/** Called when the size of a vector attribute is changed
+ * @param prop :: A property that was changed.
+ */
+void FunctionTreeView::attributeVectorSizeChanged(QtProperty *prop) {
+  QtProperty *vectorProp = m_properties[prop].parent;
+  if (!vectorProp)
+    throw std::logic_error("FunctionTreeView: inconsistency in vector "
+                           "properties.\nAttribute property not found.");
+  auto funProp = m_properties[vectorProp].parent;
+  if (!funProp)
+    throw std::logic_error("FunctionTreeView: inconsistency in vector "
+                           "properties.\nFunction property not found.");
+  auto fun = getFunction(funProp, true);
+  if (!fun)
+    throw std::logic_error("FunctionTreeView: inconsistency in vector "
+                           "properties.\nFunction undefined.");
+  auto attName = vectorProp->propertyName().toStdString();
+  auto attribute = fun->getAttribute(attName).asVector();
+  auto newSize = m_attributeSizeManager->value(prop);
+  if (newSize < 0)
+    newSize = 0;
+  if (attribute.size() != static_cast<size_t>(newSize)) {
+    if (newSize == 0) {
+      attribute.clear();
+    } else {
+      attribute.resize(newSize);
+    }
+    fun->setAttributeValue(attName, attribute);
+    setFunction(funProp, fun);
+    updateFunctionIndices();
+    emit functionStructureChanged();
+  }
+}
+
+/**
+ * Slot connected to a property displaying the value of a member of a vector
+ * attribute.
+ * @param prop :: A property that was changed.
+ */
+void FunctionTreeView::attributeVectorDoubleChanged(QtProperty *prop) {
+  QtProperty *vectorProp = m_properties[prop].parent;
+  if (!vectorProp)
+    throw std::runtime_error(
+        "FunctionTreeView: inconsistency in vector properties.");
+  attributeChanged(vectorProp);
+}
+
+void FunctionTreeView::parameterChanged(QtProperty *prop) {
+  bool isGlobal = true;
+  QString newTie;
+
+  auto tieProp = getTieProperty(prop);
+  if (tieProp && !tieProp->isEnabled()) {
+    if (isGlobal) {
+      // it is a fixed tie
+      newTie = QString("%1=%2")
+                   .arg(prop->propertyName())
+                   .arg(m_parameterManager->value(prop));
+    }
+    if (!newTie.isEmpty()) {
+      m_tieManager->setValue(tieProp, newTie);
+    }
+  }
+  emit parameterChanged(getIndex(prop), prop->propertyName());
+}
+
+/// Called when a tie property changes
+void FunctionTreeView::tieChanged(QtProperty *prop) {
+  for (const auto &atie : m_ties) {
+    if (atie.tieProp == prop) {
+      emit tiesChanged();
+    }
+  }
+}
+
+/// Called when a constraint property changes
+void FunctionTreeView::constraintChanged(QtProperty *prop) {
+  for (const auto &constraint : m_constraints) {
+    const bool isLower = constraint.lower == prop;
+    const bool isUpper = constraint.upper == prop;
+    if (isLower || isUpper) {
+      emit constraintsChanged();
+    }
+  }
+}
+
+void FunctionTreeView::parameterButtonClicked(QtProperty *prop) {
+  emit localParameterButtonClicked(getIndex(prop) + prop->propertyName());
+}
+
+bool FunctionTreeView::hasFunction() const {
+  return !m_functionManager->properties().isEmpty();
+}
+
+/// Resize the browser's columns
+/// @param s0 :: New size for the first column (Parameter).
+/// @param s1 :: New size for the second column (Value).
+/// @param s2 :: New size for the third optional column (Global).
+void FunctionTreeView::setColumnSizes(int s0, int s1, int s2) {
+  m_browser->setColumnSizes(s0, s1, s2);
+}
+
+/**
+ * Emit a signal when any of the Global options change.
+ */
+void FunctionTreeView::globalChanged(QtProperty *, const QString &, bool) {
+  emit globalsChanged();
+}
+
+/**
+ * Set display of parameter errors on/off
+ * @param enabled :: [input] On/off display of errors
+ */
+void FunctionTreeView::setErrorsEnabled(bool enabled) {
+  m_parameterManager->setErrorsEnabled(enabled);
+}
+
+/**
+ * Clear all errors, if they are set
+ */
+void FunctionTreeView::clearErrors() { m_parameterManager->clearErrors(); }
+
+} // namespace MantidWidgets
+} // namespace MantidQt
-- 
GitLab