Skip to content
Snippets Groups Projects
FitPropertyBrowser.cpp 107 KiB
Newer Older
#include "MantidQtWidgets/Common/FitPropertyBrowser.h"
#include "MantidQtWidgets/Common/HelpWindow.h"
#include "MantidQtWidgets/Common/MantidDesktopServices.h"
#include "MantidQtWidgets/Common/MultifitSetupDialog.h"
#include "MantidQtWidgets/Common/PropertyHandler.h"
#include "MantidQtWidgets/Common/SequentialFitDialog.h"

#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/CompositeFunction.h"
#include "MantidAPI/CostFunctionFactory.h"
#include "MantidAPI/FrameworkManager.h"
#include "MantidAPI/FuncMinimizerFactory.h"
#include "MantidAPI/IBackgroundFunction.h"
#include "MantidAPI/IFuncMinimizer.h"
#include "MantidAPI/IPeakFunction.h"
#include "MantidAPI/ITableWorkspace.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/ParameterTie.h"
#include "MantidAPI/TableRow.h"
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidKernel/ConfigService.h"
#include "MantidKernel/Logger.h"
#include "MantidQtWidgets/Common/QtPropertyBrowser/FilenameDialogEditor.h"
#include "MantidQtWidgets/Common/QtPropertyBrowser/FormulaDialogEditor.h"
#include "MantidQtWidgets/Common/QtPropertyBrowser/StringEditorFactory.h"
#include "MantidQtWidgets/Common/QtPropertyBrowser/DoubleEditorFactory.h"
#include "MantidQtWidgets/Common/QtPropertyBrowser/ParameterPropertyManager.h"
#include "MantidQtWidgets/Common/QtPropertyBrowser/qteditorfactory.h"
#include "MantidQtWidgets/Common/QtPropertyBrowser/qttreepropertybrowser.h"
#include <Poco/ActiveResult.h>

#include <QApplication>
#include <QClipboard>
#include <QGridLayout>
#include <QInputDialog>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QSettings>
#include <QTreeWidget>
#include <QVBoxLayout>

#include <algorithm>

using API::MantidDesktopServices;

namespace MantidWidgets {
namespace {
Mantid::Kernel::Logger g_log("FitPropertyBrowser");
}

/**
 * Constructor
 * @param parent :: The parent widget - must be an ApplicationWindow
 * @param mantidui :: The UI form for MantidPlot
FitPropertyBrowser::FitPropertyBrowser(QWidget *parent, QObject *mantidui)
    : QDockWidget("Fit Function", parent), m_workspaceIndex(nullptr),
      m_startX(nullptr), m_endX(nullptr), m_output(nullptr),
      m_minimizer(nullptr), m_ignoreInvalidData(nullptr),
      m_costFunction(nullptr), m_maxIterations(nullptr), m_peakRadius(nullptr),
      m_logValue(nullptr), m_plotDiff(nullptr), m_plotCompositeMembers(nullptr),
      m_convolveMembers(nullptr), m_rawData(nullptr), m_xColumn(nullptr),
      m_yColumn(nullptr), m_errColumn(nullptr), m_showParamErrors(nullptr),
      m_evaluationType(nullptr), m_compositeFunction(), m_browser(nullptr),
      m_fitActionUndoFit(nullptr), m_fitActionSeqFit(nullptr),
      m_fitActionFit(nullptr), m_fitActionEvaluate(nullptr),
      m_functionsGroup(nullptr), m_settingsGroup(nullptr),
      m_customSettingsGroup(nullptr), m_changeSlotsEnabled(false),
      m_guessOutputName(true),
      m_updateObserver(*this, &FitPropertyBrowser::handleFactoryUpdate),
      m_fitMapper(nullptr), m_fitMenu(nullptr),
      m_displayActionPlotGuess(nullptr), m_displayActionQuality(nullptr),
      m_displayActionClearAll(nullptr), m_setupActionCustomSetup(nullptr),
      m_setupActionRemove(nullptr), m_tip(nullptr), m_fitSelector(nullptr),
Peterson, Peter's avatar
Peterson, Peter committed
      m_fitTree(nullptr), m_currentHandler(nullptr),
      m_defaultFunction("Gaussian"), m_defaultPeak("Gaussian"),
      m_defaultBackground("LinearBackground"), m_index_(0), m_peakToolOn(false),
      m_auto_back(false),
      m_autoBgName(QString::fromStdString(
          Mantid::Kernel::ConfigService::Instance().getString(
              "curvefitting.autoBackground"))),
      m_autoBackground(nullptr), m_decimals(-1), m_mantidui(mantidui),
      m_shouldBeNormalised(false) {
  Mantid::API::FrameworkManager::Instance().loadPlugins();
  // Try to create a Gaussian. Failing will mean that CurveFitting dll is not
  // loaded
  boost::shared_ptr<Mantid::API::IFunction> f =
      boost::shared_ptr<Mantid::API::IFunction>(
          Mantid::API::FunctionFactory::Instance().createFunction("Gaussian"));
  if (m_autoBgName.toLower() == "none") {
    m_autoBgName = "";
    setAutoBackgroundName(m_autoBgName);
  }

  std::string def = Mantid::Kernel::ConfigService::Instance().getString(
      "curvefitting.defaultPeak");
  if (!def.empty()) {
    m_defaultPeak = def;
  }

  def = Mantid::Kernel::ConfigService::Instance().getString(
      "curvefitting.autoBackground");
  if (!def.empty()) {
    m_defaultBackground = def;
  }
  m_defaultFunction = m_defaultPeak;

  setObjectName(
      "FitFunction"); // this is needed for QMainWindow::restoreState()
  setMinimumHeight(150);
  setMinimumWidth(200);

  QWidget *w = new QWidget(this);
  /* Create property managers: they create, own properties, get and set values
   */
  m_groupManager = new QtGroupPropertyManager(w);
  m_doubleManager = new QtDoublePropertyManager(w);
  m_stringManager = new QtStringPropertyManager(w);
  m_enumManager = new QtEnumPropertyManager(w);
  m_intManager = new QtIntPropertyManager(w);
  m_boolManager = new QtBoolPropertyManager(w);
  m_filenameManager = new QtStringPropertyManager(w);
  m_formulaManager = new QtStringPropertyManager(w);
  m_columnManager = new QtEnumPropertyManager(w);
  m_workspace = m_enumManager->addProperty("Workspace");
  m_vectorManager = new QtGroupPropertyManager(w);
  m_vectorSizeManager = new QtIntPropertyManager(w);
  m_vectorDoubleManager = new QtDoublePropertyManager(w);
  m_parameterManager = new ParameterPropertyManager(w);
 * Initialise the fit property browser
 */
void FitPropertyBrowser::init() {
  QWidget *w = new QWidget(this);
  QSettings settings;
  settings.beginGroup("Mantid/FitBrowser");
  QtProperty *functionsGroup = m_groupManager->addProperty("Functions");
  connect(this, SIGNAL(xRangeChanged(double, double)), m_mantidui,
          SLOT(x_range_from_picker(double, double)));
  /* Create input - output properties */
  QtProperty *settingsGroup = m_groupManager->addProperty("Settings");
  m_startX = addDoubleProperty("StartX");
  m_endX = addDoubleProperty("EndX");

  m_workspaceIndex = m_intManager->addProperty("Workspace Index");
  m_output = m_stringManager->addProperty("Output");
  m_minimizer = m_enumManager->addProperty("Minimizer");
  m_minimizers << "Levenberg-Marquardt"
               << "Simplex"
DiegoMonserrat's avatar
DiegoMonserrat committed
               << "FABADA"
               << "Conjugate gradient (Fletcher-Reeves imp.)"
               << "Conjugate gradient (Polak-Ribiere imp.)"
               << "Damped GaussNewton";

  m_ignoreInvalidData = m_boolManager->addProperty("Ignore invalid data");
  setIgnoreInvalidData(settings.value("Ignore invalid data", false).toBool());
  m_enumManager->setEnumNames(m_minimizer, m_minimizers);
  m_costFunction = m_enumManager->addProperty("Cost function");
  m_costFunctions << "Least squares"
                  << "Rwp"
                  << "Unweighted least squares";
  m_enumManager->setEnumNames(m_costFunction, m_costFunctions);
  m_maxIterations = m_intManager->addProperty("Max Iterations");
  m_intManager->setValue(m_maxIterations,
                         settings.value("Max Iterations", 500).toInt());
  m_peakRadius = m_intManager->addProperty("Peak Radius");
  m_intManager->setValue(m_peakRadius,
                         settings.value("Peak Radius", 0).toInt());

  m_plotDiff = m_boolManager->addProperty("Plot Difference");
  bool plotDiff = settings.value("Plot Difference", QVariant(true)).toBool();
  m_boolManager->setValue(m_plotDiff, plotDiff);

  m_plotCompositeMembers = m_boolManager->addProperty("Plot Composite Members");
  bool plotCompositeItems =
      settings.value(m_plotCompositeMembers->propertyName(), QVariant(false))
          .toBool();
  m_boolManager->setValue(m_plotCompositeMembers, plotCompositeItems);
  m_convolveMembers = m_boolManager->addProperty("Convolve Composite Members");
  bool convolveCompositeItems =
      settings.value(m_plotCompositeMembers->propertyName(), QVariant(false))
          .toBool();
  m_boolManager->setValue(m_convolveMembers, convolveCompositeItems);

  m_showParamErrors = m_boolManager->addProperty("Show Parameter Errors");
  bool showParamErrors =
      settings.value(m_showParamErrors->propertyName(), false).toBool();
  m_boolManager->setValue(m_showParamErrors, showParamErrors);
  m_parameterManager->setErrorsEnabled(showParamErrors);

  m_evaluationType = m_enumManager->addProperty("Evaluate Function As");
  m_evaluationType->setToolTip(
      "Consider using Histogram fit which may produce more accurate results.");
  m_evaluationTypes << "CentrePoint"
                    << "Histogram";
  m_enumManager->setEnumNames(m_evaluationType, m_evaluationTypes);
  int evaluationType =
      settings.value(m_evaluationType->propertyName(), 0).toInt();
  m_enumManager->setValue(m_evaluationType, evaluationType);
  m_xColumn = m_columnManager->addProperty("XColumn");
  m_yColumn = m_columnManager->addProperty("YColumn");
  m_errColumn = m_columnManager->addProperty("ErrColumn");
  settingsGroup->addSubProperty(m_workspace);
  settingsGroup->addSubProperty(m_startX);
  settingsGroup->addSubProperty(m_endX);

  // Only include the cost function when in the dock widget inside mantid plot,
  // not on muon analysis widget
  // Include minimiser and plot difference under a different settings section.
  settingsGroup->addSubProperty(m_output);
  settingsGroup->addSubProperty(m_minimizer);
  settingsGroup->addSubProperty(m_ignoreInvalidData);
  settingsGroup->addSubProperty(m_costFunction);
  settingsGroup->addSubProperty(m_maxIterations);
  settingsGroup->addSubProperty(m_peakRadius);
  settingsGroup->addSubProperty(m_plotDiff);
  settingsGroup->addSubProperty(m_plotCompositeMembers);
  settingsGroup->addSubProperty(m_convolveMembers);
  settingsGroup->addSubProperty(m_showParamErrors);
  settingsGroup->addSubProperty(m_evaluationType);
  /* Create editors and assign them to the managers */

  updateDecimals();
  m_functionsGroup = m_browser->addProperty(functionsGroup);
  m_settingsGroup = m_browser->addProperty(settingsGroup);
 * @brief Initialise the layout.
 * This initialization includes:
 *   1. SIGNALs/SLOTs when properties change.
 *   2. Action menus and associated SIGNALs/SLOTs.
 *   3. Initialize the CompositeFunction, the root from which to build the
 * Model. 4. Update the list of available functions
 * @param w widget parenting the action menus and the property tree browser
 */
Anthony Lim's avatar
Anthony Lim committed
void FitPropertyBrowser::initLayout(QWidget *w) { initBasicLayout(w); }
 * @brief Initialise the layout for the fit button.
 * This initialization includes:
 *   1. SIGNALs/SLOTs when properties change.
 *   2. Actions and associated SIGNALs/SLOTs.
 * @param w widget parenting the action menus and the property tree browser
 * @return push botton for the fit menu
 */
QPushButton *FitPropertyBrowser::createFitMenuButton(QWidget *w) {
Anthony Lim's avatar
Anthony Lim committed
  QPushButton *btnFit = new QPushButton("Fit");
  m_tip = new QLabel("", w);

  m_fitMapper = new QSignalMapper(this);
  m_fitMenu = new QMenu(this);
Anthony Lim's avatar
Anthony Lim committed
  populateFitMenuButton(m_fitMapper, m_fitMenu);
Anthony Lim's avatar
Anthony Lim committed
  connect(m_fitMapper, SIGNAL(mapped(const QString &)), this,
          SLOT(executeFitMenu(const QString &)));
  btnFit->setMenu(m_fitMenu);
  return btnFit;
 * @brief Populate the fit button.
 * This initialization includes:
 *   1. SIGNALs/SLOTs when properties change.
 *   2. Actions and associated SIGNALs/SLOTs.
 * @param fitMapper the QMap to the fit mapper
 * @param fitMenu the QMenu for the fit button
 */
Anthony Lim's avatar
Anthony Lim committed
void FitPropertyBrowser::populateFitMenuButton(QSignalMapper *fitMapper,
                                               QMenu *fitMenu) {
  // assert(fitmapper);

  m_fitActionFit = new QAction("Fit", this);
  m_fitActionSeqFit = new QAction("Sequential Fit", this);
  m_fitActionUndoFit = new QAction("Undo Fit", this);
  m_fitActionEvaluate = new QAction("Evaluate function", this);

  fitMapper->setMapping(m_fitActionFit, "Fit");
  fitMapper->setMapping(m_fitActionSeqFit, "SeqFit");
  fitMapper->setMapping(m_fitActionUndoFit, "UndoFit");
  fitMapper->setMapping(m_fitActionEvaluate, "Evaluate");

  connect(m_fitActionFit, SIGNAL(triggered()), fitMapper, SLOT(map()));
Anthony Lim's avatar
Anthony Lim committed
  connect(m_fitActionSeqFit, SIGNAL(triggered()), fitMapper, SLOT(map()));
  connect(m_fitActionUndoFit, SIGNAL(triggered()), fitMapper, SLOT(map()));
Anthony Lim's avatar
Anthony Lim committed
  connect(m_fitActionEvaluate, SIGNAL(triggered()), fitMapper, SLOT(map()));

  fitMenu->addAction(m_fitActionFit);
  fitMenu->addAction(m_fitActionSeqFit);
  fitMenu->addAction(m_fitActionEvaluate);
  fitMenu->addSeparator();
  fitMenu->addAction(m_fitActionUndoFit);
  fitMenu->addSeparator();
 * @brief Initialise the layout, except for the fit button in the menu bar.
 * This initialization includes:
 *   1. SIGNALs/SLOTs when properties change.
 *   2. Action menus and associated SIGNALs/SLOTs.
 *   3. Initialize the CompositeFunction, the root from which to build the
 * Model. 4. Update the list of available functions
 * @param w widget parenting the action menus and the property tree browser
 */
void FitPropertyBrowser::initBasicLayout(QWidget *w) {
Anthony Lim's avatar
Anthony Lim committed
  QPushButton *btnFit = createFitMenuButton(w);
  // to be able to change windows title from tread
  connect(this, SIGNAL(changeWindowTitle(const QString &)), this,
          SLOT(setWindowTitle(const QString &)));

  /* Create the top level group */


  connect(m_enumManager, SIGNAL(propertyChanged(QtProperty *)), this,
          SLOT(enumChanged(QtProperty *)));
  connect(m_boolManager, SIGNAL(propertyChanged(QtProperty *)), this,
          SLOT(boolChanged(QtProperty *)));
  connect(m_intManager, SIGNAL(propertyChanged(QtProperty *)), this,
          SLOT(intChanged(QtProperty *)));
  connect(m_doubleManager, SIGNAL(propertyChanged(QtProperty *)), this,
          SLOT(doubleChanged(QtProperty *)));
  connect(m_stringManager, SIGNAL(propertyChanged(QtProperty *)), this,
          SLOT(stringChanged(QtProperty *)));
  connect(m_filenameManager, SIGNAL(propertyChanged(QtProperty *)), this,
          SLOT(stringChanged(QtProperty *)));
  connect(m_formulaManager, SIGNAL(propertyChanged(QtProperty *)), this,
          SLOT(stringChanged(QtProperty *)));
  connect(m_columnManager, SIGNAL(propertyChanged(QtProperty *)), this,
          SLOT(columnChanged(QtProperty *)));
  connect(m_vectorDoubleManager, SIGNAL(propertyChanged(QtProperty *)), this,
          SLOT(vectorDoubleChanged(QtProperty *)));
  connect(m_parameterManager, SIGNAL(propertyChanged(QtProperty *)), this,
          SLOT(parameterChanged(QtProperty *)));
  connect(m_vectorSizeManager, SIGNAL(propertyChanged(QtProperty *)), this,
          SLOT(vectorSizeChanged(QtProperty *)));

  QVBoxLayout *layout = new QVBoxLayout(w);
  QGridLayout *buttonsLayout = new QGridLayout();

  QPushButton *btnDisplay = new QPushButton("Display");
  QMenu *displayMenu = new QMenu(this);
  m_displayActionPlotGuess = new QAction("Plot Guess", this);
  m_displayActionPlotGuess->setEnabled(false);
  m_displayActionQuality = new QAction("Quality", this);
  m_displayActionQuality->setCheckable(true);
  m_displayActionQuality->setChecked(true);
  m_displayActionClearAll = new QAction("Clear fit curves", this);
  QSignalMapper *displayMapper = new QSignalMapper(this);

  displayMapper->setMapping(m_displayActionPlotGuess, "PlotGuess");
  displayMapper->setMapping(m_displayActionQuality, "Quality");
  displayMapper->setMapping(m_displayActionClearAll, "ClearAll");
  connect(m_displayActionPlotGuess, SIGNAL(triggered()), displayMapper,
  connect(m_displayActionQuality, SIGNAL(triggered()), displayMapper,
  connect(m_displayActionClearAll, SIGNAL(triggered()), displayMapper,
          SLOT(map()));
  connect(displayMapper, SIGNAL(mapped(const QString &)), this,
          SLOT(executeDisplayMenu(const QString &)));
  displayMenu->addAction(m_displayActionPlotGuess);
  displayMenu->addAction(m_displayActionClearAll);
  displayMenu->addAction(m_displayActionQuality);
  QPushButton *btnSetup = new QPushButton("Setup");
  QMenu *setupMenu = new QMenu(this);
  m_setupActionCustomSetup = new QAction("Custom Setup", this);
  QAction *setupActionManageSetup = new QAction("Manage Setup", this);
  QAction *setupActionFindPeaks = new QAction("Find Peaks", this);
  QAction *setupActionClearFit = new QAction("Clear Model", this);
  QMenu *setupSubMenuCustom = new QMenu(this);
  m_setupActionCustomSetup->setMenu(setupSubMenuCustom);
  // empty menu for now, so set it disabled to avoid confusing users
  m_setupActionCustomSetup->setEnabled(false);
  QMenu *setupSubMenuManage = new QMenu(this);
  QAction *setupActionSave = new QAction("Save Setup", this);
  m_setupActionRemove = new QAction("Remove Setup", this);
  QAction *setupActionCopyToClipboard = new QAction("Copy To Clipboard", this);
  QAction *setupActionLoadFromString = new QAction("Load From String", this);
  QSignalMapper *setupManageMapper = new QSignalMapper(this);
  setupManageMapper->setMapping(setupActionSave, "SaveSetup");
  setupManageMapper->setMapping(setupActionCopyToClipboard, "CopyToClipboard");
  setupManageMapper->setMapping(setupActionLoadFromString, "LoadFromString");
  connect(setupActionSave, SIGNAL(triggered()), setupManageMapper, SLOT(map()));
  connect(setupActionCopyToClipboard, SIGNAL(triggered()), setupManageMapper,
  connect(setupActionLoadFromString, SIGNAL(triggered()), setupManageMapper,
          SLOT(map()));
  connect(setupManageMapper, SIGNAL(mapped(const QString &)), this,
          SLOT(executeSetupManageMenu(const QString &)));
  setupSubMenuManage->addAction(setupActionSave);
  setupSubMenuManage->addAction(m_setupActionRemove);
  setupSubMenuManage->addAction(setupActionCopyToClipboard);
  setupSubMenuManage->addAction(setupActionLoadFromString);
  setupActionManageSetup->setMenu(setupSubMenuManage);
  QMenu *setupSubMenuRemove = new QMenu(this);
  m_setupActionRemove->setMenu(setupSubMenuRemove);
  // empty menu for now, so set it disabled to avoid confusing users
  m_setupActionRemove->setEnabled(false);
  QSignalMapper *setupMapper = new QSignalMapper(this);
  setupMapper->setMapping(setupActionClearFit, "ClearFit");
  setupMapper->setMapping(setupActionFindPeaks, "FindPeaks");
  connect(setupActionClearFit, SIGNAL(triggered()), setupMapper, SLOT(map()));
  connect(setupActionFindPeaks, SIGNAL(triggered()), setupMapper, SLOT(map()));
  connect(setupMapper, SIGNAL(mapped(const QString &)), this,
          SLOT(executeSetupMenu(const QString &)));

  setupMenu->addAction(m_setupActionCustomSetup);
  setupMenu->addAction(setupActionManageSetup);
  setupMenu->addSeparator();
  setupMenu->addAction(setupActionFindPeaks);
  setupMenu->addSeparator();
  setupMenu->addAction(setupActionClearFit);
  buttonsLayout->addWidget(btnFit, 0, 0);
  buttonsLayout->addWidget(btnDisplay, 0, 1);
  buttonsLayout->addWidget(btnSetup, 0, 2);
  m_status = new QLabel("Status:", w);
  m_status->hide();
Anthony Lim's avatar
Anthony Lim committed
  connect(this, SIGNAL(fitResultsChanged(const QString &)), this,
          SLOT(showFitResultStatus(const QString &)), Qt::QueuedConnection);
  layout->addWidget(m_status);
  layout->addLayout(buttonsLayout);
  layout->addWidget(m_tip);
  layout->addWidget(m_browser);

  setWidget(w);

  m_browser->setContextMenuPolicy(Qt::CustomContextMenu);
  connect(m_browser, SIGNAL(customContextMenuRequested(const QPoint &)), this,
          SLOT(popupMenu(const QPoint &)));
  connect(m_browser, SIGNAL(currentItemChanged(QtBrowserItem *)), this,
          SLOT(currentItemChanged(QtBrowserItem *)));
  connect(this, SIGNAL(multifitFinished()), this,
          SLOT(processMultiBGResults()));

  createCompositeFunction();

  // Update tooltips when function structure is (or might've been) changed in
  // any way
  connect(this, SIGNAL(functionChanged()), SLOT(updateStructureTooltips()));
  connect(this, SIGNAL(functionChanged()), SLOT(clearFitResultStatus()));
  // Update available functions in this fit property browser, when the function
  // factory changes.
  using Mantid::API::FunctionFactory;
  FunctionFactory::Instance().notificationCenter.addObserver(m_updateObserver);
  connect(this, SIGNAL(functionFactoryUpdateReceived()), this,
          SLOT(populateFunctionNames()));
  FunctionFactory::Instance().enableNotifications();

  // Initial call, as function is not changed when it's created for the first
  // time
  m_changeSlotsEnabled = true;
Roman Tolchenov's avatar
Roman Tolchenov committed
  populateFunctionNames();
 * @brief Create editors and assign them to the managers.
 * Associates a particular widget factory to each property manager. Thus, the
 * factory will automatically create widgets befitting to edit the properties
 * that we define.
 * @param w :: widget showing the properties tree and the actions buttons
 */
void FitPropertyBrowser::createEditors(QWidget *w) {
  QtCheckBoxFactory *checkBoxFactory = new QtCheckBoxFactory(w);
  QtEnumEditorFactory *comboBoxFactory = new QtEnumEditorFactory(w);
  QtSpinBoxFactory *spinBoxFactory = new QtSpinBoxFactory(w);
  DoubleEditorFactory *doubleEditorFactory = new DoubleEditorFactory(w);
  StringEditorFactory *stringEditFactory = new StringEditorFactory(w);
  FilenameDialogEditorFactory *filenameDialogEditorFactory =
      new FilenameDialogEditorFactory(w);
  FormulaDialogEditorFactory *formulaDialogEditFactory =
      new FormulaDialogEditorFactory(w);

  m_browser = new QtTreePropertyBrowser();
  m_browser->setFactoryForManager(m_enumManager, comboBoxFactory);
  m_browser->setFactoryForManager(m_boolManager, checkBoxFactory);
  m_browser->setFactoryForManager(m_intManager, spinBoxFactory);
  m_browser->setFactoryForManager(m_doubleManager, doubleEditorFactory);
  m_browser->setFactoryForManager(m_stringManager, stringEditFactory);
  m_browser->setFactoryForManager(m_filenameManager,
                                  filenameDialogEditorFactory);
  m_browser->setFactoryForManager(m_formulaManager, formulaDialogEditFactory);
  m_browser->setFactoryForManager(m_columnManager, comboBoxFactory);
  m_browser->setFactoryForManager(m_vectorSizeManager, spinBoxFactory);
  m_browser->setFactoryForManager(m_vectorDoubleManager, doubleEditorFactory);
  m_browser->setFactoryForManager(m_parameterManager,
                                  new ParameterEditorFactory(w));
/// Update setup menus according to how these are set in
/// settings
void FitPropertyBrowser::updateSetupMenus() {
  QMenu *menuLoad = m_setupActionCustomSetup->menu();
  menuLoad->clear();
  QMenu *menuRemove = m_setupActionRemove->menu();
  QSettings settings;
  settings.beginGroup("Mantid/FitBrowser/SavedFunctions");
  QStringList names = settings.childKeys();

  QSignalMapper *mapperLoad = new QSignalMapper(this);
  QSignalMapper *mapperRemove = new QSignalMapper(this);
  // enable actions that open the menus only if there will be >=1 entries in
  // there
  m_setupActionCustomSetup->setEnabled(names.size() >= 1);
  m_setupActionRemove->setEnabled(names.size() >= 1);
  for (int i = 0; i < names.size(); i++) {
    QAction *itemLoad = new QAction(names.at(i), this);
    QAction *itemRemove = new QAction(names.at(i), this);
    mapperLoad->setMapping(itemLoad, names.at(i));
    mapperRemove->setMapping(itemRemove, names.at(i));
    connect(itemLoad, SIGNAL(triggered()), mapperLoad, SLOT(map()));
    connect(itemRemove, SIGNAL(triggered()), mapperRemove, SLOT(map()));
    menuLoad->addAction(itemLoad);
    menuRemove->addAction(itemRemove);
  }
  connect(mapperLoad, SIGNAL(mapped(const QString &)), this,
          SLOT(executeCustomSetupLoad(const QString &)));
  connect(mapperRemove, SIGNAL(mapped(const QString &)), this,
          SLOT(executeCustomSetupRemove(const QString &)));
}

void FitPropertyBrowser::executeCustomSetupLoad(const QString &name) {
  QSettings settings;
  settings.beginGroup("Mantid/FitBrowser/SavedFunctions");
  QStringList names = settings.childKeys();
  QString str = settings.value(name).toString();
  loadFunction(str);
}
void FitPropertyBrowser::executeCustomSetupRemove(const QString &name) {
  QSettings settings;
  settings.beginGroup("Mantid/FitBrowser/SavedFunctions");
  QStringList names = settings.childKeys();
  settings.remove(name);
  updateSetupMenus();
/**
 * Recursively updates structure tooltips for all the functions
 */
void FitPropertyBrowser::updateStructureTooltips() {
  // Call tooltip update func on the root handler - it goes down recursively
  getHandler()->updateStructureTooltip();
}

void FitPropertyBrowser::executeFitMenu(const QString &item) {
  if (item == "Fit") {
  } else if (item == "SeqFit") {
  } else if (item == "UndoFit") {
  } else if (item == "Evaluate") {
    doFit(0);
void FitPropertyBrowser::executeDisplayMenu(const QString &item) {
  if (item == "PlotGuess") {
  } else if (item == "ClearAll") {
void FitPropertyBrowser::executeSetupMenu(const QString &item) {
    clear();
  if (item == "FindPeaks")
    findPeaks();
}

void FitPropertyBrowser::executeSetupManageMenu(const QString &item) {
  if (item == "SaveSetup")
    saveFunction();
  if (item == "CopyToClipboard")
    copy();
  if (item == "LoadFromString")
    loadFunctionFromString();
}

/// Destructor
FitPropertyBrowser::~FitPropertyBrowser() { m_compositeFunction.reset(); }

/// Get handler to the root composite function
PropertyHandler *FitPropertyBrowser::getHandler() const {
  return static_cast<PropertyHandler *>(m_compositeFunction->getHandler());
PropertyHandler *FitPropertyBrowser::addFunction(const std::string &fnName) {
  PropertyHandler *h = getHandler()->addFunction(fnName);
  emit functionChanged();
  return h;
}

void FitPropertyBrowser::removeFunction(PropertyHandler *handler) {
  if (handler) {
    emit removePlotSignal(getHandler());
    handler->removeFunction();
    compositeFunction()->checkFunction();
    emit functionRemoved();
    emit functionChanged();
  }
}

/** Slot. Called to add a new function
 */
void FitPropertyBrowser::addFunction() {
  QtBrowserItem *ci = m_browser->currentItem();
  // Find the function which has ci as its top browser item
  auto cf = getHandler()->findCompositeFunction(ci);
  // Declare new widget for picking fit functions
  m_fitSelector = new QDialog();
  m_fitSelector->setModal(true);
  // QTreeWidget *m_fitTree = new QTreeWidget();
  m_fitTree = new QTreeWidget;

  // Add functions to each of the categories. If it appears in more than one
  // category then add to both
  // Store in a map. Key = category. Value = vector of fit functions belonging
  // to that category.
  std::map<std::string, std::vector<std::string>> categories;
  for (int i = 0; i < m_registeredFunctions.size(); ++i) {
    boost::shared_ptr<Mantid::API::IFunction> f =
        Mantid::API::FunctionFactory::Instance().createFunction(
            m_registeredFunctions[i].toStdString());
    std::vector<std::string> tempCategories = f->categories();
    for (size_t j = 0; j < tempCategories.size(); ++j) {
      categories[tempCategories[boost::lexical_cast<int>(j)]].push_back(
          m_registeredFunctions[i].toStdString());

  // Construct the QTreeWidget based on the map information of categories and
  // their respective fit functions.
  std::map<std::string, std::vector<std::string>>::const_iterator sItr =
      categories.end();
  for (std::map<std::string, std::vector<std::string>>::const_iterator itr =
           categories.begin();
       itr != sItr; ++itr) {
    QTreeWidgetItem *category = new QTreeWidgetItem(m_fitTree);
    category->setText(0, QString::fromStdString(itr->first));

    std::vector<std::string>::const_iterator fitItrEnd = itr->second.end();
    for (std::vector<std::string>::const_iterator fitItrBegin =
             itr->second.begin();
         fitItrBegin != fitItrEnd; ++fitItrBegin) {
      QTreeWidgetItem *fit = new QTreeWidgetItem(category);
      fit->setText(0, QString::fromStdString(fitItrBegin[0]));
  // Set the layout of the widget.
  m_fitTree->setToolTip("Select a function type and press OK.");
  m_fitTree->setHeaderLabel("Fit - Select function type");
  QDialogButtonBox *buttonBox =
      new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
  connect(buttonBox, SIGNAL(accepted()), this, SLOT(acceptFit()));
  connect(buttonBox, SIGNAL(rejected()), this, SLOT(closeFit()));
  connect(m_fitTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this,
          SLOT(acceptFit()));
  QVBoxLayout *layout = new QVBoxLayout();
  layout->addWidget(m_fitTree);
  layout->addWidget(buttonBox);
  m_fitSelector->setLayout(layout);
  m_fitSelector->show();
}

void FitPropertyBrowser::acceptFit() {
  QtBrowserItem *ci = m_browser->currentItem();
  boost::shared_ptr<const Mantid::API::CompositeFunction> cf =
      getHandler()->findCompositeFunction(ci);
  if (!cf)
  QList<QTreeWidgetItem *> items(m_fitTree->selectedItems());
  if (items.size() != 1)
    return;
  if (items[0]->parent() == nullptr)
  PropertyHandler *h = getHandler()->findHandler(cf);
  h->addFunction(items[0]->text(0).toStdString());
  emit functionChanged();
void FitPropertyBrowser::closeFit() { m_fitSelector->close(); }
/**
 * Create CompositeFunction from function pointer
 * @param func :: [input] Pointer to function
 */
void FitPropertyBrowser::createCompositeFunction(
    const Mantid::API::IFunction_sptr func) {
  if (m_compositeFunction) {
    emit functionRemoved();
    m_autoBackground = nullptr;
  if (!func) {
    m_compositeFunction.reset(new Mantid::API::CompositeFunction);
  } else {
    auto cf = boost::dynamic_pointer_cast<Mantid::API::CompositeFunction>(func);
    if (!cf || (cf->name() != "CompositeFunction" && cf->name() != "MultiBG" &&
                cf->name() != "MultiDomainFunction")) {
      m_compositeFunction.reset(new Mantid::API::CompositeFunction);
      m_compositeFunction->addFunction(func);
      m_compositeFunction = cf;
    }
  }
  setWorkspace(m_compositeFunction);

  PropertyHandler *h = new PropertyHandler(
      m_compositeFunction, Mantid::API::CompositeFunction_sptr(), this);
  m_compositeFunction->setHandler(h);
  setCurrentFunction(h);

    addAutoBackground();
  }

  disableUndo();
  setFitEnabled(m_compositeFunction->nFunctions() > 0);
  emit functionChanged();
}

/**
 * Create CompositeFunction from string
 * @param str :: [input] Function string
 */
void FitPropertyBrowser::createCompositeFunction(const QString &str) {
  if (str.isEmpty()) {
    createCompositeFunction(Mantid::API::IFunction_sptr());
  } else {
    auto f = Mantid::API::FunctionFactory::Instance().createInitialized(
        str.toStdString());
    if (f) {
      createCompositeFunction(f);
    } else {
      createCompositeFunction(Mantid::API::IFunction_sptr());
    }
  }
}

void FitPropertyBrowser::popupMenu(const QPoint &) {
  QtBrowserItem *ci = m_browser->currentItem();
  if (!ci)
    return;
  QMenu *menu = new QMenu(this);
  QAction *action;

  bool isFunctionsGroup = ci == m_functionsGroup;
  bool isSettingsGroup = ci == m_settingsGroup;
  bool isASetting = ci->parent() == m_settingsGroup;
  bool isFunction = getHandler()->findFunction(ci) != nullptr;
  bool isCompositeFunction =
      isFunction && getHandler()->findCompositeFunction(ci);
  PropertyHandler *h = getHandler()->findHandler(ci->property());
  if (isFunctionsGroup) {
    action = new QAction("Add function", this);
    connect(action, SIGNAL(triggered()), this, SLOT(addFunction()));
    menu->addAction(action);

    if (m_compositeFunction->name() == "MultiBG" &&
        m_compositeFunction->nFunctions() == 1 &&
        Mantid::API::AnalysisDataService::Instance().doesExist(
            workspaceName())) {
      action = new QAction("Setup multifit", this);
      connect(action, SIGNAL(triggered()), this, SLOT(setupMultifit()));
    if (m_peakToolOn) {
      if (h && h->hasPlot()) {
        action = new QAction("Remove plot", this);
        connect(action, SIGNAL(triggered()), this, SLOT(removeGuessAll()));
        menu->addAction(action);
      } else {
        action = new QAction("Plot", this);
        connect(action, SIGNAL(triggered()), this, SLOT(plotGuessAll()));
        menu->addAction(action);
      }
    }

    menu->addSeparator();

    action = new QAction("Save", this);
    connect(action, SIGNAL(triggered()), this, SLOT(saveFunction()));
    menu->addAction(action);

    action = new QAction("Load", this);
    connect(action, SIGNAL(triggered()), this, SLOT(loadFunction()));
    menu->addAction(action);

    action = new QAction("Copy To Clipboard", this);
    connect(action, SIGNAL(triggered()), this, SLOT(copy()));
    menu->addAction(action);

    menu->addSeparator();
    action = new QAction("Help", this);
    connect(action, SIGNAL(triggered()), this, SLOT(browserHelp()));
  } else if (isFunctionsGroup || isSettingsGroup || isASetting) {
    if (isFitEnabled()) {
      action = new QAction("Fit", this);
      connect(action, SIGNAL(triggered()), this, SLOT(fit()));
      menu->addAction(action);
    }

    if (isUndoEnabled()) {
      action = new QAction("Undo Fit", this);
      connect(action, SIGNAL(triggered()), this, SLOT(undoFit()));
      menu->addAction(action);
    }

    action = new QAction("Clear all", this);
    connect(action, SIGNAL(triggered()), this, SLOT(clear()));
    menu->addAction(action);

    action = new QAction("Help", this);
    connect(action, SIGNAL(triggered()), this, SLOT(browserHelp()));
  } else if (isFunction) {
    if (isCompositeFunction) {
      action = new QAction("Add function", this);
      connect(action, SIGNAL(triggered()), this, SLOT(addFunction()));
      menu->addAction(action);
    }

    action = new QAction("Remove", this);
    connect(action, SIGNAL(triggered()), this, SLOT(deleteFunction()));
    menu->addAction(action);

    if (m_peakToolOn) {
      if (h && h->hasPlot()) {
        action = new QAction("Remove plot", this);
        connect(action, SIGNAL(triggered()), this, SLOT(removeGuessCurrent()));
        menu->addAction(action);
      } else {
        action = new QAction("Plot", this);
        connect(action, SIGNAL(triggered()), this, SLOT(plotGuessCurrent()));
        menu->addAction(action);
      }
    }

    action = new QAction("Help", this);
    connect(action, SIGNAL(triggered()), this, SLOT(functionHelp()));
    menu->addSeparator();
    bool isParameter = h->isParameter(ci->property());
    bool isTie = !isParameter && ci->property()->propertyName() == "Tie";
    bool isLowerBound =
        !isParameter && ci->property()->propertyName() == "Lower Bound";
    bool isUpperBound =
        !isParameter && ci->property()->propertyName() == "Upper Bound";
    bool isType = isParameter && ci->property()->propertyName() == "Type";
    if (isType) {
      isParameter = false;
    }
    if (isTie) {
      action = new QAction("Remove", this);
      connect(action, SIGNAL(triggered()), this, SLOT(deleteTie()));
      menu->addAction(action);
    } else if (isLowerBound || isUpperBound) {
      action = new QAction("Remove", this);
      connect(action, SIGNAL(triggered()), this, SLOT(removeBounds()));
      menu->addAction(action);
    } else if (count() > 0 && isParameter) {
      bool hasTies;
      bool hasBounds;
      hasConstraints(ci->property(), hasTies, hasBounds);
      if (!hasTies && !hasBounds) {
        action = new QAction("Fix", this);
        connect(action, SIGNAL(triggered()), this, SLOT(addFixTie()));
        menu->addAction(action);
      }

        QMenu *constraintMenu = menu->addMenu("Constraint");

        QMenu *detailMenu = constraintMenu->addMenu("Lower Bound");
        action = new QAction("10%", this);
        connect(action, SIGNAL(triggered()), this, SLOT(addLowerBound10()));
        detailMenu->addAction(action);

        action = new QAction("50%", this);
        connect(action, SIGNAL(triggered()), this, SLOT(addLowerBound50()));
        detailMenu->addAction(action);

        action = new QAction("Custom", this);
        connect(action, SIGNAL(triggered()), this, SLOT(addLowerBound()));
        detailMenu->addAction(action);
        detailMenu = constraintMenu->addMenu("Upper Bound");

        action = new QAction("10%", this);
        connect(action, SIGNAL(triggered()), this, SLOT(addUpperBound10()));
        detailMenu->addAction(action);

        action = new QAction("50%", this);
        connect(action, SIGNAL(triggered()), this, SLOT(addUpperBound50()));
        detailMenu->addAction(action);

        action = new QAction("Custom", this);
        connect(action, SIGNAL(triggered()), this, SLOT(addUpperBound()));
        detailMenu->addAction(action);
        detailMenu = constraintMenu->addMenu("Both Bounds");

        action = new QAction("10%", this);
        connect(action, SIGNAL(triggered()), this, SLOT(addBothBounds10()));
        detailMenu->addAction(action);

        action = new QAction("50%", this);
        connect(action, SIGNAL(triggered()), this, SLOT(addBothBounds50()));
        detailMenu->addAction(action);

        action = new QAction("Custom", this);
        connect(action, SIGNAL(triggered()), this, SLOT(addBothBounds()));
        detailMenu->addAction(action);
      }

      if (hasBounds) {
        action = new QAction("Remove constraints", this);
        connect(action, SIGNAL(triggered()), this, SLOT(removeBounds()));
        menu->addAction(action);
      }

      if (!hasTies && !hasBounds) {
        if (count() == 1) {
          action = new QAction("Tie", this);
          connect(action, SIGNAL(triggered()), this, SLOT(addTie()));
          menu->addAction(action);
        } else {
          QMenu *detail = menu->addMenu("Tie");
          action = new QAction("To function", this);
          connect(action, SIGNAL(triggered()), this, SLOT(addTieToFunction()));