Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
MantidUI.cpp 134.99 KiB
// Python header must go first
#include "MantidQtAPI/PythonThreading.h"

#include "AlgorithmDockWidget.h"
#include "AlgorithmHistoryWindow.h"
#include "AlgorithmMonitor.h"
#include "ImportWorkspaceDlg.h"
#include "MantidGroupPlotGenerator.h"
#include "MantidMDCurve.h"
#include "MantidMDCurveDialog.h"
#include "MantidMatrix.h"
#include "MantidMatrixCurve.h"
#include "MantidQtMantidWidgets/FitPropertyBrowser.h"
#include "MantidQtMantidWidgets/MantidSurfacePlotDialog.h"
#include "MantidQtMantidWidgets/MantidWSIndexDialog.h"
#include "MantidSampleLogDialog.h"
#include "MantidSampleMaterialDialog.h"
#include "MantidTable.h"
#include "MantidUI.h"
#include "ProjectSerialiser.h"

#include "../../MantidQt/MantidWidgets/ui_SequentialFitDialog.h"
#include "../Folder.h"
#include "../ScriptingWindow.h"
#include "../Spectrogram.h"
#include "../TiledWindow.h"
#include "MantidQtAPI/pixmaps.h"

#include "Mantid/InstrumentWidget/InstrumentWindow.h"
#include "MantidAPI/Axis.h"
#include "MantidAPI/TextAxis.h"
#include "MantidKernel/ConfigService.h"
#include "MantidKernel/DateAndTime.h"
#include "MantidKernel/DateAndTime.h"
#include "MantidKernel/EnvironmentHistory.h"
#include "MantidKernel/FacilityInfo.h"
#include "MantidKernel/LogFilter.h"
#include "MantidKernel/Property.h"
#include "MantidKernel/TimeSeriesProperty.h"
#include "MantidKernel/UnitConversion.h"

#include "InstrumentWidget/InstrumentWindow.h"

#include "MantidQtAPI/AlgorithmInputHistory.h"
#include "MantidQtAPI/InterfaceManager.h"
#include "MantidQtAPI/PlotAxis.h"
#include "MantidQtAPI/VatesViewerInterface.h"

#include "MantidQtMantidWidgets/MantidTreeWidget.h"
#include "MantidQtMantidWidgets/WorkspacePresenter/QWorkspaceDockView.h"

#include "MantidAPI/CompositeFunction.h"
#include "MantidAPI/IMDEventWorkspace.h"
#include "MantidAPI/IMDHistoWorkspace.h"
#include "MantidAPI/IPeaksWorkspace.h"
#include "MantidAPI/LogFilterGenerator.h"
#include "MantidAPI/Run.h"
#include "MantidAPI/SpectrumInfo.h"
#include "MantidAPI/WorkspaceGroup.h"

#include <QListWidget>
#include <QMdiArea>
#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
#include <QMessageBox>
#include <QTextEdit>
#include <QTextEdit>
#include <QToolBar>
#include <algorithm>
#include <qwt_plot_curve.h>
#include <time.h>

#ifdef _WIN32
#include <windows.h>
#endif

#include <algorithm>
#include <fstream>
#include <locale>
#include <set>
#include <sstream>

#include <boost/tokenizer.hpp>

#include <Poco/ActiveResult.h>

#include "MantidAPI/IMDWorkspace.h"
#include "MantidQtFactory/WidgetFactory.h"
#include "MantidQtSliceViewer/SliceViewerWindow.h"

#include "MantidQtSpectrumViewer/SpectrumView.h"
#include <typeinfo>

using namespace std;

using namespace Mantid::API;
using namespace MantidQt::API;
using namespace MantidQt::MantidWidgets;
using MantidQt::MantidWidgets::MantidWSIndexDialog;
using MantidQt::MantidWidgets::MantidSurfacePlotDialog;
using MantidQt::MantidWidgets::MantidTreeWidget;
using Mantid::Kernel::DateAndTime;
using MantidQt::SliceViewer::SliceViewerWindow;

namespace MantidException = Mantid::Kernel::Exception;

namespace {
/// The number of detectors to show within a group before eliding
size_t DET_TABLE_NDETS_GROUP = 10;

// Initialize logger
Mantid::Kernel::Logger g_log("MantidUI");

bool isOfType(const QObject *obj, const char *toCompare) {
  return strcmp(obj->metaObject()->className(), toCompare) == 0;
}

/// Number of subplots above which user confirmation will be required
constexpr int REASONABLE_NUM_SUBPLOTS(12);

/// Get graph legend key given workspace name and spectrum number
QString getLegendKey(const QString &wsName, const int spectrum) {
  const auto ws = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
      wsName.toStdString());
  if (ws) {
    const auto axis = ws->getAxis(1); // y
    return QString::fromStdString(axis->label(spectrum));
  }
  return QString();
}

/// Get all graph legend keys in one string
QString getLegendKeys(const QString &wsName, const std::set<int> &spectra) {
  QString legendText = wsName + '\n';
  int curveIndex(0);
  for (const auto &spec : spectra) {
    legendText += "\\l(" + QString::number(++curveIndex) + ")" +
                  getLegendKey(wsName, spec) + "\n";
  }
  return legendText;
}

/// Decide whether this graph in a multilayer plot should have an X axis label
bool drawXAxisLabel(const int row, const int col, const int nRows,
                    const int nCols, const int nPlots) {
  if (row == nRows - 1) {
    return true; // last row
  } else if (row == nRows - 2) {
    // Needs a label if there is no subplot below it
    return ((row + 1) * nCols) + col + 1 > nPlots;
  } else {
    return false;
  }
}

/// Spectra names for a fit results workspace
const std::vector<std::string> FIT_RESULTS_SPECTRA_NAMES{"Data", "Calc",
                                                         "Diff"};

/// Decide whether the named workspace is the results from a fit
/// (will have 3 spectra called "Data", "Calc" and "Diff")
bool workspaceIsFitResult(const QString &wsName) {
  bool isFit = false;
  const auto &ws = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
      wsName.toStdString());
  if (ws) {
    if (FIT_RESULTS_SPECTRA_NAMES.size() == ws->getNumberHistograms()) {
      std::vector<std::string> spectraNames;
      const auto specAxis = ws->getAxis(1); // y
      for (size_t iSpec = 0; iSpec < FIT_RESULTS_SPECTRA_NAMES.size();
           ++iSpec) {
        spectraNames.push_back(specAxis->label(iSpec));
      }
      isFit = spectraNames == FIT_RESULTS_SPECTRA_NAMES;
    }
  }
  return isFit;
}

/// Return curve type for spectrum of a set of fit results
GraphOptions::CurveType getCurveTypeForFitResult(const size_t spectrum) {
  switch (spectrum) {
  case 0:
    return GraphOptions::CurveType::LineSymbols;
  case 1:
    return GraphOptions::CurveType::Line;
  default:
    return GraphOptions::CurveType::Unspecified;
  }
}
}

MantidUI::MantidUI(ApplicationWindow *aw)
    : m_finishedLoadDAEObserver(*this,
                                &MantidUI::handleLoadDAEFinishedNotification),
      m_configServiceObserver(*this, &MantidUI::handleConfigServiceUpdate),
      m_appWindow(aw), m_lastShownInstrumentWin(NULL),
      m_lastShownSliceViewWin(NULL), m_lastShownSpectrumViewerWin(NULL),
      m_lastShownColorFillWin(NULL), m_lastShown1DPlotWin(NULL),
      m_vatesSubWindow(NULL)
//, m_spectrumViewWindow(NULL)
{

  // To be able to use them in queued signals they need to be registered
  static bool registered_addtional_types = false;
  if (!registered_addtional_types) {
    registered_addtional_types = true;
    qRegisterMetaType<Mantid::API::Workspace_sptr>();
    qRegisterMetaType<Mantid::API::MatrixWorkspace_sptr>();
    qRegisterMetaType<Mantid::API::MatrixWorkspace_const_sptr>();
    // Register std::string as well as we use it alot
    qRegisterMetaType<std::string>();
  }

  m_exploreMantid = boost::make_shared<QWorkspaceDockView>(this, aw);
  m_exploreMantid->init();
  m_exploreAlgorithms = new AlgorithmDockWidget(this, aw);

  actionCopyRowToTable = new QAction(this);
  actionCopyRowToTable->setIcon(QIcon(getQPixmap("table_xpm")));
  connect(actionCopyRowToTable, SIGNAL(triggered()), this,
          SLOT(copyRowToTable()));

  actionCopyRowToGraph = new QAction(this);
  actionCopyRowToGraph->setIcon(QIcon(getQPixmap("graph_xpm")));
  connect(actionCopyRowToGraph, SIGNAL(triggered()), this,
          SLOT(copyRowToGraph()));

  actionCopyRowToGraphErr = new QAction(this);
  actionCopyRowToGraphErr->setIcon(QIcon(getQPixmap("graph_xpm")));
  connect(actionCopyRowToGraphErr, SIGNAL(triggered()), this,
          SLOT(copyRowToGraphErr()));

  actionWaterfallPlot = new QAction(QIcon(":/waterfall_plot.png"),
                                    tr("Plot spectra as waterfall"), this);
  connect(actionWaterfallPlot, SIGNAL(triggered()), this,
          SLOT(copyRowsToWaterfall()));

  actionCopyDetectorsToTable = new QAction(tr("View detectors table"), this);
  actionCopyDetectorsToTable->setIcon(QIcon(getQPixmap("table_xpm")));
  connect(actionCopyDetectorsToTable, SIGNAL(triggered()), this,
          SLOT(copyDetectorsToTable()));

  actionCopyValues = new QAction(tr("Copy"), this);
  actionCopyValues->setIcon(QIcon(getQPixmap("copy_xpm")));
  connect(actionCopyValues, SIGNAL(triggered()), this, SLOT(copyValues()));

  actionCopyColumnToTable = new QAction(this);
  actionCopyColumnToTable->setIcon(QIcon(getQPixmap("table_xpm")));
  connect(actionCopyColumnToTable, SIGNAL(triggered()), this,
          SLOT(copyColumnToTable()));

  actionCopyColumnToGraph = new QAction(this);
  actionCopyColumnToGraph->setIcon(QIcon(getQPixmap("graph_xpm")));
  connect(actionCopyColumnToGraph, SIGNAL(triggered()), this,
          SLOT(copyColumnToGraph()));

  actionCopyColumnToGraphErr = new QAction(this);
  actionCopyColumnToGraphErr->setIcon(QIcon(getQPixmap("graph_xpm")));
  connect(actionCopyColumnToGraphErr, SIGNAL(triggered()), this,
          SLOT(copyColumnToGraphErr()));

  connect(this, SIGNAL(needToCreateLoadDAEMantidMatrix(const QString &)), this,
          SLOT(createLoadDAEMantidMatrix(const QString &)));
  connect(this, SIGNAL(needToShowCritical(const QString &)), this,
          SLOT(showCritical(const QString &)));

  m_algMonitor = new AlgorithmMonitor(this);
  connect(m_algMonitor, SIGNAL(algorithmStarted(void *)), m_exploreAlgorithms,
          SLOT(algorithmStarted(void *)), Qt::QueuedConnection);
  connect(m_algMonitor, SIGNAL(algorithmFinished(void *)), m_exploreAlgorithms,
          SLOT(algorithmFinished(void *)), Qt::QueuedConnection);
  connect(m_algMonitor, SIGNAL(needUpdateProgress(
                            void *, double, const QString &, double, int)),
          m_exploreAlgorithms,
          SLOT(updateProgress(void *, double, const QString &, double, int)),
          Qt::QueuedConnection);
  m_algMonitor->start();

  mantidMenu = new QMenu(m_appWindow);
  mantidMenu->setObjectName("mantidMenu");
  mantidMenuAboutToShow();

  QShortcut *sc =
      new QShortcut(QKeySequence(QKeySequence::Delete), m_appWindow);
  connect(sc, SIGNAL(activated()), this, SLOT(deletePressEvent()));

  menuMantidMatrix = new QMenu(m_appWindow);
  connect(menuMantidMatrix, SIGNAL(aboutToShow()), this,
          SLOT(menuMantidMatrixAboutToShow()));

  init();
}

// Should it be moved to the constructor?
void MantidUI::init() {
  Mantid::Kernel::ConfigService::Instance().addObserver(
      m_configServiceObserver);

  m_exploreAlgorithms->update();

  try {
    m_defaultFitFunction =
        new MantidQt::MantidWidgets::FitPropertyBrowser(m_appWindow, this);
    m_defaultFitFunction->init();
    // this make the progress bar work with Fit algorithm running form the fit
    // browser
    connect(m_defaultFitFunction,
            SIGNAL(executeFit(QString, QHash<QString, QString>,
                              Mantid::API::AlgorithmObserver *)),
            this, SLOT(showAlgorithmDialog(QString, QHash<QString, QString>,
                                           Mantid::API::AlgorithmObserver *)));
    m_defaultFitFunction->hide();
    m_appWindow->addDockWidget(Qt::LeftDockWidgetArea, m_defaultFitFunction);

    m_fitFunction = m_defaultFitFunction;
  } catch (...) {
    m_defaultFitFunction = NULL;
    m_fitFunction = NULL;
    g_log.warning("Curve fitting plugin not loaded. Some functionality will be "
                  "unavailable.");
  }
}

/// Slot: Receives a new X range from a FitPropertyBrowser and re-emits it.
void MantidUI::x_range_from_picker(double xmin, double xmax) {
  emit x_range_update(xmin, xmax);
}

/// Updates the algorithms tree as this may have changed
void MantidUI::updateAlgorithms() { m_exploreAlgorithms->update(); }

/// Updates the workspace tree
void MantidUI::updateWorkspaces() { m_exploreMantid->refreshWorkspaces(); }

void MantidUI::addMenuItems(QMenu *menu) {
  actionToggleMantid = m_exploreMantid->toggleViewAction();
  actionToggleMantid->setIcon(getQPixmap("mantid_matrix_xpm"));
  actionToggleMantid->setShortcut(tr("Ctrl+Shift+M"));
  menu->addAction(actionToggleMantid);

  actionToggleAlgorithms = m_exploreAlgorithms->toggleViewAction();
  actionToggleAlgorithms->setShortcut(tr("Ctrl+Shift+A"));
  menu->addAction(actionToggleAlgorithms);

  if (m_fitFunction) {
    actionToggleFitFunction = m_fitFunction->toggleViewAction();
    menu->addAction(actionToggleFitFunction);
  }
}

// Show / hide the FitPropertyBrowser
void MantidUI::showFitPropertyBrowser(bool on) {
  if (!m_fitFunction)
    return;
  if (on) {
    m_fitFunction->show();
  } else {
    m_fitFunction->hide();
  }
}

/**
* Be careful where this is called, if it is a called too late in the Qt shutdown
* the application
* crashes
*/
void MantidUI::shutdown() {
  g_log.notice("MantidPlot is shutting down...");

  // First we need to cancel any running algorithms otherwise bad things can
  // happen if they call
  // the logging framework after it's been shutdown. The cancel calls within
  // cancelAll are not
  // blocking, hence the loop to make sure they're all done before moving on.
  // (N.B. Tried copying
  // the wait/exit/wait business from the AlgorithmMonitor dtor, but that gave
  // occasional crashes.)
  if (m_algMonitor) {
    m_algMonitor->cancelAll();
    while (m_algMonitor->count() > 0) {
      Poco::Thread::sleep(100);
    }
  }
  // If any python objects need to be cleared away then the GIL needs to be
  // held. This doesn't feel like
  // it is in the right place but it will do no harm
  ScopedPythonGIL gil;
  // Relevant notifications are connected to signals that will close all
  // dependent windows
  Mantid::API::FrameworkManager::Instance().shutdown();
}

MantidUI::~MantidUI() {
  delete m_algMonitor;

  Mantid::Kernel::ConfigService::Instance().removeObserver(
      m_configServiceObserver);

  delete m_fitFunction;
}

void MantidUI::saveSettings() const {
  // Save algorithm dialog input
  MantidQt::API::AlgorithmInputHistory::Instance().save();
}

QStringList MantidUI::getWorkspaceNames() {
  QStringList sl;
  auto sv = Mantid::API::AnalysisDataService::Instance().getObjectNames();
  for (const auto &name : sv)
    sl << QString::fromStdString(name);
  return sl;
}

QStringList MantidUI::getAlgorithmNames() {
  QStringList sl;
  std::vector<std::string> sv;
  sv = Mantid::API::AlgorithmFactory::Instance().getKeys();
  for (size_t i = 0; i < sv.size(); i++)
    sl << QString::fromStdString(sv[i]);
  return sl;
}

/**
*  Returns the number of algorithms currently executing
*/
int MantidUI::runningAlgCount() const { return m_algMonitor->count(); }

/**
* Alerts applicationWindow that the ADS has been modified.
*/
void MantidUI::updateProject() { m_appWindow->modifiedProject(); }
/**
* Ticket #678
*/
void MantidUI::saveNexusWorkspace() { executeSaveNexus(); }

/**
* DeleteWorkspace
@param workspaceName :: Name of the workspace to delete
*/
void MantidUI::deleteWorkspace(const QString &workspaceName) {
  auto alg = createAlgorithm("DeleteWorkspace");
  alg->setLogging(false);
  alg->setPropertyValue("Workspace", workspaceName.toStdString());
  executeAlgorithmAsync(alg);
}

void MantidUI::deleteWorkspaces(const QStringList &wsNames) {
  MantidMatrix *m = dynamic_cast<MantidMatrix *>(m_appWindow->activeWindow());

  try {
    if (!wsNames.isEmpty()) {
      for (auto &ws : wsNames) {
        deleteWorkspace(ws);
      }
    } else if ((m &&
                (strcmp(m->metaObject()->className(), "MantidMatrix") == 0)) &&
               !m->workspaceName().isEmpty()) {
      deleteWorkspace(m->workspaceName());
    }
  } catch (...) {
    QMessageBox::warning(m_appWindow, "",
                         "Could not delete selected workspaces.");
  }
}

/**
getSelectedWorkspaceName
*/
QString MantidUI::getSelectedWorkspaceName() {
  auto names = m_exploreMantid->getSelectedWorkspaceNames();
  QString str;

  if (!names.empty())
    str = QString::fromStdString(names[0]);

  if (str.isEmpty()) {
    // Check if a mantid matrix is selected
    MantidMatrix *m = qobject_cast<MantidMatrix *>(appWindow()->activeWindow());
    if (!m)
      return "";

    str = m->workspaceName();
  }
  return str;
}

Mantid::API::Workspace_const_sptr MantidUI::getSelectedWorkspace() {
  return m_exploreMantid->getSelectedWorkspace();
}

Mantid::API::Workspace_const_sptr
MantidUI::getWorkspace(const QString &workspaceName) {
  if (AnalysisDataService::Instance().doesExist(workspaceName.toStdString())) {
    return AnalysisDataService::Instance().retrieve(
        workspaceName.toStdString());
  }

  Workspace_sptr empty;

  return empty; //??
}

/**   Extension to ApplicationWindow::menuAboutToShow() to deal with Mantid.
*/
bool MantidUI::menuAboutToShow(MdiSubWindow *w) {

  if (w && isOfType(w, "MantidMatrix")) {
    auto plotMenuAction =
        appWindow()->myMenuBar()->addMenu(appWindow()->plot3DMenu);
    plotMenuAction->setText(tr("3D &Plot"));
    appWindow()->actionCopySelection->setEnabled(true);
    appWindow()->actionPasteSelection->setEnabled(false);
    appWindow()->actionClearSelection->setEnabled(false);

    auto menuMantidMatrixAction =
        appWindow()->myMenuBar()->addMenu(menuMantidMatrix);
    menuMantidMatrixAction->setText(tr("&Workspace"));
    return true;
  }

  return false;
}

Graph3D *MantidUI::plot3DMatrix(int style) {
  MdiSubWindow *w = appWindow()->activeWindow();
  if (isOfType(w, "MantidMatrix")) {
    return static_cast<MantidMatrix *>(w)->plotGraph3D(style);
  }

  return 0;
}

MultiLayer *MantidUI::plotSpectrogram(GraphOptions::CurveType type) {
  MantidMatrix *m = dynamic_cast<MantidMatrix *>(appWindow()->activeWindow());
  if (m) {
    return drawSingleColorFillPlot(
        QString::fromStdString(m->getWorkspaceName()), type);
  }
  return 0;
}

/**  Import a MatrixWorkspace into a MantidMatrix.
@param wsName :: Workspace name
@param lower :: An optional lower boundary
@param upper :: An optional upper boundary
@param showDlg :: If true show a dialog box to set some import parameters
@param makeVisible :: If true show the created MantidMatrix, hide otherwise.
@return A pointer to the new MantidMatrix.
*/
MantidMatrix *MantidUI::importMatrixWorkspace(const QString &wsName, int lower,
                                              int upper, bool showDlg,
                                              bool makeVisible) {
  MatrixWorkspace_sptr ws;
  if (AnalysisDataService::Instance().doesExist(wsName.toStdString())) {
    ws = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
        wsName.toStdString());
  }

  MantidMatrix *matrix = importMatrixWorkspace(ws, lower, upper, showDlg);
  if (matrix) {
    appWindow()->addMdiSubWindow(matrix, makeVisible);
  }
  return matrix;
}

/**  Import a MatrixWorkspace into a MantidMatrix.
@param workspace :: Workspace
@param lower :: An optional lower boundary
@param upper :: An optional upper boundary
@param showDlg :: If true show a dialog box to set some import parameters
@return A pointer to the new MantidMatrix.
*/
MantidMatrix *
MantidUI::importMatrixWorkspace(const MatrixWorkspace_sptr workspace, int lower,
                                int upper, bool showDlg) {
  MantidMatrix *matrix = 0;
  if (workspace) {
    const QString wsName(workspace->getName().c_str());
    if (showDlg) {
      ImportWorkspaceDlg dlg(appWindow(), workspace->getNumberHistograms());
      if (dlg.exec() == QDialog::Accepted) {
        int start = dlg.getLowerLimit();
        int end = dlg.getUpperLimit();
        matrix = new MantidMatrix(workspace, appWindow(), "Mantid", wsName,
                                  start, end);
        if (dlg.isFiltered())
          matrix->setRange(0, dlg.getMaxValue());
      }
    } else {
      matrix = new MantidMatrix(workspace, appWindow(), "Mantid", wsName, lower,
                                upper);
    }
  }
  return matrix;
}

/**  Import a Workspace into MantidPlot.
@param wsName :: Workspace name
@param showDlg :: If true show a dialog box to set some import parameters
@param makeVisible :: If true show the created widget, hide otherwise.
*/
void MantidUI::importWorkspace(const QString &wsName, bool showDlg,
                               bool makeVisible) {
  MantidMatrix *mm =
      importMatrixWorkspace(wsName, -1, -1, showDlg, makeVisible);
  ScopedOverrideCursor waitCursor;
  if (!mm) {
    importTableWorkspace(wsName, showDlg, makeVisible);
  }
}

/**  Import the selected workspace, if any. Displays the import dialog.
*/
void MantidUI::importWorkspace() {
  QString wsName = getSelectedWorkspaceName();
  importWorkspace(wsName, true, true);
}

/**  Import the selected table workspace transposed.
*/
void MantidUI::importTransposed() {
  ScopedOverrideCursor waitCursor;
  QString wsName = getSelectedWorkspaceName();
  ITableWorkspace_sptr ws;
  if (AnalysisDataService::Instance().doesExist(wsName.toStdString())) {
    ws = AnalysisDataService::Instance().retrieveWS<ITableWorkspace>(
        wsName.toStdString());
    importTableWorkspace(wsName, true, true, true);
  }
}

/**  Create a TableWorkspace of box data from the MDEventWorkspace
*/
void MantidUI::importBoxDataTable() {
  std::cout << "MantidUI::importBoxDataTable()\n";
  QString wsName = getSelectedWorkspaceName();
  try {
    // Get the MD event table
    IMDEventWorkspace_sptr ws = boost::dynamic_pointer_cast<IMDEventWorkspace>(
        AnalysisDataService::Instance().retrieve(wsName.toStdString()));
    if (!ws)
      return;
    ITableWorkspace_sptr tabWs = ws->makeBoxTable(0, 0);
    if (!tabWs)
      return;
    std::string tableName = wsName.toStdString() + std::string("_boxdata");
    AnalysisDataService::Instance().addOrReplace(tableName, tabWs);
    // Now show that table
    importWorkspace(QString::fromStdString(tableName), true, true);
  } catch (...) {
  }
}

/** Plots a Curve showing intensities for a MDWorkspace.
* But only if the workspace meets certain criteria, such as
* having only one non-integrated dimension. Should exit gracefully otherwise.
*/
void MantidUI::showMDPlot() {
  QString wsName = getSelectedWorkspaceName();

  // Create a dialog to ask for options
  MantidMDCurveDialog dlg(appWindow(), wsName);
  if (dlg.exec() == QDialog::Rejected)
    return;
  // Extract the settings from the dialog opened earlier
  bool showErrors = dlg.showErrorBars();
  LinePlotOptions *opts = dlg.getLineOptionsWidget();
  QStringList all;
  all << wsName;
  plotMDList(all, opts->getPlotAxis(), opts->getNormalization(), showErrors);
}

/**
 * Plots a curve showing intensities for MDWorkspaces
 * @param wsNames : Names of the workspaces to plot
 * @param plotAxis : Axis number to plot
 * @param normalization: Normalization option to use
 * @param showErrors: True if errors are to be show
 * @param plotWindow :: Window to use for plotting. If NULL a new one will be
 * created
 * @param clearWindow :: Whether to clean the plotWindow before plotting.Ignored
 * if plotWindow == NULL
 * @return NULL if failure. Otherwise, if plotWindow == NULL - created window,
 * if not NULL - plotWindow
 */
MultiLayer *
MantidUI::plotMDList(const QStringList &wsNames, const int plotAxis,
                     const Mantid::API::MDNormalization normalization,
                     const bool showErrors, MultiLayer *plotWindow,
                     bool clearWindow) {
  ScopedOverrideCursor waitCursor;
  auto firstName = wsNames.at(0);

  bool isGraphNew = false;
  MultiLayer *ml = appWindow()->prepareMultiLayer(isGraphNew, plotWindow,
                                                  firstName, clearWindow);

  Graph *g = ml->activeGraph();
  try {
    for (int i = 0; i < wsNames.size(); ++i) {
      // Create the curve with defaults
      auto wsName = wsNames.at(i);
      MantidMDCurve *curve = new MantidMDCurve(wsName, g, showErrors);
      MantidQwtIMDWorkspaceData *data = curve->mantidData();

      // Apply the settings
      data->setPreviewMode(false);
      data->setPlotAxisChoice(plotAxis);
      data->setNormalization(normalization);

      g->setNormalizableMD(true);
      g->setNormalizationMD(normalization);

      // Using information from the first graph
      if (i == 0 && isGraphNew)
        g->setAutoScale();
    }

  } catch (std::invalid_argument &e) {
    g_log.warning() << e.what() << '\n';
  } catch (std::runtime_error &e) {
    g_log.warning() << e.what() << '\n';
  } catch (...) {
  }

  if (!isGraphNew)
    // Replot graph is we've added curves to existing one
    g->replot();

  // Check if window does not contain any curves and should be closed
  ml->maybeNeedToClose();

  return ml;
}

/*
Generates a table workspace from a md workspace and pulls up
a grid to display the results.
*/
void MantidUI::showListData() {
  QString wsName = getSelectedWorkspaceName();
  QString tableWsName = wsName + "_data_list_table";

  Mantid::API::IAlgorithm_sptr queryWorkspace =
      this->createAlgorithm("QueryMDWorkspace");
  queryWorkspace->initialize();
  queryWorkspace->setPropertyValue("InputWorkspace", wsName.toStdString());
  std::string sTableWorkspaceName = tableWsName.toStdString();
  queryWorkspace->setPropertyValue("OutputWorkspace", sTableWorkspaceName);
  queryWorkspace->setProperty("LimitRows", false);
  queryWorkspace->execute();

  importWorkspace(tableWsName);
}

void MantidUI::showVatesSimpleInterface() {
  QString wsName = getSelectedWorkspaceName();
  try {
    IMDEventWorkspace_sptr mdews =
        boost::dynamic_pointer_cast<IMDEventWorkspace>(
            AnalysisDataService::Instance().retrieve(wsName.toStdString()));

    IPeaksWorkspace_sptr pws = boost::dynamic_pointer_cast<IPeaksWorkspace>(
        AnalysisDataService::Instance().retrieve(wsName.toStdString()));

    IMDHistoWorkspace_sptr mdhist =
        boost::dynamic_pointer_cast<IMDHistoWorkspace>(
            AnalysisDataService::Instance().retrieve(wsName.toStdString()));

    if (!mdews && !pws && !mdhist) {
      return;
    }

    // Set the type of workspace, the GUI needs it and
    // extract the instrument which was used to measure the workspace data
    int wsType = MantidQt::API::VatesViewerInterface::MDEW;

    std::string instrumentName;

    // check for peak workspace
    if (pws) {
      wsType = MantidQt::API::VatesViewerInterface::PEAKS;

      instrumentName = pws->getInstrument()->getFullName();
    }

    // Check for histo workspace
    if (mdhist) {
      wsType = MantidQt::API::VatesViewerInterface::MDHW;

      // Get the instrument name
      if (mdhist->getNumExperimentInfo() > 0) {
        instrumentName =
            mdhist->getExperimentInfo(0)->getInstrument()->getFullName();
      }
    }

    // Check for event workspace
    if (mdews) {
      // Get the instrument name
      if (mdews->getNumExperimentInfo() > 0) {
        instrumentName =
            mdews->getExperimentInfo(0)->getInstrument()->getFullName();
      }
    }

    if (m_vatesSubWindow) {
      QWidget *vwidget = m_vatesSubWindow->widget();
      vwidget->show();
      qobject_cast<MantidQt::API::VatesViewerInterface *>(vwidget)
          ->renderWorkspace(wsName, wsType, instrumentName);
      return;
    } else {
      m_vatesSubWindow = new QMdiSubWindow;
      m_vatesSubWindow->setAttribute(Qt::WA_DeleteOnClose, false);
#ifdef Q_OS_MAC
      // Work around to ensure that floating windows remain on top of the main
      // application window, but below other applications on Mac
      // Note: Qt::Tool cannot have both a max and min button on OSX
      Qt::WindowFlags flags = m_vatesSubWindow->windowFlags();
      flags |= Qt::Tool;
      flags |= Qt::CustomizeWindowHint;
      flags |= Qt::WindowMinimizeButtonHint;
      flags |= Qt::WindowCloseButtonHint;
      m_vatesSubWindow->setWindowFlags(flags);
#endif
      QIcon icon;
      icon.addFile(
          QString::fromUtf8(":/VatesSimpleGuiViewWidgets/icons/pvIcon.png"),
          QSize(), QIcon::Normal, QIcon::Off);
      m_vatesSubWindow->setWindowIcon(icon);
      connect(m_appWindow, SIGNAL(shutting_down()), m_vatesSubWindow,
              SLOT(close()));
      MantidQt::API::InterfaceManager interfaceManager;
      MantidQt::API::VatesViewerInterface *vsui =
          interfaceManager.createVatesSimpleGui();
      if (vsui) {
        connect(m_appWindow, SIGNAL(shutting_down()), vsui, SLOT(shutdown()));
        connect(vsui, SIGNAL(requestClose()), m_vatesSubWindow, SLOT(close()));
        vsui->setParent(m_vatesSubWindow);
        m_vatesSubWindow->setWindowTitle("Vates Simple Interface");
        vsui->setupPluginMode(wsType, instrumentName);
        m_vatesSubWindow->setWidget(vsui);
        m_vatesSubWindow->widget()->show();
        vsui->renderWorkspace(wsName, wsType, instrumentName);
        // Keep and handle to the window for later serialisation
        appWindow()->addSerialisableWindow(vsui);
        appWindow()->modifiedProject();
      } else {
        delete m_vatesSubWindow;
        m_vatesSubWindow = NULL;
        return;
      }
    }
  } catch (std::runtime_error &e) {
    throw std::runtime_error(e);
  } catch (...) {
  }
  // reset the qt error redirection that Paraview puts in place
  // this may not be necessary if we move to qt5
  qInstallMsgHandler(0);
}

void MantidUI::showSpectrumViewer() {
  QString wsName = getSelectedWorkspaceName();
  try {
    MatrixWorkspace_sptr wksp = boost::dynamic_pointer_cast<MatrixWorkspace>(
        AnalysisDataService::Instance().retrieve(wsName.toStdString()));
    if (wksp) {
      MantidQt::SpectrumView::SpectrumView *viewer;
      try {
        viewer = new MantidQt::SpectrumView::SpectrumView(m_appWindow);
      } catch (std::runtime_error &e) {
        m_lastShownSpectrumViewerWin = NULL;
        g_log.error() << "Could not create spectrum viewer: " << e.what()
                      << "\n";
        throw std::runtime_error(e);
      }
      // Delete on close so we don't hold a shared pointer to a workspace
      // which has been deleted in the ADS and is "inaccessible"
      viewer->setAttribute(Qt::WA_DeleteOnClose, true);
      viewer->resize(1050, 800);
      connect(m_appWindow, SIGNAL(shutting_down()), viewer, SLOT(close()));

      if (workspacesDockPlot1To1()) {
        // only one at any given time
        if (m_lastShownSpectrumViewerWin) {
          m_lastShownSpectrumViewerWin->close();
          QPoint p = m_lastShownSpectrumViewerWin->pos();
          delete m_lastShownSpectrumViewerWin;
          viewer->move(p);
        }
      }
      m_lastShownSpectrumViewerWin = viewer;

      viewer->show();
      viewer->renderWorkspace(wksp);
      // Add to the list of serialisable windows
      appWindow()->addSerialisableWindow(viewer);
      appWindow()->modifiedProject();
    } else {
      g_log.information()
          << "Only event or matrix workspaces are currently supported.\n"
          << "Please convert to one of these before using the ImageView.\n";
    }
  } catch (std::runtime_error &e) {
    g_log.error() << e.what() << "\n";
    throw std::runtime_error(e);
  } catch (...) {
    g_log.error() << "Image View: Exception getting workspace\n";
  }
}

/** Create a window with a SliceViewer widget to show
* the selected workspace
*/
void MantidUI::showSliceViewer() {
  // Retrieve the MDWorkspace
  QString wsName = getSelectedWorkspaceName();
  IMDWorkspace_sptr mdws = boost::dynamic_pointer_cast<IMDWorkspace>(
      AnalysisDataService::Instance().retrieve(wsName.toStdString()));
  MatrixWorkspace_sptr mw = boost::dynamic_pointer_cast<MatrixWorkspace>(mdws);
  if (mdws) {
    // Create the slice viewer window
    SliceViewerWindow *w;
    try {
      w = MantidQt::Factory::WidgetFactory::Instance()->createSliceViewerWindow(
          wsName, "");
    } catch (std::runtime_error &e) {
      m_lastShownSliceViewWin = NULL;
      g_log.error() << "Could not create slice viewer: " << e.what() << "\n";
      throw std::runtime_error(e);
    }

    // Special options for viewing MatrixWorkspaces
    if (mw) {
      w->getSlicer()->setTransparentZeros(false);
    }

    // Global option for color bar autoscaling
    w->getSlicer()->setColorBarAutoScale(m_appWindow->autoscale2DPlots);

    // Connect the MantidPlot close() event with the the window's close().
    QObject::connect(appWindow(), SIGNAL(destroyed()), w, SLOT(close()));

    if (workspacesDockPlot1To1()) {
      // only one at any given time
      if (m_lastShownSliceViewWin) {
        m_lastShownSliceViewWin->close();
        QPoint p = m_lastShownSliceViewWin->pos();
        // the factory keeps a list of all opened slice viewers
        MantidQt::Factory::WidgetFactory::Instance()->closeSliceViewerWindow(
            m_lastShownSliceViewWin);
        delete m_lastShownSliceViewWin;
        w->move(p);
      }
    }
    m_lastShownSliceViewWin = w;

    // Pop up the window
    w->show();
    // Keep and handle to the window for later serialisation
    appWindow()->addSerialisableWindow(w);
    appWindow()->modifiedProject();
  }
}

/** #539: For adding Workspace History display to MantidPlot
Show Algorithm History Details in a window .
*/
void MantidUI::showAlgorithmHistory() {
  QString wsName = getSelectedWorkspaceName();
  Mantid::API::Workspace_const_sptr wsptr = getWorkspace(wsName);
  if (NULL != wsptr) {
    // If the workspace has any AlgorithHistory ...
    if (!wsptr->getHistory().empty()) {
      // ... create and display the window.
      AlgorithmHistoryWindow *palgHist =
          new AlgorithmHistoryWindow(m_appWindow, wsptr);
      if (NULL != palgHist) {
        palgHist->show();
      }
    }
  } else {
    QMessageBox::information(appWindow(), "Mantid", "Invalid WorkSpace");
    return;
  }
}

/**  Create a new Table and fill it with the data from a Tableworkspace
@param wsName :: Workspace name
@param showDlg :: If true show a dialog box to set some import parameters
@param makeVisible :: If true show the created Table, hide otherwise.
@param transpose :: Transpose the table
@return A pointer to the new Table.
*/
Table *MantidUI::importTableWorkspace(const QString &wsName, bool,
                                      bool makeVisible, bool transpose) {
  ITableWorkspace_sptr ws;
  if (AnalysisDataService::Instance().doesExist(wsName.toStdString())) {
    ws = AnalysisDataService::Instance().retrieveWS<ITableWorkspace>(
        wsName.toStdString());
  }

  if (!ws.get())
    return 0;

  if (ws->rowCount() == 0 || ws->columnCount() == 0) {
    showCritical("Cannot create an empty table");
    return 0;
  }

  Table *t = new MantidTable(appWindow()->scriptingEnv(), ws, wsName,
                             appWindow(), transpose);
  if (makeVisible)
    t->showNormal();
  else
    t->showMinimized();
  return t;
}

void MantidUI::showContextMenu(QMenu &cm, MdiSubWindow *w) {
  if (MantidMatrix *mm = dynamic_cast<MantidMatrix *>(w)) {

    bool areSpectraSelected = mm->setSelectedRows();
    bool areColumnsSelected = mm->setSelectedColumns();
    cm.addAction(actionCopyValues);
    if (areSpectraSelected)
      cm.addAction(actionCopyRowToTable);
    if (areColumnsSelected)
      cm.addAction(actionCopyColumnToTable);
    cm.addSeparator();
    cm.addAction(actionCopyDetectorsToTable);
    cm.addSeparator();

    if (areSpectraSelected && mm->numCols() > 1) {
      // Enable the appropriate options
      cm.addAction(actionCopyRowToGraph);
      cm.addAction(actionCopyRowToGraphErr);
      if (mm->getSelectedRows().size() > 1) {
        cm.addAction(actionWaterfallPlot);
      }
    }
    if (areColumnsSelected && mm->numRows() > 1) {
      cm.addAction(actionCopyColumnToGraph);
      cm.addAction(actionCopyColumnToGraphErr);
    }

    // Set the option texts to the correct plurality
    if (mm->getSelectedRows().size() > 1) {
      actionCopyRowToTable->setText("Copy spectra to table");
      actionCopyRowToGraph->setText("Plot spectra (values only)");
      actionCopyRowToGraphErr->setText("Plot spectra (values + errors)");
    } else {
      actionCopyRowToTable->setText("Copy spectrum to table");
      actionCopyRowToGraph->setText("Plot spectrum (values only)");
      actionCopyRowToGraphErr->setText("Plot spectrum (values + errors)");
    }
    if (mm->getSelectedColumns().size() > 1) {
      actionCopyColumnToTable->setText("Copy bins to table");
      actionCopyColumnToGraph->setText("Plot bins (values only)");
      actionCopyColumnToGraphErr->setText("Plot bins (values + errors)");
    } else {
      actionCopyColumnToTable->setText("Copy bin to table");
      actionCopyColumnToGraph->setText("Plot bin (values only)");
      actionCopyColumnToGraphErr->setText("Plot bin (values + errors)");
    }
  }
}

void MantidUI::copyRowToTable() {
  MantidMatrix *m = dynamic_cast<MantidMatrix *>(appWindow()->activeWindow());
  if (!m || !isOfType(m, "MantidMatrix"))
    return;
  Table *t = createTableFromSelectedRows(m, true, true);
  t->showNormal();
}

void MantidUI::copyColumnToTable() {
  MantidMatrix *m = dynamic_cast<MantidMatrix *>(appWindow()->activeWindow());
  if (!m || !isOfType(m, "MantidMatrix"))
    return;
  Table *t = createTableFromSelectedColumns(m, true);
  t->showNormal();
}

void MantidUI::copyRowToGraph() {
  MantidMatrix *m = dynamic_cast<MantidMatrix *>(appWindow()->activeWindow());
  if (!m || !isOfType(m, "MantidMatrix"))
    return;
  plotSelectedRows(m, MantidQt::DistributionDefault, false);
}

void MantidUI::copyColumnToGraph() {
  MantidMatrix *m = dynamic_cast<MantidMatrix *>(appWindow()->activeWindow());
  if (!m || !isOfType(m, "MantidMatrix"))
    return;
  plotSelectedColumns(m, false);
}

void MantidUI::copyColumnToGraphErr() {
  MantidMatrix *m = dynamic_cast<MantidMatrix *>(appWindow()->activeWindow());
  if (!m || !isOfType(m, "MantidMatrix"))
    return;
  plotSelectedColumns(m, true);
}

void MantidUI::copyRowToGraphErr() {
  MantidMatrix *m = dynamic_cast<MantidMatrix *>(appWindow()->activeWindow());
  if (!m || !isOfType(m, "MantidMatrix"))
    return;
  plotSelectedRows(m, MantidQt::DistributionDefault, true);
}

void MantidUI::copyRowsToWaterfall() {
  const MantidMatrix *const m =
      dynamic_cast<MantidMatrix *>(appWindow()->activeWindow());
  if (!m || !isOfType(m, "MantidMatrix"))
    return;
  MultiLayer *ml = plotSelectedRows(m, MantidQt::DistributionDefault, false);
  if (ml)
    convertToWaterfall(ml);
}

void MantidUI::plotWholeAsWaterfall() {
  const MantidMatrix *const m =
      dynamic_cast<MantidMatrix *>(appWindow()->activeWindow());
  if (!m || !isOfType(m, "MantidMatrix"))
    return;
  MultiLayer *ml = plotSpectraRange(m->workspaceName(), 0, m->numRows() - 1,
                                    MantidQt::DistributionDefault, false);
  if (ml)
    convertToWaterfall(ml);
}

void MantidUI::convertToWaterfall(MultiLayer *ml) {
  ml->hide();
  ml->activeGraph()->setWaterfallOffset(10, 20);
  ml->setWaterfallLayout();
  // Next two lines replace the legend so that it works on reversing the curve
  // order
  ml->activeGraph()->removeLegend();
  ml->activeGraph()->newLegend();
  ml->show();
}

void MantidUI::copyDetectorsToTable() {
  MantidMatrix *m = dynamic_cast<MantidMatrix *>(appWindow()->activeWindow());
  if (!m || !isOfType(m, "MantidMatrix"))
    return;
  createTableDetectors(m);
}

void MantidUI::copyValues() {
  MantidMatrix *m = dynamic_cast<MantidMatrix *>(appWindow()->activeWindow());
  if (!m || !isOfType(m, "MantidMatrix"))
    return;
  m->copySelection();
}

Table *MantidUI::createTableDetectors(MantidMatrix *m) {
  std::vector<int> indices(m->numRows(), 0);
  for (int i = 0; i < m->numRows(); i++) {
    indices[i] = m->workspaceIndex(i);
  }
  Table *t = createDetectorTable(m->workspaceName(), indices);
  return t;
}

/**
* Show the detector table - this method is here for the Python interface
*/
Table *MantidUI::createDetectorTable(const QString &wsName) {
  std::vector<int> indices;
  Table *t = createDetectorTable(wsName, indices);
  return t;
}

/**
* Create the relevant detector table for the given workspace
* @param wsName :: The name of the workspace
* @param indices :: Limit the table to these workspace indices (MatrixWorkspace
* only). If the vector is empty,
* all the indices are used.
* @param include_data :: If true then first value from the each spectrum is
* displayed (MatrixWorkspace only)
*/
Table *MantidUI::createDetectorTable(const QString &wsName,
                                     const std::vector<int> &indices,
                                     bool include_data) {
  if (AnalysisDataService::Instance().doesExist(wsName.toStdString())) {
    auto ws = AnalysisDataService::Instance().retrieve(wsName.toStdString());
    // Standard MatrixWorkspace
    auto matrix = boost::dynamic_pointer_cast<MatrixWorkspace>(ws);
    if (matrix) {
      return createDetectorTable(wsName, matrix, indices, include_data);
    }
    auto peaks = boost::dynamic_pointer_cast<IPeaksWorkspace>(ws);
    if (peaks) {
      return createDetectorTable(wsName, peaks);
    }
  }
  return NULL;
}

/**
* Create the instrument detector table from a MatrixWorkspace
* @param wsName :: The name of the workspace
* @param ws :: A pointer to a MatrixWorkspace
* @param indices :: Limit the table to these workspace indices
* @param include_data :: If true then first value from the each spectrum is
* displayed
*/
Table *MantidUI::createDetectorTable(
    const QString &wsName, const Mantid::API::MatrixWorkspace_sptr &ws,
    const std::vector<int> &indices, bool include_data) {
  using namespace Mantid::Geometry;

  // check if efixed value is available
  bool calcQ(true);
  try {
    auto detector = ws->getDetector(0);
    ws->getEFixed(detector);
  } catch (std::runtime_error &) {
    calcQ = false;
  }

  // Prepare column names. Types will be determined from QVariant
  QStringList colNames;
  colNames << "Index"
           << "Spectrum No"
           << "Detector ID(s)";
  if (include_data) {
    colNames << "Data Value"
             << "Data Error";
  }

  colNames << "R"
           << "Theta";
  if (calcQ) {
    colNames << "Q";
  }
  colNames << "Phi"
           << "Monitor";

  const int ncols = static_cast<int>(colNames.size());
  const int nrows = indices.empty()
                        ? static_cast<int>(ws->getNumberHistograms())
                        : static_cast<int>(indices.size());
  Table *t =
      new Table(appWindow()->scriptingEnv(), nrows, ncols, "", appWindow(), 0);
  appWindow()->initTable(
      t, appWindow()->generateUniqueName(wsName + "-Detectors-"));
  // Set the column names
  for (int col = 0; col < ncols; ++col) {
    t->setColName(col, colNames[col]);
    t->setColPlotDesignation(col, Table::None);
  }
  t->setHeaderColType();
  t->setTextFormat(2);
  t->setTextFormat(ncols - 1);

  // Cache some frequently used values
  IComponent_const_sptr sample = ws->getInstrument()->getSample();
  bool signedThetaParamRetrieved(false),
      showSignedTwoTheta(false); // If true,  signedVersion of the two theta
                                 // value should be displayed
  QVector<QList<QVariant>> tableColValues;
  tableColValues.resize(nrows);
  const auto &spectrumInfo = ws->spectrumInfo();
  PARALLEL_FOR_IF(Mantid::Kernel::threadSafe(*ws))
  for (int row = 0; row < nrows; ++row) {
    // Note PARALLEL_START_INTERUPT_REGION & friends apparently not needed (like
    // in algorithms)
    // as there's an extensive try...catch below. If it was need, using those
    // macros would
    // require data members and methods that are available in algorithm classed
    // but not here,
    // including m_cancel, m_parallelException, interrrupt_point().
    QList<QVariant> &colValues = tableColValues[row];
    size_t wsIndex = indices.empty() ? static_cast<size_t>(row) : indices[row];
    colValues << QVariant(static_cast<double>(wsIndex));
    const double dataY0(ws->readY(wsIndex)[0]), dataE0(ws->readE(wsIndex)[0]);
    try {
      auto &spectrum = ws->getSpectrum(wsIndex);
      Mantid::specnum_t specNo = spectrum.getSpectrumNo();
      QString detIds("");
      const auto &ids = spectrum.getDetectorIDs();
      size_t ndets = ids.size();
      auto iter = ids.begin();
      auto itEnd = ids.end();
      if (ndets > DET_TABLE_NDETS_GROUP) {
        detIds = QString("%1,%2...(%3 more)...%4,%5");
        // post-fix increments and returns last value
        // NOTE: Doing this detIds.arg(*iter++).arg(*iter++).arg(ndets-4) seems
        // to result
        // in an undefined order in which the iterator is dereference and
        // incremented leading
        // to the first two items being backward on some systems
        const Mantid::detid_t first(*iter++), second(*iter++);
        detIds =
            detIds.arg(first).arg(second).arg(ndets - 4); // First two + n extra
        auto revIter = ids.rbegin(); // Set iterators are unidirectional ... so
                                     // no operator-()
        const Mantid::detid_t last(*revIter++), lastm1(*revIter++);
        detIds = detIds.arg(lastm1).arg(last);
      } else {
        for (; iter != itEnd; ++iter) {
          detIds += QString::number(*iter) + ",";
        }
        detIds.chop(1); // Drop last comma
      }

      // Geometry
      if (!spectrumInfo.hasDetectors(wsIndex))
        throw std::runtime_error("No detectors found.");
      if (!signedThetaParamRetrieved) {
        const std::vector<std::string> &parameters =
            spectrumInfo.detector(wsIndex)
                .getStringParameter("show-signed-theta", true); // recursive
        showSignedTwoTheta = (!parameters.empty() &&
                              find(parameters.begin(), parameters.end(),
                                   "Always") != parameters.end());
        signedThetaParamRetrieved = true;
      }
      double R(0.0), theta(0.0), phi(0.0);
      // R and theta used as dummy variables
      // Note: phi is the angle around Z, not necessarily the beam direction.
      spectrumInfo.position(wsIndex).getSpherical(R, theta, phi);
      // R is actually L2 (same as R if sample is at (0,0,0))
      R = spectrumInfo.l2(wsIndex);
      // Theta is actually 'twoTheta' for detectors (twice the scattering
      // angle), if Z is the beam direction this corresponds to theta in
      // spherical coordinates.
      // For monitors we follow historic behaviour and display theta
      const bool isMonitor = spectrumInfo.isMonitor(wsIndex);
      if (!isMonitor) {
        try {
          theta = showSignedTwoTheta ? spectrumInfo.signedTwoTheta(wsIndex)
                                     : spectrumInfo.twoTheta(wsIndex);
          theta *= 180.0 / M_PI; // To degrees
        } catch (const std::exception &ex) {
          // Log the error and leave theta as it is
          g_log.error(ex.what());
        }
      }
      const QString isMonitorDisplay = isMonitor ? "yes" : "no";
      colValues << QVariant(specNo) << QVariant(detIds);
      // Y/E
      if (include_data) {
        colValues << QVariant(dataY0) << QVariant(dataE0); // data
      }
      colValues << QVariant(R) << QVariant(theta);

      if (calcQ) {

        try {
          // Get unsigned theta and efixed value
          IDetector_const_sptr det(&spectrumInfo.detector(wsIndex),
                                   Mantid::NoDeleting());
          double efixed = ws->getEFixed(det);
          double usignTheta = spectrumInfo.twoTheta(wsIndex) * 0.5;

          double q = Mantid::Kernel::UnitConversion::run(usignTheta, efixed);
          colValues << QVariant(q);
        } catch (std::runtime_error &) {
          colValues << QVariant("No Efixed");
        }
      }

      colValues << QVariant(phi)               // rtp
                << QVariant(isMonitorDisplay); // monitor
    } catch (...) {
      // spectrumNo=-1, detID=0
      colValues << QVariant(-1) << QVariant("0");
      // Y/E
      if (include_data) {
        colValues << QVariant(dataY0) << QVariant(dataE0); // data
      }
      colValues << QVariant("0") << QVariant("0") // rt
                << QVariant("0")                  // efixed
                << QVariant("0")                  // rtp
                << QVariant("n/a");               // monitor
    }                                             // End catch for no spectrum
  }

  // This modifies widgets, so it needs to run in the Qt GUI thread: no openmp
  // here
  for (int row = 0; row < nrows; ++row) {
    const QList<QVariant> &colValues = tableColValues[row];
    for (int col = 0; col < ncols; ++col) {
      const QVariant &colValue = colValues[col];
      if (QMetaType::QString == colValue.userType()) // Avoid a compiler warning
                                                     // with type() about
                                                     // comparing different
                                                     // enums...
      {
        t->setText(row, col, colValue.toString());
      } else {
        t->setCell(row, col, colValue.toDouble());
      }
    }
  }

  // want all the detector tables as read-only
  t->setReadOnlyAllColumns(true);
  t->showNormal();

  return t;
}

/**
* Creates a table showing the detectors contributing to the peaks within a
* PeaksWorkspace
* @param wsName :: The name of the workspace
* @param ws :: A pointer to an IPeaksWorkspace object
*/
Table *
MantidUI::createDetectorTable(const QString &wsName,
                              const Mantid::API::IPeaksWorkspace_sptr &ws) {
  // Import the peaks table too for reference
  bool dialog(false), visible(true);
  importTableWorkspace(wsName, dialog, visible);

  auto idtable = ws->createDetectorTable();
  bool transpose = false;
  QString tableName = wsName + "-Detectors";
  Table *t = new MantidTable(appWindow()->scriptingEnv(), idtable, tableName,
                             appWindow(), transpose);
  if (!t)
    return NULL;
  // want all the detector tables as read-only
  t->setReadOnlyAllColumns(true);
  t->showNormal();
  return t;
}

/**
 * Triggered by a delete key press, and attempts to delete a workspace if it
 * passes the focus checks
 */
void MantidUI::deletePressEvent() {
  m_exploreMantid->onClickDeleteWorkspaces();
}

/**
 * Check if drop event can be accepted
 */
bool MantidUI::canAcceptDrop(QDragEnterEvent *e) {
  QString name = e->mimeData()->objectName();

  return (name == "MantidWorkspace" || e->mimeData()->hasUrls() ||
          name == "TiledWindow");
}

bool MantidUI::drop(QDropEvent *e) {
  QString name = e->mimeData()->objectName();
  if (name == "MantidWorkspace") {
    QString text = e->mimeData()->text();
    int endIndex = 0;
    QStringList wsNames;
    while (text.indexOf("[\"", endIndex) > -1) {
      int startIndex = text.indexOf("[\"", endIndex) + 2;
      endIndex = text.indexOf("\"]", startIndex);
      wsNames.append(text.mid(startIndex, endIndex - startIndex));
    }

    foreach (const auto &wsName, wsNames) { importWorkspace(wsName, false); }
    return true;
  } else if (e->mimeData()->hasUrls()) {
    QStringList pyFiles = extractPyFiles(e->mimeData()->urls());
    if (pyFiles.size() > 0) {
      try {
        MantidQt::API::ProjectSerialiser serialiser(m_appWindow);
        serialiser.openScriptWindow(pyFiles);
      } catch (std::runtime_error &error) {
        g_log.error()
            << "Failed to Load the python files. The reason for failure is: "
            << error.what() << '\n';
      } catch (std::logic_error &error) {
        g_log.error()
            << "Failed to Load the python files. The reason for failure is: "
            << error.what() << '\n';
      }
    } else {
      // pass to Loading of mantid workspaces
      m_exploreMantid->dropEvent(e);
    }
    return true;
  } else if (name == "TiledWindow") {
    MdiSubWindow *w =
        m_appWindow->currentFolder()->window(e->mimeData()->text());
    if (!w)
      return false;
    TiledWindow *tw = dynamic_cast<TiledWindow *>(w);
    if (!tw)
      return false;
    tw->removeSelectionToDefaultWindowType();

    return true;
  }

  return false;
}

/// extracts the files from a mimedata object that have a .py extension
QStringList MantidUI::extractPyFiles(const QList<QUrl> &urlList) const {
  QStringList filenames;
  for (int i = 0; i < urlList.size(); ++i) {
    QString fName = urlList[i].toLocalFile();
    if (fName.size() > 0) {
      QFileInfo fi(fName);

      if (fi.suffix().toUpper() == "PY") {
        filenames.append(fName);
      }
    }
  }
  return filenames;
}

/**
Executes the Save Nexus dialogue from the right click context menu.

The Save > Nexus function from the button in the Dock (with Load, Delete, Group,
Sort, Save buttons) is in MantidDock in function handleShowSaveAlgorithm()

saveNexus Input Dialog is a generic dialog.Below code is added to remove
the workspaces except the selected workspace from the InputWorkspace combo

*/
void MantidUI::executeSaveNexus() {
  QString wsName = getSelectedWorkspaceName();
  QHash<QString, QString> presets;
  if (!wsName.isEmpty()) {
    presets["InputWorkspace"] = wsName;
  }
  showAlgorithmDialog("SaveNexus", presets);
}

//-----------------------------------------------------------------------------
/** Open an algorithm dialog to execute the named algorithm.
*
* @param algName :: name of the algorithm
* @param version :: version number, -1 for latest
* @return true if sucessful.
*/
void MantidUI::showAlgorithmDialog(const QString &algName, int version) {
  Mantid::API::IAlgorithm_sptr alg = this->createAlgorithm(algName, version);
  if (!alg)
    return;
  MantidQt::API::AlgorithmDialog *dlg = createAlgorithmDialog(alg);

  if (algName == "Load") {
    // when loading files, we'll need to update the list of recent files
    // hook up MantidUI::fileDialogAccept() to the LoadDialog dialog accepted()
    // signal
    connect(dlg, SIGNAL(accepted()), this, SLOT(loadFileDialogAccept()));
  }

  dlg->show();
  dlg->raise();
  dlg->activateWindow();
}

/**
* Execute an algorithm. Show the algorithm dialog before executing. The property
* widgets will be preset
*   with values in paramList.
* @param algName :: The algorithm name
* @param paramList :: A list of algorithm properties to be passed to
* Algorithm::setProperties
* @param obs :: A pointer to an instance of AlgorithmObserver which will be
* attached to the finish notification
* @param version :: version number, -1 for latest
*/
void MantidUI::showAlgorithmDialog(const QString &algName,
                                   QHash<QString, QString> paramList,
                                   Mantid::API::AlgorithmObserver *obs,
                                   int version) {
  // Get latest version of the algorithm
  Mantid::API::IAlgorithm_sptr alg = this->createAlgorithm(algName, version);
  if (!alg)
    return;

  for (QHash<QString, QString>::Iterator it = paramList.begin();
       it != paramList.end(); ++it) {
    alg->setPropertyValue(it.key().toStdString(), it.value().toStdString());
  }
  MantidQt::API::AlgorithmDialog *dlg = createAlgorithmDialog(alg);

  if (algName == "Load") {
    // when loading files, we'll need to update the list of recent files
    // hook up MantidUI::fileDialogAccept() to the LoadDialog dialog accepted()
    // signal
    connect(dlg, SIGNAL(accepted()), this, SLOT(loadFileDialogAccept()));
  }

  if (obs) {
    dlg->addAlgorithmObserver(obs);
  }

  dlg->show();
  dlg->raise();
  dlg->activateWindow();
}

/**
* Slot for executing an algorithm.
* @param alg :: Shared pointer to an algorithm to execute with all properties
* already set.
*/
void MantidUI::executeAlgorithm(Mantid::API::IAlgorithm_sptr alg) {
  executeAlgorithmAsync(alg);
}

/**
* This creates an algorithm dialog (the default property entry thingie).
*/
MantidQt::API::AlgorithmDialog *
MantidUI::createAlgorithmDialog(Mantid::API::IAlgorithm_sptr alg) {
  QHash<QString, QString> presets;
  QStringList enabled;

  // If a property was explicitly set show it as preset in the dialog
  const std::vector<Mantid::Kernel::Property *> props = alg->getProperties();
  std::vector<Mantid::Kernel::Property *>::const_iterator p = props.begin();
  for (; p != props.end(); ++p) {
    if (!(**p).isDefault()) {
      QString property_name = QString::fromStdString((**p).name());
      presets.insert(property_name, QString::fromStdString((**p).value()));
      enabled.append(property_name);
    }
  }

  // If a workspace is selected in the dock then set this as a preset for the
  // dialog
  QString selected = getSelectedWorkspaceName();
  if (!selected.isEmpty()) {
    QString property_name = findInputWorkspaceProperty(alg);
    if (!presets.contains(property_name)) {
      presets.insert(property_name, selected);
      // Keep it enabled
      enabled.append(property_name);
    }
  }

  // Check if a workspace is selected in the dock and set this as a preference
  // for the input workspace
  // This is an optional message displayed at the top of the GUI.
  QString optional_msg(alg->summary().c_str());

  MantidQt::API::InterfaceManager interfaceManager;
  MantidQt::API::AlgorithmDialog *dlg = interfaceManager.createDialog(
      alg, m_appWindow, false, presets, optional_msg, enabled);
  return dlg;
}

/**
* Find the first input workspace for an algorithm
* @param algorithm :: A pointer to the algorithm instance
*/
QString MantidUI::findInputWorkspaceProperty(
    Mantid::API::IAlgorithm_sptr algorithm) const {
  // Iterate through the properties and find the first input one
  std::vector<Mantid::Kernel::Property *> props = algorithm->getProperties();
  std::vector<Mantid::Kernel::Property *>::const_iterator pend = props.end();
  for (std::vector<Mantid::Kernel::Property *>::const_iterator pitr =
           props.begin();
       pitr != pend; ++pitr) {
    Mantid::Kernel::Property *base_prop = *pitr;
    const Mantid::API::IWorkspaceProperty *ws_prop =
        dynamic_cast<Mantid::API::IWorkspaceProperty *>(base_prop);
    if (ws_prop) {
      unsigned int direction = base_prop->direction();
      if (direction == Mantid::Kernel::Direction::Input ||
          direction == Mantid::Kernel::Direction::InOut) {
        return QString::fromStdString(base_prop->name());
      }
    }
  }
  return QString();
}

void MantidUI::copyWorkspacestoVector(
    const QList<QTreeWidgetItem *> &selectedItems,
    std::vector<std::string> &inputWSVec) {
  // iterate through each of the selected workspaces
  QList<QTreeWidgetItem *>::const_iterator itr;
  for (itr = selectedItems.begin(); itr != selectedItems.end(); ++itr) {
    std::string inputWSName = (*itr)->text(0).toStdString();
    inputWSVec.push_back(inputWSName);
  } // end of for loop for input workspaces
}

/**
* Determine if the workspace has one or more UB matrixes on one of it's samples.
* @param wsName
* @return True if present
*/
bool MantidUI::hasUB(const QString &wsName) {
  const std::string algName("HasUB");
  Mantid::API::IAlgorithm_sptr alg;
  try {

    alg = Mantid::API::AlgorithmManager::Instance().create(algName);
  } catch (...) {
    QMessageBox::critical(appWindow(), "MantidPlot - Algorithm error",
                          "Cannot create algorithm " +
                              QString::fromStdString(algName));
    return false;
  }
  if (!alg) {
    return false;
  }

  alg->setLogging(false);
  alg->setPropertyValue("Workspace", wsName.toStdString());
  executeAlgorithmAsync(alg, true);

  bool hasUB = alg->getProperty("HasUB");
  return hasUB;
}

/**
* Clears the UB from the selected workspace
* @param wsName :: selected workspace name
*/
void MantidUI::clearUB(const QStringList &wsName) {
  const std::string algName("ClearUB");
  const int version = -1;
  for (int i = 0; i < wsName.size(); ++i) {
    Mantid::API::IAlgorithm_sptr alg;
    try {
      alg = Mantid::API::AlgorithmManager::Instance().create(algName, version);
    } catch (...) {
      QMessageBox::critical(appWindow(), "MantidPlot - Algorithm error",
                            "Cannot create algorithm " +
                                QString::fromStdString(algName) + " version " +
                                QString::number(version));
      return;
    }
    if (!alg) {
      return;
    }

    alg->setPropertyValue("Workspace", wsName[i].toStdString());
    executeAlgorithmAsync(alg);
  }
}

/**
* Renames selected workspace
* @param wsName :: selected workspace name
*/
void MantidUI::renameWorkspace(QStringList wsName) {
  // If the wsname is blank look for an active window and assume this workspace
  // is
  // the one to rename
  if (wsName.isEmpty()) {
    MantidMatrix *matrix =
        dynamic_cast<MantidMatrix *>(appWindow()->activeWindow());
    if (matrix) {
      wsName.append(matrix->workspaceName());
    } else {
      return;
    }
  }

  // Determine the algorithm
  QString algName("RenameWorkspace");
  if (wsName.size() > 1)
    algName = "RenameWorkspaces";

  QHash<QString, QString> presets;
  if (wsName.size() > 1) {
    presets["InputWorkspaces"] = wsName.join(",");
  } else {
    presets["InputWorkspace"] = wsName[0];
  }
  showAlgorithmDialog(algName, presets);
}

void MantidUI::setFitFunctionBrowser(
    MantidQt::MantidWidgets::FitPropertyBrowser *newBrowser) {
  if (newBrowser == NULL)
    m_fitFunction = m_defaultFitFunction;
  else
    m_fitFunction = newBrowser;
}

void MantidUI::groupWorkspaces() {
  try {
    std::string sgrpName("NewGroup");
    QString qwsGrpName = QString::fromStdString(sgrpName);
    // get selected workspaces
    auto selectedItems = m_exploreMantid->getSelectedWorkspaceNames();
    if (selectedItems.size() < 2) {
      throw std::runtime_error("Select atleast two workspaces to group ");
    }
    if (Mantid::API::AnalysisDataService::Instance().doesExist(sgrpName)) {
      if (QMessageBox::question(
              appWindow(), "",
              "Workspace " + qwsGrpName +
                  " already exists. Do you want to replace it?",
              QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes)
        return;
    }
    //
    std::string algName("GroupWorkspaces");
    Mantid::API::IAlgorithm_sptr alg =
        Mantid::API::AlgorithmManager::Instance().create(algName, 1);
    alg->initialize();
    alg->setProperty("InputWorkspaces", selectedItems);
    alg->setPropertyValue("OutputWorkspace", sgrpName);
    // execute the algorithm
    bool bStatus = alg->execute();
    if (!bStatus) {
      QMessageBox::critical(appWindow(), "MantidPlot - Algorithm error",
                            " Error in GroupWorkspaces algorithm");
    }

  } catch (std::invalid_argument &) {
    QMessageBox::critical(appWindow(), "MantidPlot - Algorithm error",
                          " Error in GroupWorkspaces algorithm");
  } catch (Mantid::Kernel::Exception::NotFoundError &) // if not a valid object
                                                       // in analysis data
                                                       // service
  {
    QMessageBox::critical(appWindow(), "MantidPlot - Algorithm error",
                          " Error in GroupWorkspaces algorithm");
  } catch (std::runtime_error &) {
    QMessageBox::critical(appWindow(), "MantidPlot - Algorithm error",
                          " Error in GroupWorkspaces algorithm");
  } catch (std::exception &) {
    QMessageBox::critical(appWindow(), "MantidPlot - Algorithm error",
                          " Error in GroupWorkspaces algorithm");
  }
}
void MantidUI::ungroupWorkspaces() {
  try {
    auto selectedItems = m_exploreMantid->getSelectedWorkspaceNames();
    if (selectedItems.empty()) {
      throw std::runtime_error("Select a group workspace to Ungroup.");
    }

    // workspace name
    std::string wsname = selectedItems[0];

    std::string algName("UnGroupWorkspace");
    Mantid::API::IAlgorithm_sptr alg =
        Mantid::API::AlgorithmManager::Instance().create(algName, 1);
    alg->initialize();
    alg->setProperty("InputWorkspace", wsname);

    // execute the algorithm
    bool bStatus = alg->execute();
    if (!bStatus) {
      QMessageBox::critical(appWindow(), "MantidPlot - Algorithm error",
                            " Error in UnGroupWorkspace algorithm");
    }

  } catch (std::invalid_argument &) {
    QMessageBox::critical(appWindow(), "MantidPlot - Algorithm error",
                          " Error in UnGroupWorkspace algorithm");
  } catch (std::runtime_error &) {
    QMessageBox::critical(appWindow(), "MantidPlot - Algorithm error",
                          " Error in UnGroupWorkspace algorithm");
  } catch (std::exception &) {
    QMessageBox::critical(appWindow(), "MantidPlot - Algorithm error",
                          " Error in UnGroupWorkspace algorithm");
  }
}

Mantid::API::IAlgorithm_sptr MantidUI::createAlgorithm(const QString &algName,
                                                       int version) {
  emit algorithmAboutToBeCreated();
  Mantid::API::IAlgorithm_sptr alg;
  try {
    alg = Mantid::API::AlgorithmManager::Instance().create(
        algName.toStdString(), version);
  } catch (...) {
    QString message = "Cannot create algorithm \"" + algName + "\"";
    if (version != -1) {
      message += " version " + QString::number(version);
    }
    QMessageBox::warning(appWindow(), "MantidPlot", message);
    alg = Mantid::API::IAlgorithm_sptr();
  }
  return alg;
}

bool MantidUI::executeAlgorithmAsync(Mantid::API::IAlgorithm_sptr alg,
                                     const bool wait) {
  if (wait) {
    Poco::ActiveResult<bool> result(alg->executeAsync());
    while (!result.available()) {
      QCoreApplication::processEvents();
    }
    result.wait();

    try {
      return result.data();
    } catch (Poco::NullPointerException &) {
      return false;
    }
  } else {
    try {
      alg->executeAsync();
    } catch (Poco::NoThreadAvailableException &) {
      g_log.error() << "No thread was available to run the " << alg->name()
                    << " algorithm in the background.\n";
      return false;
    }
    return true;
  }
}

/**
* Slot to update the recent files list (from main appWindow) when accepting
* LoadDialog dialogs
*/
void MantidUI::loadFileDialogAccept() {
  QObject *sender = QObject::sender();
  MantidQt::API::AlgorithmDialog *dlg =
      reinterpret_cast<MantidQt::API::AlgorithmDialog *>(sender);
  if (!dlg)
    return; // should never happen

  QString fn = MantidQt::API::AlgorithmInputHistory::Instance().previousInput(
      "Load", "Filename");
  appWindow()->updateRecentFilesList(fn);
  // recent files list updated. After this point, the Qt signal handler will go
  // to LoadDialog::accept()
}

void MantidUI::handleLoadDAEFinishedNotification(
    const Poco::AutoPtr<Algorithm::FinishedNotification> &pNf) {
  std::string wsNAme = pNf->algorithm()->getProperty("OutputWorkspace");
  emit needToCreateLoadDAEMantidMatrix(QString::fromStdString(wsNAme));
}

void MantidUI::createLoadDAEMantidMatrix(const QString &wsQName) {
  std::string wsName = wsQName.toStdString();
  Workspace_sptr ws = AnalysisDataService::Instance().retrieve(wsName);

  if (ws.use_count() == 0) {
    QMessageBox::warning(m_appWindow, tr("Mantid"),
                         tr("A workspace with this name already exists.\n"),
                         QMessageBox::Ok, QMessageBox::Ok);
    return;
  }
  importMatrixWorkspace(QString::fromStdString(wsName), -1, -1, false, true);

  int updateInterval = m_DAE_map[wsName];
  if (updateInterval > 0) {
    IAlgorithm_sptr updater = createAlgorithm("UpdateDAE");
    updater->setPropertyValue("Workspace", wsName);
    updater->setPropertyValue("UpdateRate",
                              QString::number(updateInterval).toStdString());
    executeAlgorithmAsync(updater);
  }
}

void MantidUI::showCritical(const QString &text) {
  QMessageBox::critical(appWindow(), "Mantid - Error", text);
}

void MantidUI::showAlgMonitor() { m_algMonitor->showDialog(); }

void MantidUI::handleConfigServiceUpdate(
    Mantid::Kernel::ConfigValChangeNotification_ptr pNf) {
  if (pNf->key() == "pythonscripts.directories") {
    // this code ad the filepaths inside the pythonscripts.directories to the
    // python sys if they are not already there. This is to cope with the
    // requirement
    // at #7097 of letting python scripts usable when downloaded from Script
    // Repository.
    // This code was added because changing the pythonscripts.directories update
    // the
    // python path just after restarting MantidPlot.
    QString code =
        QString("import sys\n"
                "paths = '%1'\n"
                "list_of_path = paths.split(';')\n"
                "if isinstance(list_of_path,str):\n"
                "  list_of_path = [list_of_path,]\n"
                "for value in list_of_path:\n"
                "  if value not in sys.path: sys.path.append(value)\n")
            .arg(QString::fromStdString(pNf->curValue()));
    // run this code silently
    appWindow()->runPythonScript(code, false, true, true);
  }
}

void MantidUI::manageMantidWorkspaces() {
#ifdef _WIN32
  memoryImage();
#else
  QMessageBox::warning(appWindow(), tr("Mantid Workspace"),
                       tr("Clicked on Manage Workspace"), tr("Ok"),
                       tr("Cancel"), QString(), 0, 1);
#endif
}

/** Create an instrument window from a named workspace.
*  The window will be returned hidden.
*  @param wsName The name of the workspace for which to generate the instrument
* view.
*  @param tab    The index of the tab (starting from 0) to initially display
* (default: 0)
*  @return A pointer to the instrument window widget if created. NULL otherwise.
*/
InstrumentWindow *MantidUI::getInstrumentView(const QString &wsName, int tab) {
  if (!Mantid::API::AnalysisDataService::Instance().doesExist(
          wsName.toStdString()))
    return NULL;
  MatrixWorkspace_const_sptr ws =
      boost::dynamic_pointer_cast<const MatrixWorkspace>(getWorkspace(wsName));
  if (!ws)
    return NULL;
  ScopedOverrideCursor waitCursor;
  Mantid::Geometry::Instrument_const_sptr instr = ws->getInstrument();
  if (!instr || instr->getName().empty()) {
    QMessageBox::critical(appWindow(), "MantidPlot - Error",
                          "Instrument view cannot be opened");
    return NULL;
  }

  // Need a new window
  const QString windowName(QString("InstrumentWindow:") + wsName);

  try {
    InstrumentWindow *insWin = new InstrumentWindow(
        wsName, QString("Instrument"), appWindow(), windowName);

    insWin->selectTab(tab);

    appWindow()->addMdiSubWindow(insWin);

    return insWin;
  } catch (const std::exception &e) {
    QString errorMessage =
        "Instrument view cannot be created:\n\n" + QString(e.what());
    QMessageBox::critical(appWindow(), "MantidPlot - Error", errorMessage);

    return NULL;
  }
}

void MantidUI::showMantidInstrument(const QString &wsName) {
  InstrumentWindow *insWin = getInstrumentView(wsName);

  if (!insWin) {
    m_lastShownInstrumentWin = NULL;
    return;
  }

  if (workspacesDockPlot1To1()) {
    // replace last one
    if (m_lastShownInstrumentWin) {
      m_lastShownInstrumentWin->close();
      QPoint p = m_lastShownInstrumentWin->pos();
      delete m_lastShownInstrumentWin;
      insWin->move(p);
    }
  }
  m_lastShownInstrumentWin = insWin;

  if (!insWin->isVisible()) {
    insWin->show();
  }
}

void MantidUI::showMantidInstrument() {
  MantidMatrix *m = dynamic_cast<MantidMatrix *>(appWindow()->activeWindow());
  if (!m || !isOfType(m, "MantidMatrix"))
    return;
  if (!m->workspaceName().isEmpty()) {
    showMantidInstrument(m->workspaceName());
  }
}

void MantidUI::showMantidInstrumentSelected() {
  QString wsName = getSelectedWorkspaceName();
  if (!wsName.isEmpty())
    showMantidInstrument(wsName);
}

void MantidUI::mantidMenuAboutToShow() {
  mantidMenu->clear();
  // Ticket #672 Mantid Menu Improvements

  /*mantidMenu->insertItem(tr("&Manage Workspaces"), this,
  SLOT(manageMantidWorkspaces() ) );
  mantidMenu->insertItem(tr("&Instrument Window"), this,
  SLOT(showMantidInstrument() ) );
  mantidMenu->insertItem(tr("&Plot Memory Usage"), this,
  SLOT(manageMantidWorkspaces() ));
  */

  QAction *tstAction = new QAction("&Plot Memory Usage", this);
  connect(tstAction, SIGNAL(triggered()), this, SLOT(manageMantidWorkspaces()));
  mantidMenu->addAction(tstAction);
}

void MantidUI::insertMenu() {
  auto mantidMenuAction = appWindow()->myMenuBar()->addMenu(mantidMenu);
  mantidMenuAction->setText(tr("Man&tid"));
}

void MantidUI::clearAllMemory(const bool prompt) {
  if (prompt) {
    QMessageBox::StandardButton pressed = QMessageBox::question(
        appWindow(), "MantidPlot",
        "All workspaces and windows will be removed. Are you sure?",
        QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok);

    if (pressed != QMessageBox::Ok)
      return;
  }
  // If any python objects need to be cleared away then the GIL needs to be
  // held. This doesn't feel like
  // it is in the right place but it will do no harm
  ScopedPythonGIL gil;
  // Relevant notifications are connected to signals that will close all
  // dependent windows
  Mantid::API::FrameworkManager::Instance().clear();
}

void MantidUI::saveProject(bool saved) {
  if (!saved) {
    QString savemsg =
        tr("Save changes to project: <p><b> %1 </b> ?").arg("untitled");
    int result = QMessageBox::information(appWindow(), tr("MantidPlot"),
                                          savemsg, tr("Yes"), tr("No"), 0, 2);
    if (result == 0)
      appWindow()->saveProject();
  }
  // close all the windows opened
  foreach (MdiSubWindow *sub_win, appWindow()->windowsList()) {
    sub_win->setconfirmcloseFlag(false);
    sub_win->close();
  }
  Mantid::API::FrameworkManager::Instance().clear();
}

void MantidUI::enableSaveNexus(const QString &wsName) {
  appWindow()->enableSaveNexus(wsName);
}

void MantidUI::disableSaveNexus() { appWindow()->disableSaveNexus(); }

/**
*  Prepares the Mantid Menu depending on the state of the active MantidMatrix.
*/
void MantidUI::menuMantidMatrixAboutToShow() {
  menuMantidMatrix->clear();
  MantidMatrix *w = dynamic_cast<MantidMatrix *>(appWindow()->activeWindow());
  // menuMantidMatrix->addAction(actionCopyValues);
  menuMantidMatrix->addAction(actionCopyDetectorsToTable);
  menuMantidMatrix->addSeparator();
  menuMantidMatrix->addAction(tr("Set &Properties..."), w,
                              SLOT(setMatrixProperties()));

  ///
  menuMantidMatrix->addSeparator();
  QAction *action = new QAction("Show instrument", this);
  connect(action, SIGNAL(triggered()), this, SLOT(showMantidInstrument()));
  menuMantidMatrix->addAction(action);

  action = new QAction("Plot spectrum...", this);
  connect(action, SIGNAL(triggered()), m_exploreMantid.get(),
          SLOT(plotSpectra()));
  menuMantidMatrix->addAction(action);

  action = new QAction("Plot as waterfall", this);
  connect(action, SIGNAL(triggered()), SLOT(plotWholeAsWaterfall()));
  menuMantidMatrix->addAction(action);

  action = new QAction("Sample Logs...", this);
  connect(action, SIGNAL(triggered()), this, SLOT(showLogFileWindow()));
  menuMantidMatrix->addAction(action);

  action = new QAction("Sample Material...", this);
  connect(action, SIGNAL(triggered()), this, SLOT(showSampleMaterialWindow()));
  menuMantidMatrix->addAction(action);

  action = new QAction("Show History", this);
  connect(action, SIGNAL(triggered()), this, SLOT(showAlgorithmHistory()));
  menuMantidMatrix->addAction(action);

  action = new QAction("Save Nexus", this);
  connect(action, SIGNAL(triggered()), this, SLOT(saveNexusWorkspace()));
  menuMantidMatrix->addAction(action);

  action = new QAction("Rename", this);
  connect(action, SIGNAL(triggered()), this, SLOT(renameWorkspace()));
  menuMantidMatrix->addAction(action);

  // separate delete
  menuMantidMatrix->addSeparator();

  action = new QAction("Delete", this);
  connect(action, SIGNAL(triggered()), m_exploreMantid.get(),
          SLOT(deleteWorkspaces()));
  menuMantidMatrix->addAction(action);
}

/// Catches the signal from InstrumentWindow to plot a spectrum.
MultiLayer *MantidUI::plotInstrumentSpectrum(const QString &wsName, int spec) {
  QMessageBox::information(appWindow(), "OK",
                           wsName + " " + QString::number(spec));
  return plotSpectraRange(wsName, spec, spec, MantidQt::DistributionDefault,
                          false);
}

/// Catches the signal from InstrumentWindow to plot a spectrum.
MultiLayer *MantidUI::plotInstrumentSpectrumList(const QString &wsName,
                                                 const std::set<int> &spec) {
  return plot1D(wsName, spec, true, MantidQt::DistributionDefault, false);
}

/**
* Sets the flag that tells the scripting environment that
* a script is currently running
*/
void MantidUI::setIsRunning(bool) {
  // deprecated
}

/**
* Merge the curves from the two given MultiLayer objects
*/
MultiLayer *MantidUI::mergePlots(MultiLayer *mlayer_1, MultiLayer *mlayer_2) {
  if (!mlayer_1)
    return NULL;
  if (!mlayer_2)
    return mlayer_1;
  int ncurves_on_two = mlayer_2->activeGraph()->visibleCurves();
  for (int c = 0; c < ncurves_on_two; ++c) {
    mlayer_1->insertCurve(mlayer_2, c);
  }

  // Hide the second graph for now as closing it
  // deletes the curves that were associated with it
  mlayer_2->close();

  return mlayer_1;
}

MantidMatrix *MantidUI::getMantidMatrix(const QString &wsName) {
  QList<MdiSubWindow *> windows = appWindow()->windowsList();
  QListIterator<MdiSubWindow *> itr(windows);
  MantidMatrix *m(0);
  while (itr.hasNext()) {
    MdiSubWindow *w = itr.next();
    if (isOfType(w, "MantidMatrix") && w->name() == wsName) {
      m = qobject_cast<MantidMatrix *>(w);
    }
  }
  return m;
}

bool MantidUI::createScriptInputDialog(const QString &alg_name,
                                       const QString &preset_values,
                                       const QString &optional_msg,
                                       const QStringList &enabled,
                                       const QStringList &disabled) {
  IAlgorithm_sptr alg =
      AlgorithmManager::Instance().newestInstanceOf(alg_name.toStdString());
  if (!alg) {
    return false;
  }

  // PyQt can't pass a dictionary across the boundary as a dictionary can
  // contain arbitrary data types
  QHash<QString, QString> presets;
  QStringList chopped = preset_values.split('|', QString::SkipEmptyParts);
  QStringListIterator itr(chopped);
  while (itr.hasNext()) {
    QString namevalue = itr.next();
    QString name = namevalue.section('=', 0, 0);
    // Simplified removes trims from start and end and replaces all n counts of
    // whitespace with a single whitespace
    QString value = namevalue.section('=', 1, 1).simplified();
    presets.insert(name, value);
  }

  MantidQt::API::InterfaceManager interfaceManager;
  MantidQt::API::AlgorithmDialog *dlg = interfaceManager.createDialog(
      alg, m_appWindow->getScriptWindowHandle(), true, presets, optional_msg,
      enabled, disabled);
  dlg->setShowKeepOpen(false);
  return (dlg->exec() == QDialog::Accepted);
}

/** Displays a string in a Qtiplot table
*  @param logName :: the title of the table is based on this
*  @param data :: the string to display
*/
void MantidUI::importString(const QString &logName, const QString &data) {
  importString(logName, data, QString(""));
}

/** Displays a string in a Qtiplot table
*  @param logName :: the title of the table is based on this
*  @param data :: the string to display
*  @param sep :: the seperator character
*  @param wsName :: add workspace name to the table window title bar, defaults
* to logname if left blank
*/
void MantidUI::importString(const QString &logName, const QString &data,
                            const QString &sep, const QString &wsName) {
  QStringList loglines = QStringList(data);
  if (sep.length() > 0) {
    loglines = data.split(sep, QString::SkipEmptyParts);
  }

  Table *t = new Table(appWindow()->scriptingEnv(), loglines.size(), 1, "",
                       appWindow(), 0);
  if (!t)
    return;
  // Have to replace "_" since the legend widget uses them to separate things
  QString label;
  label = logName;
  formatLogName(label, wsName);

  appWindow()->initTable(t, appWindow()->generateUniqueName(label + "-"));
  t->setColName(0, "Log entry");
  t->setColumnType(0, Table::Text);
  t->setReadOnlyColumn(0, true); // Read-only

  for (int i = 0; i < loglines.size(); ++i) {
    t->setText(i, 0, loglines[i]);
  }

  // Show table
  t->resize(2 * t->table()->horizontalHeader()->sectionSize(0) + 55,
            (qMin(10, 1) + 1) * t->table()->verticalHeader()->sectionSize(0) +
                100);
  t->setAttribute(Qt::WA_DeleteOnClose);
  t->resizeColumnsToContents();
  t->showNormal();
}
/** Displays a string in a Qtiplot table
*  @param logName :: the title of the table is based on this
*  @param data :: a formatted string with the time series data to display
*  @param wsName :: add workspace name to the table window title bar, defaults
* to logname if left blank
*/
void MantidUI::importStrSeriesLog(const QString &logName, const QString &data,
                                  const QString &wsName) {
  QStringList loglines = data.split("\n", QString::SkipEmptyParts);

  int rowcount(loglines.count());
  Table *t =
      new Table(appWindow()->scriptingEnv(), rowcount, 2, "", appWindow(), 0);
  if (!t)
    return;
  QString label;
  label = logName;
  formatLogName(label, wsName);

  appWindow()->initTable(t, appWindow()->generateUniqueName(label + "-"));
  t->setColName(0, "Time");
  t->setColumnType(0, Table::Time);
  t->setTimeFormat("HH:mm:ss", 0, false);
  t->setColName(1, label.section("-", 1));
  t->setColumnType(1, Table::Text);

  // Make both columns read-only
  t->setReadOnlyColumn(0, true);
  t->setReadOnlyColumn(1, true);

  QStringList::const_iterator sEnd = loglines.end();
  int row(0);
  for (QStringList::const_iterator sItr = loglines.begin(); sItr != sEnd;
       ++sItr, ++row) {
    QStringList ts = (*sItr).split(QRegExp("\\s+"));
    t->setText(row, 0, ts[1]);
    QStringList ds(ts);
    ds.removeFirst(); // remove date
    ds.removeFirst(); // and time
    t->setText(row, 1, ds.join(" "));
    t->setTextAlignment(row, 1, Qt::AlignLeft | Qt::AlignVCenter);
  }

  // Show table
  t->resize(2 * t->table()->horizontalHeader()->sectionSize(0) + 55,
            (qMin(10, rowcount) + 1) *
                    t->table()->verticalHeader()->sectionSize(0) +
                100);
  t->setAttribute(Qt::WA_DeleteOnClose);
  t->resizeColumnsToContents();
  t->showNormal();
}

//------------------------------------------------------------------------------------------------
/**  Import a numeric log data. It will be shown in a graph and copied into a
* table
* @param wsName :: The workspace name which log data will be imported
* @param logName :: The name of the log property to import
* @param filter :: Filter flag telling how to filter the log data.
* - 0 means no filtering
* - 1 filter by running status
* - 2 filter by period
* - 3 filter by status & period
*/
void MantidUI::importNumSeriesLog(const QString &wsName, const QString &logName,
                                  int filter) {
  // if you need to add a final filter value to the end of the filter to match
  // the extent of the data, then set this to the index of the row to add the
  // value
  int addFinalFilterValueIndex = 0;
  Mantid::Kernel::DateAndTime lastFilterTime;

  // Convert input int into enum value
  const Mantid::API::LogFilterGenerator::FilterType filterType = [&filter]() {
    switch (filter) {
    case 0:
      return Mantid::API::LogFilterGenerator::FilterType::None;
    case 1:
      return Mantid::API::LogFilterGenerator::FilterType::Status;
    case 2:
      return Mantid::API::LogFilterGenerator::FilterType::Period;
    case 3:
      return Mantid::API::LogFilterGenerator::FilterType::StatusAndPeriod;
    default:
      return Mantid::API::LogFilterGenerator::FilterType::None;
    }
  }();

  // Make sure the workspace exists and contains the log
  MatrixWorkspace_const_sptr ws =
      boost::dynamic_pointer_cast<const MatrixWorkspace>(getWorkspace(wsName));
  if (!ws)
    return;

  Mantid::Kernel::Property *logData =
      ws->run().getLogData(logName.toStdString());
  if (!logData)
    return;

  // Generate the filter
  Mantid::API::LogFilterGenerator filterGenerator(filterType, ws);
  const auto &flt = filterGenerator.generateFilter(logName.toStdString());

  // Get a map of time/value. This greatly speeds up display.
  // NOTE: valueAsMap() skips repeated values.
  std::map<DateAndTime, double> time_value_map =
      flt->data()->valueAsCorrectMap();
  int rowcount = static_cast<int>(time_value_map.size());
  int colCount = 2;

  Table *t = new Table(appWindow()->scriptingEnv(), rowcount, colCount, "",
                       appWindow(), 0);
  if (!t)
    return;
  // Have to replace "_" since the legend widget uses them to separate things
  QString label;
  label = logName;
  formatLogName(label, wsName);

  // Get the starting time of the log.
  Mantid::Kernel::DateAndTime startTime;
  // Toggle to switch between using the real date or the change in seconds.
  bool useAbsoluteDate = false;

  if (!time_value_map.empty()) {
    try {
      startTime = ws->run().startTime();
    } catch (std::runtime_error &) {
      // This means the start time is missing, use absolute times instead
      useAbsoluteDate = true;
    }
  }

  // Make a unique title, and put in the start time of the log
  QString title =
      label + QString::fromStdString(" (" + startTime.toSimpleString() + ")");
  appWindow()->initTable(t, appWindow()->generateUniqueName(title));

  // Make both columns read-only
  t->setReadOnlyColumn(0, true);
  t->setReadOnlyColumn(1, true);
  // Set numeric precision.
  // It's the number of all digits
  t->setNumericPrecision(16);

  if (useAbsoluteDate) {
    // --------- Date
    t->setColName(0, "Time");
    t->setColumnType(0, Table::Date);
    t->setDateFormat("yyyy-MMM-dd HH:mm:ss.ffffff", 0, false);
  } else {
    // Seconds offset
    t->setColName(0, "Time (sec)");
    t->setColumnType(0, Table::Numeric);
  }

  // Make the column header with the units, if any
  QString column1 = label.section("-", 1);
  if (logData->units() != "")
    column1 =
        column1 + QString::fromStdString(" (in " + logData->units() + ")");
  t->setColName(1, column1);

  int iValueCurve = 0;

  // Applying filter column to table
  if (filterType != Mantid::API::LogFilterGenerator::FilterType::None) {
    if (flt->filter()) {
      // Valid filter was found
      t->addColumns(2);
      t->setColName(2, "FTime");

      if (useAbsoluteDate) {
        t->setColumnType(2, Table::Date);
        t->setDateFormat("yyyy-MMM-dd HH:mm:ss", 2,
                         false); // This is the format of the date column
      } else {
        t->setColumnType(2, Table::Numeric);
      }

      t->setColPlotDesignation(2, Table::X);
      t->setColName(3, "Filter");

      if (flt->filter()->size() > rowcount) {
        t->addRows(flt->filter()->size() - rowcount);
      }

      if (flt->data()->size() > rowcount) {
        t->addRows(flt->data()->size() - rowcount);
      }

      for (int i = 0; i < flt->filter()->size(); i++) {
        if (flt->filter()->nthInterval(i).begin() >
            0) // protect against bizarre values we sometimes get
        {
          std::string time_string =
              extractLogTime(flt->filter()->nthInterval(i).begin(),
                             useAbsoluteDate, startTime);

          t->setText(i, 2, QString::fromStdString(time_string));
          t->setCell(i, 3, !flt->filter()->nthValue(i));
          if ((i + 1 == flt->filter()->size()) &&
              (!flt->filter()->nthValue(
                  i))) // last filter value and set to be filtering
          {
            addFinalFilterValueIndex = i + 1;
            lastFilterTime = flt->filter()->nthInterval(i).begin();
          }
        }
      }

    } // end (valid filter exists)
  }

  Mantid::Kernel::DateAndTime lastTime;
  double lastValue = 0;

  // Iterate through the time-value map.
  std::map<DateAndTime, double>::iterator it = time_value_map.begin();
  if (it != time_value_map.end()) {
    for (int i = 0; it != time_value_map.end(); ++i, ++it) {
      lastTime = it->first;
      lastValue = it->second;

      std::string time_string =
          extractLogTime(lastTime, useAbsoluteDate, startTime);

      t->setText(i, 0, QString::fromStdString(time_string));
      t->setCell(i, 1, lastValue);
    }
  }

  try {
    // Set the filter strings
    if (filter && flt->filter() && lastTime < flt->filter()->lastTime()) {
      rowcount = static_cast<int>(time_value_map.size());
      if (rowcount == t->numRows())
        t->addRows(1);

      std::string time_string =
          extractLogTime(flt->filter()->lastTime(), useAbsoluteDate, startTime);

      t->setText(rowcount, 0, QString::fromStdString(time_string));
      t->setCell(rowcount, 1, lastValue);
    }
  } catch (...) {
  }

  // add a final filter value if needed and the data exceed the filter range
  if ((addFinalFilterValueIndex > 0) && (lastFilterTime < lastTime)) {
    if (addFinalFilterValueIndex >= t->numRows()) {
      t->addRows(1);
    }
    std::string end_string =
        extractLogTime(lastTime, useAbsoluteDate, startTime);
    t->setText(addFinalFilterValueIndex, 2, QString::fromStdString(end_string));
    t->setCell(addFinalFilterValueIndex, 3,
               1); // only need to add it if filter =1
  }

  // Show table

  t->resize(2 * t->table()->horizontalHeader()->sectionSize(0) + 55,
            (qMin(10, t->numRows()) + 1) *
                    t->table()->verticalHeader()->sectionSize(0) +
                100);
  // t->askOnCloseEvent(false);
  t->setAttribute(Qt::WA_DeleteOnClose);
  t->showNormal();

  // Do not create graph if there is only one value in the table or using
  // absolute dates
  if (t->numRows() < 2 || useAbsoluteDate)
    return;

  QStringList colNames;
  if (filter && flt->filter()) {
    colNames << t->colName(3);
  }
  colNames << t->colName(1);
  MultiLayer *ml = appWindow()->multilayerPlot(t, colNames, GraphOptions::Line);
  // ml->askOnCloseEvent(false);
  ml->setAttribute(Qt::WA_DeleteOnClose);

  Graph *g = ml->activeGraph();

  // Set x-axis label format
  if (useAbsoluteDate) {
    Mantid::Kernel::DateAndTime label_as_ptime =
        flt->data()->nthInterval(0).begin();
    QDateTime dt = QDateTime::fromTime_t(uint(label_as_ptime.to_localtime_t()));
    QString format = dt.toString(Qt::ISODate) + ";HH:mm:ss";
    g->setLabelsDateTimeFormat(2, ScaleDraw::Date, format);
  } else {
    // Make the x-axis a numeric format, 0 decimals
    g->setLabelsNumericFormat(2, 1, 0, "");
  }

  // Set style #3 (HorizontalSteps) for curve iValueCurve
  g->setCurveStyle(iValueCurve, 3);
  QPen pn = QPen(Qt::black);
  g->setCurvePen(iValueCurve, pn);

  if (filter && flt->filter()) {
    int iFilterCurve = 1;
    QwtPlotCurve *c = g->curve(iFilterCurve);
    if (c) {
      // Set the right axis as Y axis for the filter curve.
      c->setAxis(2, 1);
      // Set style #3 (HorizontalSteps) for curve 1
      // Set scale of right Y-axis (#3) from 0 to 1
      g->setCurveStyle(iFilterCurve, 3);
      g->setScale(3, 0, 1);
      // Fill area under the curve with a pattern
      QBrush br = QBrush(Qt::gray, Qt::Dense5Pattern);
      g->setCurveBrush(iFilterCurve, br);
      // Set line colour
      QPen pn = QPen(Qt::gray);
      g->setCurvePen(iFilterCurve, pn);
    }
  }
  g->setTitle(label);
  g->setAutoScale();

  ml->showNormal();
}

/** Format a log name for a title bar
@param[out] label :: the QString that will hold the caption
@param[in] wsName :: the workspace name
*/
void MantidUI::formatLogName(QString &label, const QString &wsName) {
  label.replace("_", "-");
  if (!wsName.isEmpty()) {
    label = wsName + "-" + label;
  }
}

std::string MantidUI::extractLogTime(Mantid::Kernel::DateAndTime value,
                                     bool useAbsoluteDate,
                                     Mantid::Kernel::DateAndTime start) {
  std::string time_string;
  if (useAbsoluteDate) {
    // Convert time into string
    time_string = value.toSimpleString();
  } else {
    // How many seconds elapsed?
    Mantid::Kernel::time_duration elapsed = value - start;
    double seconds = Mantid::Kernel::DateAndTime::secondsFromDuration(elapsed);

    // Output with 6 decimal points
    std::ostringstream oss;
    oss.precision(6);
    oss << std::fixed << seconds;
    time_string = oss.str();
  }
  return time_string;
}

void MantidUI::showLogFileWindow() {
  // Need a new window to display entries
  MantidSampleLogDialog *dlg =
      new MantidSampleLogDialog(getSelectedWorkspaceName(), this);
  dlg->setModal(false);
  dlg->setAttribute(Qt::WA_DeleteOnClose);
  dlg->show();
  dlg->setFocus();
}

void MantidUI::showSampleMaterialWindow() {
  MantidSampleMaterialDialog *dlg =
      new MantidSampleMaterialDialog(getSelectedWorkspaceName(), this);
  dlg->setModal(false);
  dlg->setAttribute(Qt::WA_DeleteOnClose);
  dlg->show();
  dlg->setFocus();
  dlg->updateMaterial();
}

//  *****      Plotting Methods     *****  //

/** Create a Table form specified spectra in a MatrixWorkspace
@param tableName :: Table name
@param workspaceName :: Shared pointer to the workspace
@param indexList :: A list of spectra indices to go to the table
@param errs :: If true include the errors into the table
@param binCentres :: If true the X column will contain the bin centres, i.e.
(x_i+1 + x_i)/2.
If false the Y values will be in the same row with the left bin boundaries.
If the workspace is not a histogram the parameter is ignored.
*/
Table *MantidUI::createTableFromSpectraList(const QString &tableName,
                                            const QString &workspaceName,
                                            QList<int> indexList, bool errs,
                                            bool binCentres) {
  MatrixWorkspace_const_sptr workspace =
      boost::dynamic_pointer_cast<const MatrixWorkspace>(
          getWorkspace(workspaceName));
  if (!workspace) {
    throw std::invalid_argument(workspaceName.toStdString() +
                                " is not a Matrix Workspace.");
  }

  int nspec = static_cast<int>(workspace->getNumberHistograms());
  // Loop through the list of index and remove all the indexes that are out of
  // range

  for (QList<int>::iterator it = indexList.begin(); it != indexList.end();) {
    if ((*it) > nspec || (*it) < 0) {
      // erase moves iterator to next position
      it = indexList.erase(it);
    } else {
      ++it;
    }
  }
  if (indexList.empty())
    return 0;

  int c = errs ? 2 : 1;
  int numRows = static_cast<int>(workspace->blocksize());
  bool isHistogram = workspace->isHistogramData();
  int no_cols = static_cast<int>(indexList.size());
  Table *t = new Table(appWindow()->scriptingEnv(), numRows, (1 + c) * no_cols,
                       "", appWindow(), 0);
  appWindow()->initTable(t, appWindow()->generateUniqueName(tableName + "-"));
  // t->askOnCloseEvent(false);

  for (int i = 0; i < no_cols; i++) {
    const Mantid::MantidVec &dataX = workspace->readX(indexList[i]);
    const Mantid::MantidVec &dataY = workspace->readY(indexList[i]);
    const Mantid::MantidVec &dataE = workspace->readE(indexList[i]);

    const int kY = (c + 1) * i + 1;
    const int kX = (c + 1) * i;
    int kErr = 0;
    t->setColName(kY, "YS" + QString::number(indexList[i]));
    t->setColName(kX, "XS" + QString::number(indexList[i]));
    t->setColPlotDesignation(kX, Table::X);
    if (errs) {
      kErr = (c + 1) * i + 2;
      t->setColPlotDesignation(kErr, Table::yErr);
      t->setColName(kErr, "ES" + QString::number(indexList[i]));
    }
    for (int j = 0; j < numRows; j++) {
      if (isHistogram && binCentres) {
        t->setCell(j, kX, (dataX[j] + dataX[j + 1]) / 2);
      } else {
        t->setCell(j, kX, dataX[j]);
      }
      t->setCell(j, kY, dataY[j]);

      if (errs)
        t->setCell(j, kErr, dataE[j]);
    }
    if (isHistogram && (!binCentres)) {
      int iRow = numRows;
      t->addRows(1);
      if (i == 0)
        t->setCell(iRow, 0, dataX[iRow]);
      t->setCell(iRow, kY, 0);
      if (errs)
        t->setCell(iRow, kErr, 0);
    }
  }

  return t;
}

/** Creates a Qtiplot Table from selected spectra of MantidMatrix m.
The columns are: 1st column is x-values from the first selected spectrum,
2nd column is y-values of the first spectrum. Depending on value of errs
the 3rd column contains either first spectrum errors (errs == true) or
y-values of the second spectrum (errs == false). Consecutive columns have
y-values and errors (if errs is true) of the following spectra. If visible ==
true
the table is made visible in Qtiplot.

The name of a Y column is "Y"+QString\:\:number(i), where i is the row in the
MantidMatrix,
not the spectrum index in the workspace.

*/
Table *MantidUI::createTableFromSelectedRows(MantidMatrix *m, bool errs,
                                             bool binCentres) {
  const QList<int> &indexList = m->getSelectedRows();
  if (indexList.empty())
    return NULL;

  return createTableFromSpectraList(
      m->name(), QString::fromStdString(m->workspace()->getName()), indexList,
      errs, binCentres);
}

/**  Create a 1d graph from a Table.
@param t :: Pointer to the Table.
@param type :: Type of the curve. Possible values are:
- Graph::Line
- Graph::Scatter
- Graph::LineSymbols
- Graph::HorizontalSteps
*/
MultiLayer *MantidUI::createGraphFromTable(Table *t, int type) {
  if (!t)
    return NULL;
  QStringList lst = t->colNames();
  QStringList::const_iterator itr;
  for (itr = lst.begin(); itr != lst.end(); ++itr) {
    // remove the X names from the column list and pass the X removed list
    // to multilayerPlot
    QString str = (*itr);
    if (str.contains("XS", Qt::CaseInsensitive)) {
      int index = lst.indexOf(str);
      lst.removeAt(index);
    }
  }

  MultiLayer *ml = appWindow()->multilayerPlot(t, lst, GraphOptions::Line);
  Graph *g = ml->activeGraph();
  appWindow()->polishGraph(g, type);
  for (int i = 0; i < g->curves(); i++)
    g->setCurveStyle(i, type);

  return ml;
}

/** Set properties of a 1d graph which plots bin data from a workspace.
@param ml :: MultiLayer plot with the graph
@param Name :: Name of the graph
@param workspace :: The workspace
*/
void MantidUI::setUpBinGraph(
    MultiLayer *ml, const QString &Name,
    Mantid::API::MatrixWorkspace_const_sptr workspace) {
  Graph *g = ml->activeGraph();
  g->setTitle(tr("Workspace ") + Name);
  QString xtitle;
  if (workspace->axes() >
      1) // Protection against calling this on 1D/single value workspaces
  {
    xtitle = MantidQt::API::PlotAxis(*workspace, 1).title();
  }
}

/**
Plots the spectra from the given workspaces
@param ws_names :: List of ws names to plot
@param indexList :: List of indices to plot for each workspace
@param spectrumPlot :: True if indices should be interpreted as row indices
@param distr :: if true, workspace plot as y data/bin width
@param errs :: If true include the errors on the graph
@param style :: Curve style for plot
@param plotWindow :: Window to plot to. If NULL a new one will be created
@param clearWindow :: Whether to clear specified plotWindow before plotting.
Ignored if plotWindow == NULL
@param waterfallPlot :: If true create a waterfall type plot
*/
MultiLayer *MantidUI::plot1D(const QStringList &ws_names,
                             const QList<int> &indexList, bool spectrumPlot,
                             MantidQt::DistributionFlag distr, bool errs,
                             GraphOptions::CurveType style,
                             MultiLayer *plotWindow, bool clearWindow,
                             bool waterfallPlot) {
  // Convert the list into a map (with the same workspace as key in each case)
  QMultiMap<QString, int> pairs;
  QListIterator<QString> ws_itr(ws_names);
  ws_itr.toBack();
  QListIterator<int> spec_itr(indexList);
  spec_itr.toBack();

  // Need to iterate through the set in reverse order to get the curves in the
  // correct order on the plot
  while (ws_itr.hasPrevious()) {
    QString workspace_name = ws_itr.previous();
    while (spec_itr.hasPrevious()) {
      pairs.insert(workspace_name, spec_itr.previous());
    }
    // Reset spectrum index pointer
    spec_itr.toBack();
  }

  // Pass over to the overloaded method
  return plot1D(pairs, spectrumPlot, distr, errs, style, plotWindow,
                clearWindow, waterfallPlot);
}

/** Create a 1D graph from the specified list of workspaces/spectra.
@param toPlot :: Map of form ws -> [spectra_list]
@param spectrumPlot :: True if indices should be interpreted as row indices
@param distr :: if true, workspace plot as y data/bin width
@param errs :: If true include the errors on the graph
@param plotWindow :: Window to plot to. If NULL a new one will be created
@param clearWindow :: Whether to clear specified plotWindow before plotting.
Ignored if plotWindow == NULL
@param waterfallPlot :: If true create a waterfall type plot
@return NULL if failure. Otherwise, if plotWindow == NULL - created window, if
not NULL - plotWindow
*/
MultiLayer *MantidUI::plot1D(const QMultiMap<QString, set<int>> &toPlot,
                             bool spectrumPlot,
                             MantidQt::DistributionFlag distr, bool errs,
                             MultiLayer *plotWindow, bool clearWindow,
                             bool waterfallPlot) {
  // Convert the list into a map (with the same workspace as key in each case)
  QMultiMap<QString, int> pairs;
  // Need to iterate through the workspaces
  QMultiMap<QString, std::set<int>>::const_iterator it;
  for (it = toPlot.constBegin(); it != toPlot.constEnd(); ++it) {
    std::set<int>::const_reverse_iterator itSet;
    for (itSet = it->rbegin(); itSet != it->rend(); ++itSet) {
      pairs.insert(it.key(), *itSet);
    }
  }

  // Pass over to the overloaded method
  return plot1D(pairs, spectrumPlot, distr, errs, GraphOptions::Unspecified,
                plotWindow, clearWindow, waterfallPlot);
}

/** Create a 1d graph from the specified spectra in a MatrixWorkspace
@param wsName :: Workspace name
@param indexList :: List of indices to plot for each workspace
@param spectrumPlot :: True if indices should be interpreted as row indices
@param distr :: if true, workspace plot as y data/bin width
@param errs :: If true include the errors on the graph
@param plotWindow :: Window to plot to. If NULL a new one will be created
@param clearWindow :: Whether to clear specified plotWindow before plotting.
Ignored if plotWindow == NULL
@param waterfallPlot :: If true create a waterfall type plot
@return NULL if failure. Otherwise, if plotWindow == NULL - created window, if
not NULL - plotWindow
*/
MultiLayer *MantidUI::plot1D(const QString &wsName,
                             const std::set<int> &indexList, bool spectrumPlot,
                             MantidQt::DistributionFlag distr, bool errs,
                             MultiLayer *plotWindow, bool clearWindow,
                             bool waterfallPlot) {
  // Convert the list into a map (with the same workspace as key in each case)
  QMultiMap<QString, int> pairs;
  // Need to iterate through the set in reverse order
  std::set<int>::const_reverse_iterator it;
  for (it = indexList.rbegin(); it != indexList.rend(); ++it) {
    pairs.insert(wsName, *it);
  }

  // Pass over to the overloaded method
  return plot1D(pairs, spectrumPlot, distr, errs, GraphOptions::Unspecified,
                plotWindow, clearWindow, waterfallPlot);
}

/** Create a 1d graph form a set of workspace-spectrum pairs
@param toPlot :: A list of workspace/spectra to be shown in the graph
@param spectrumPlot :: True if indices should be interpreted as row indices
@param distr :: if true, workspace plot as y data/bin width
@param errs :: If true include the errors to the graph
@param style :: curve style for plot
@param plotWindow :: Window to plot to. If NULL a new one will be created
@param clearWindow :: Whether to clear specified plotWindow before plotting.
Ignored if plotWindow == NULL
@param waterfallPlot :: If true create a waterfall type plot
@return NULL if failure. Otherwise, if plotWindow == NULL - created window, if
not NULL - plotWindow
*/
MultiLayer *MantidUI::plot1D(const QMultiMap<QString, int> &toPlot,
                             bool spectrumPlot,
                             MantidQt::DistributionFlag distr, bool errs,
                             GraphOptions::CurveType style,
                             MultiLayer *plotWindow, bool clearWindow,
                             bool waterfallPlot) {
  if (toPlot.size() == 0)
    return NULL;

  if (toPlot.size() > 10) {
    QMessageBox ask(appWindow());
    QAbstractButton *confirmButton =
        ask.addButton(tr("Confirm"), QMessageBox::ActionRole);
    ask.addButton(tr("Cancel"), QMessageBox::ActionRole);
    ask.setText("You selected " + QString::number(toPlot.size()) +
                " spectra to plot. "
                "Are you sure you want to plot this many?");
    ask.setIcon(QMessageBox::Question);
    ask.exec();
    if (ask.clickedButton() != confirmButton)
      return NULL;
  }
  // Force waterfall option to false if only 1 curve
  if ((NULL == plotWindow || clearWindow) && toPlot.size() == 1)
    waterfallPlot = false;

  ScopedOverrideCursor waitCursor;

  // If the first workspace selected in the tree is a WorkspaceGroup,
  // use its name directly, rather than the first in the list 'toPlot'
  // (which will be the first workspace included in the group - not
  // the best title).
  QString plotTitle = getSelectedGroupName();
  if (plotTitle.isEmpty()) {
    plotTitle = toPlot.constBegin().key();
  }

  // Limit to 1 window for this type of plot -> reuse plot/graph window
  if (workspacesDockPlot1To1()) {
    if (m_lastShown1DPlotWin) {
      plotWindow = m_lastShown1DPlotWin;
      clearWindow = true;
    }
  }
  bool isGraphNew = false;
  MultiLayer *ml = appWindow()->prepareMultiLayer(isGraphNew, plotWindow,
                                                  plotTitle, clearWindow);
  m_lastShown1DPlotWin = ml;

  // Do we plot try to plot as distribution. If request and it is not already
  // one!
  bool plotAsDistribution(false);
  if (distr == MantidQt::DistributionDefault) {
    plotAsDistribution = appWindow()->autoDistribution1D;
  } else {
    plotAsDistribution = (distr == MantidQt::DistributionTrue);
  }

  // Try to add curves to the plot
  Graph *g = ml->activeGraph();
  MantidMatrixCurve::IndexDir indexType =
      (spectrumPlot) ? MantidMatrixCurve::Spectrum : MantidMatrixCurve::Bin;
  MantidMatrixCurve *firstCurve(NULL);
  for (QMultiMap<QString, int>::const_iterator it = toPlot.begin();
       it != toPlot.end(); ++it) {
    try {
      auto *wsCurve = new MantidMatrixCurve(it.key(), g, it.value(), indexType,
                                            errs, plotAsDistribution, style);
      if (!firstCurve) {
        firstCurve = wsCurve;
        g->setNormalizable(firstCurve->isNormalizable());
        g->setDistribution(firstCurve->isDistribution());
      }
    } catch (Mantid::Kernel::Exception::NotFoundError &) {
      g_log.warning() << "Workspace " << it.key().toStdString()
                      << " not found\n";
    } catch (std::exception &ex) {
      g_log.warning() << ex.what() << '\n';
    }
  }

  if (!isGraphNew) {
    // Replot graph is we've added curves to existing one
    g->replot();
  } else {
    if (!firstCurve) {
      return NULL;
    }

    // Ensure plot encompasses all data points
    setInitialAutoscale(g);

    // This deals with the case where the X-values are not in order. In general,
    // this shouldn't
    // happen, but it does apparently with some muon analyses.
    g->checkValuesInAxisRange(firstCurve);
  }
  ml->toggleWaterfall(waterfallPlot);

  // Check if window does not contain any curves and should be closed
  ml->maybeNeedToClose();

  return ml;
}

/**
* Draw a color fill plot for each of the listed workspaces. Unfortunately the
* plotting is
* initimately linked to MantidMatrix so that one of these needs to be created
* first
* @param ui :: the sequential fitting UI form
* @param fitbrowser :: pointer to the fit property browser
*/
void MantidUI::showSequentialPlot(
    Ui::SequentialFitDialog *ui,
    MantidQt::MantidWidgets::FitPropertyBrowser *fitbrowser) {
  std::string wsName = fitbrowser->outputName();
  Mantid::API::ITableWorkspace_sptr ws =
      boost::dynamic_pointer_cast<Mantid::API::ITableWorkspace>(
          Mantid::API::AnalysisDataService::Instance().retrieve(wsName));
  if (ws) {
    if ((ws->columnCount() - 1) / 2 !=
        fitbrowser->compositeFunction()->nParams())
      return;
    Table *t = importTableWorkspace(QString::fromStdString(wsName));
    if (!t)
      return;
    QString parName;
    if (fitbrowser->compositeFunction()->nFunctions() == 1) {
      size_t i = fitbrowser->compositeFunction()->parameterIndex(
          ui->cbParameter->currentText().toStdString());
      parName = QString::fromStdString(
          fitbrowser->compositeFunction()->getFunction(0)->parameterName(i));
    } else {
      parName = ui->cbParameter->currentText();
    }
    QStringList colNames;
    colNames << t->name() + "_" + parName << t->name() + "_" + parName + "_Err";
    MultiLayer *ml = appWindow()->multilayerPlot(
        t, colNames, ui->cbCurveType->currentIndex());
    // set plot titles
    Graph *g = ml->activeGraph();
    if (g) {
      if (ui->ckbLogPlot->isChecked()) {
        g->setXAxisTitle(ui->cbLogValue->currentText());
      } else {
        g->setXAxisTitle("Spectra");
      }
      g->setYAxisTitle(parName);
      g->setTitle("");
    }
  }
}

/**
* Draw a color fill plot for each of the listed workspaces. Unfortunately the
* plotting is
* initimately linked to MantidMatrix so that one of these needs to be created
* first
* @param wsNames :: For each workspace listed create a 2D colorfill plot
* @param curveType :: The curve type for each of the plots
*/
void MantidUI::drawColorFillPlots(const QStringList &wsNames,
                                  GraphOptions::CurveType curveType) {
  int nPlots = wsNames.size();
  if (nPlots > 1) {
    QList<MultiLayer *> plots;
    for (QStringList::const_iterator cit = wsNames.begin();
         cit != wsNames.end(); ++cit) {
      const bool hidden = true;
      MultiLayer *plot =
          this->drawSingleColorFillPlot(*cit, curveType, NULL, hidden);
      if (plot)
        plots.append(plot);
    }

    if (!plots.isEmpty()) {
      nPlots = plots.size();

      int nCols = 1;
      if (nPlots >= 16)
        nCols = 4;
      else if (nPlots >= 9)
        nCols = 3;
      else if (nPlots >= 4)
        nCols = 2;
      else
        nCols = nPlots;

      int nRows = nPlots / nCols;
      if (nPlots % nCols != 0)
        ++nRows;

      auto tiledWindow = new TiledWindow(
          appWindow(), "", appWindow()->generateUniqueName("TiledWindow"),
          nRows, nCols);

      int row = 0;
      int col = 0;
      for (auto it = plots.begin(); it != plots.end(); ++it) {
        tiledWindow->addWidget(*it, row, col);
        ++col;
        if (col == nCols) {
          col = 0;
          ++row;
        }
      }

      appWindow()->addMdiSubWindow(tiledWindow);
    }
  } else if (nPlots == 1) {
    this->drawSingleColorFillPlot(wsNames.front(), curveType);
  }
}

/**
* Draw a single ColorFill plot for the named workspace
* @param wsName :: The name of the workspace which provides data for the plot
* @param curveType :: The type of curve
* @param window :: An optional pointer to a plot window. If not NULL the window
* is cleared
*                      and reused
* @param hidden
* @returns A pointer to the created plot
*/
MultiLayer *MantidUI::drawSingleColorFillPlot(const QString &wsName,
                                              GraphOptions::CurveType curveType,
                                              MultiLayer *window, bool hidden) {
  auto workspace =
      boost::dynamic_pointer_cast<const Mantid::API::MatrixWorkspace>(
          getWorkspace(wsName));
  if (!workspace)
    return NULL;

  ScopedOverrideCursor waitCursor;

  bool reusePlots = workspacesDockPlot1To1();
  if ((!reusePlots && NULL == window) ||
      (reusePlots && !m_lastShownColorFillWin)) // needs to create a new window
  {
    try {
      window = appWindow()->multilayerPlot(
          appWindow()->generateUniqueName(wsName + "-"));
      if (hidden) {
        window->hide();
      }
    } catch (std::runtime_error &e) {
      m_lastShownColorFillWin = NULL;
      g_log.error() << "Could not create color fill plot: " << e.what() << "\n";
      throw std::runtime_error(e);
    }
    window->setCloseOnEmpty(true);
    m_lastShownColorFillWin = window;
  } else {
    if (NULL == window) {
      if (NULL == m_lastShownColorFillWin)
        return NULL;
      window = m_lastShownColorFillWin;
    }
    // start fresh layer
    window->setName(appWindow()->generateUniqueName(wsName + "-"));
    window->setLayersNumber(0);
    window->addLayer();
  }

  Graph *plot = window->activeGraph();
  appWindow()->setPreferences(plot);

  plot->setTitle(wsName);

  Spectrogram *spgrm = new Spectrogram(wsName, workspace);
  plot->plotSpectrogram(spgrm, curveType);
  connect(spgrm, SIGNAL(removeMe(Spectrogram *)), plot,
          SLOT(removeSpectrogram(Spectrogram *)));
  connect(plot, SIGNAL(curveRemoved()), window, SLOT(maybeNeedToClose()),
          Qt::QueuedConnection);

  appWindow()->setSpectrogramTickStyle(plot);
  setInitialAutoscale(plot);

  return window;
}

/** Create a 1d graph form specified spectra in a MatrixWorkspace
@param wsName :: Workspace name
@param i0 :: Starting index
@param i1 :: Last index
@param errs :: If true include the errors to the graph
@param distr :: if true, workspace is a distribution
*/
MultiLayer *MantidUI::plotSpectraRange(const QString &wsName, int i0, int i1,
                                       MantidQt::DistributionFlag distr,
                                       bool errs) {
  if (i0 < 0 || i1 < 0)
    return 0;
  /** For instrument with one to many spectra-detector mapping,
  * different pixels with correspond to the same specta so
  * we need to remove doublons in this case.
  */
  std::set<int> indexList;
  for (int i = i0; i <= i1; i++)
    indexList.insert(i);

  return plot1D(wsName, indexList, true, distr, errs);
}

/**  Create a graph and plot the selected rows of a MantidMatrix
@param m :: Mantid matrix
@param distr :: if true, workspace is a distribution
@param errs :: True if the errors to be plotted
*/
MultiLayer *MantidUI::plotSelectedRows(const MantidMatrix *const m,
                                       MantidQt::DistributionFlag distr,
                                       bool errs) {
  const QList<int> &rows = m->getSelectedRows();
  std::set<int> rowSet(rows.constBegin(), rows.constEnd());

  return plot1D(m->workspaceName(), rowSet, true, distr, errs);
}

/**
 * Create a graph and plot the selected columns of a MantidMatrix
 * @param m MantidMatrix
 * @param errs True if errors are required
 * @return
 */
MultiLayer *MantidUI::plotSelectedColumns(const MantidMatrix *const m,
                                          bool errs) {
  const QList<int> &cols = m->getSelectedColumns();
  std::set<int> colSet(cols.constBegin(), cols.constEnd());

  return plot1D(m->workspaceName(), colSet, false, MantidQt::DistributionFalse,
                errs);
}

/**
 * Plot a "tiled" plot (with subplots).
 * Ask user for confirmation if lots of plots are chosen.
 * If just one workspace, put each spectrum in its own subplot
 * If multiple workspaces, each ws gets its own subplot
 * @param toPlot :: Map of form ws -> [spectra_list]
 * @param distr :: if true, workspace plot as distribution (y data/bin width)
 * @param errs :: if true, plot the errors on the graph
 * @param plotWindow :: Window to plot in - if null, create a new one
 * @return created MultiLayer, or null on failure
 */
MultiLayer *MantidUI::plotSubplots(const QMultiMap<QString, set<int>> &toPlot,
                                   MantidQt::DistributionFlag distr, bool errs,
                                   MultiLayer *plotWindow) {
  // Check if nothing to plot
  if (toPlot.size() == 0)
    return nullptr;

  // If one workspace, each spectrum goes in its own subplot.
  // Otherwise, there is one subplot per workspace.
  const int nWorkspaces = toPlot.size();
  const int nSubplots = [&toPlot, &nWorkspaces]() {
    if (nWorkspaces == 1) {
      return static_cast<int>(toPlot.begin()->size()); // number of spectra
    } else {
      return nWorkspaces;
    }
  }();

  // If user has selected a large number, check if they want to plot that many
  if (nSubplots > REASONABLE_NUM_SUBPLOTS) {
    const auto &answer = QMessageBox::question(
        appWindow(), "MantidPlot",
        "Are you sure you want to plot " + QString::number(nSubplots) +
            " subplots?",
        QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok);
    if (answer != QMessageBox::Ok) {
      return nullptr;
    }
  }

  // Get title. If this is a wsGroup, use name of that
  QString plotTitle = getSelectedGroupName();
  if (plotTitle.isEmpty()) {
    plotTitle = toPlot.constBegin().key();
  }

  // Do we plot as distribution?
  const bool plotAsDistribution = distr == MantidQt::DistributionDefault
                                      ? appWindow()->autoDistribution1D
                                      : distr == MantidQt::DistributionTrue;

  // Set the wait cursor while we are plotting
  ScopedOverrideCursor waitCursor;

  // Create window with correct number of layers, or use existing
  MultiLayer *multi;
  if (plotWindow) {
    multi = plotWindow;
    multi->setLayersNumber(0); // remove any existing plots
    multi->setLayersNumber(nSubplots);
  } else {
    multi = appWindow()->multilayerPlot(plotTitle, nSubplots, 1, nSubplots);
  }
  assert(multi);
  multi->setCloseOnEmpty(true);
  multi->arrangeLayers(true, true);

  QStringList legends; // Legends for each plot
  legends.reserve(nSubplots);
  int row(0), col(0);
  if (nWorkspaces == 1) {
    // One workspace, each spectrum in its own subplot
    const auto &wsName = toPlot.begin().key();
    const auto &spectra = toPlot.begin().value();
    for (const auto &spec : spectra) {
      const std::set<int> spectraSet{spec};
      plotLayerOfMultilayer(multi, errs, plotAsDistribution, row, col, wsName,
                            spectraSet);
      legends.append(getLegendKeys(wsName, spectraSet));
    }
  } else {
    // Each workspace in its own subplot
    for (auto iter = toPlot.constBegin(); iter != toPlot.constEnd(); ++iter) {
      const auto &wsName = iter.key();
      const auto &spectra = iter.value();
      plotLayerOfMultilayer(multi, errs, plotAsDistribution, row, col, wsName,
                            spectra);
      legends.append(getLegendKeys(wsName, spectra));
    }
  }

  multi->setCommonAxisScales();
  multi->arrangeLayers(true, true);

  // add legends last of all, so they are in the correct place
  for (int index = 0; index < multi->layers(); ++index) {
    auto *layer = multi->layer(index + 1); // MultiLayer has 1-based indices
    layer->newLegend(legends[index]);
  }

  // Check if window does not contain any curves and should be closed
  multi->maybeNeedToClose();

  return multi;
}

/**
 * Plot a single layer of a multilayer plot.
 * Data comes from the specified spectra of the named workspace.
 *
 * @param multi :: [input, output] Multilayer to plot graph onto
 * @param plotErrors :: [input] Whether to plot errors
 * @param plotDist :: [input] Whether to plot as distribution
 * @param row :: [input, output] Row counter - will be incremented
 * @param col :: [input, output] Column counter - will be incremented
 * @param wsName :: [input] Workspace name to plot from
 * @param spectra :: [input] Spectra from workspace to plot in the layer
 */
void MantidUI::plotLayerOfMultilayer(MultiLayer *multi, const bool plotErrors,
                                     const bool plotDist, int &row, int &col,
                                     const QString &wsName,
                                     const std::set<int> &spectra) {
  const int nRows = multi->getRows();
  const int nCols = multi->getCols();
  const int nPlots = multi->layers();

  // Lambda to increment row, column counters
  const auto incrementCounters = [&nRows, &nCols](int &row, int &col) {
    if (col < nCols - 1) {
      ++col;
    } else if (row < nRows - 1) {
      col = 0;
      ++row;
    }
  };

  // Lambda to set axis label hiding
  const auto formatAxes =
      [&nRows, &nCols, &nPlots](Graph *layer, const int row, const int col) {
        const bool drawYAxisLabel = col == 0;
        if (!drawXAxisLabel(row, col, nRows, nCols, nPlots)) {
          layer->setXAxisTitle(QString::null);
        }
        if (!drawYAxisLabel) {
          layer->setYAxisTitle(QString::null);
        }
      };

  const bool isFitResult = workspaceIsFitResult(wsName);

  const int layerIndex = row * nCols + col + 1; // layers numbered from 1
  auto *layer = multi->layer(layerIndex);
  for (const int spec : spectra) {
    const auto plotType = isFitResult ? getCurveTypeForFitResult(spec)
                                      : GraphOptions::Unspecified;
    layer->insertCurve(wsName, spec, plotErrors, plotType, plotDist);
  }
  m_appWindow->setPreferences(layer); // apply default style
  layer->removeTitle();
  setInitialAutoscale(layer);
  formatAxes(layer, row, col);
  incrementCounters(row, col);
}

/**
 * Plot a "tiled" plot (with subplots).
 * Ask user for confirmation if lots of plots are chosen.
 * If just one workspace, put each spectrum in its own subplot
 * If multiple workspaces, each ws gets its own subplot
 * @param toPlot :: A list of workspace/spectra to be shown in the graph
 * @param distr :: if true, workspace plot as distribution (y data/bin width)
 * @param errs :: if true, plot the errors on the graph
 * @param plotWindow :: Window to plot in - if null, create a new one
 * @return created MultiLayer, or null on failure
 */
MultiLayer *MantidUI::plotSubplots(const QMultiMap<QString, int> &toPlot,
                                   MantidQt::DistributionFlag distr, bool errs,
                                   MultiLayer *plotWindow) {

  // Convert the input map into a map of workspace->spectra
  QMultiMap<QString, std::set<int>> spectraByWorkspace;
  for (auto it = toPlot.constBegin(); it != toPlot.constEnd(); ++it) {
    if (spectraByWorkspace.contains(it.key())) { // append to existing entry
      auto entry = spectraByWorkspace.find(it.key());
      entry.value().insert(it.value());
    } else { // add a new entry
      spectraByWorkspace.insert(it.key(), std::set<int>{it.value()});
    }
  }

  // Pass over to the overloaded method
  return plotSubplots(spectraByWorkspace, distr, errs, plotWindow);
}

/**
 * Plot a "tiled" plot (with subplots).
 * Ask user for confirmation if lots of plots are chosen.
 * If just one workspace, put each spectrum in its own subplot
 * If multiple workspaces, each ws gets its own subplot
 *
 * This overload plots the same spectra for each workspace.
 *
 * @param wsNames :: A list of workspace names to be shown in the graph
 * @param indexList :: list of workspace indices
 * @param distr :: if true, workspace plot as distribution (y data/bin width)
 * @param errs :: if true, plot the errors on the graph
 * @param plotWindow :: Window to plot in - if null, create a new one
 * @return created MultiLayer, or null on failure
 */
MultiLayer *MantidUI::plotSubplots(const QStringList &wsNames,
                                   const QList<int> &indexList,
                                   MantidQt::DistributionFlag distr, bool errs,
                                   MultiLayer *plotWindow) {
  // convert input into map of workspace->spectra
  QMultiMap<QString, std::set<int>> spectraByWorkspace;
  const std::set<int> wsIndices = [&indexList]() {
    std::set<int> indexSet;
    for (const auto &index : indexList) {
      indexSet.insert(index);
    }
    return indexSet;
  }();
  for (const auto &wsName : wsNames) {
    spectraByWorkspace.insert(wsName, wsIndices);
  }

  // Pass to the overloaded method
  return plotSubplots(spectraByWorkspace, distr, errs, plotWindow);
}

Table *MantidUI::createTableFromBins(
    const QString &wsName, Mantid::API::MatrixWorkspace_const_sptr workspace,
    const QList<int> &bins, bool errs, int fromRow, int toRow) {
  if (bins.empty())
    return NULL;

  int c = errs ? 2 : 1;
  int numRows = static_cast<int>(workspace->getNumberHistograms());

  int j0 = fromRow >= 0 ? fromRow : 0;
  int j1 = toRow >= 0 ? toRow : numRows - 1;

  if (j0 >= numRows || j1 >= numRows)
    return NULL;

  Table *t = new Table(appWindow()->scriptingEnv(), numRows,
                       c * bins.size() + 1, "", appWindow(), 0);
  appWindow()->initTable(t, appWindow()->generateUniqueName(wsName + "-"));

  for (int i = 0; i < bins.size(); i++) {
    const int kY = c * i + 1;
    int kErr = 0;
    t->setColName(kY, "YB" + QString::number(bins[i]));
    if (errs) {
      kErr = 2 * i + 2;
      t->setColPlotDesignation(kErr, Table::yErr);
      t->setColName(kErr, "EB" + QString::number(bins[i]));
    }
    for (int j = j0; j <= j1; j++) {
      const Mantid::MantidVec &dataY = workspace->readY(j);
      const Mantid::MantidVec &dataE = workspace->readE(j);

      if (i == 0) {
        // Get the X axis values from the vertical axis of the workspace
        if (workspace->axes() > 1)
          t->setCell(j, 0, (*workspace->getAxis(1))(j));
        else
          t->setCell(j, 0, j);
      }
      t->setCell(j, kY, dataY[bins[i]]);
      if (errs)
        t->setCell(j, kErr, dataE[bins[i]]);
    }
  }
  return t;
}

Table *MantidUI::createTableFromSelectedColumns(MantidMatrix *m, bool errs) {
  const QList<int> &cols = m->getSelectedColumns();
  if (cols.empty())
    return 0;

  int j0 = m->workspaceIndex(0);
  int j1 = m->workspaceIndex(m->numRows() - 1);

  return createTableFromBins(m->name(), m->workspace(), cols, errs, j0, j1);
}

/** Saves data to  nexus file
* @param wsName :: Name of the workspace to be saved
* @param fileName :: name of the nexus file to created
*/
void MantidUI::savedatainNexusFormat(const std::string &fileName,
                                     const std::string &wsName) {
  auto inputWorkspace =
      AnalysisDataService::Instance().retrieveWS<Workspace>(wsName);

  // Typically, we use SaveNexusProcessed to save a workspace
  QString algorithm = "SaveNexusProcessed";

  //...but if it's an MD workspace, we use SaveMD instead
  if (boost::dynamic_pointer_cast<const IMDEventWorkspace>(inputWorkspace) ||
      boost::dynamic_pointer_cast<const IMDHistoWorkspace>(inputWorkspace))
    algorithm = "SaveMD";

  try {
    Mantid::API::IAlgorithm_sptr alg = createAlgorithm(algorithm);
    alg->setPropertyValue("Filename", fileName);
    alg->setPropertyValue("InputWorkspace", wsName);
    executeAlgorithmAsync(alg, true /* wait for completion */);
  } catch (...) {
  }
}
/** Loads data from nexus file
* @param wsName :: Name of the workspace to be created
* @param fileName :: name of the nexus file
*/
void MantidUI::loadWSFromFile(const std::string &wsName,
                              const std::string &fileName) {
  if (fileName.empty())
    return;
  try {
    Mantid::API::IAlgorithm_sptr alg = createAlgorithm("Load");
    alg->setPropertyValue("Filename", fileName);
    alg->setPropertyValue("OutputWorkspace", wsName);
    executeAlgorithmAsync(alg, true /* wait for completion */);
  } catch (...) {
  }
}

bool MantidUI::workspacesDockPlot1To1() {
  return "On" ==
         Mantid::Kernel::ConfigService::Instance().getString(
             "MantidOptions.ReusePlotInstances");
}

/**
 * If a workspace group is selected, return its name
 * @returns :: Name of selected workspace group, or empty if no group selected
 */
QString MantidUI::getSelectedGroupName() const {
  auto selNames = m_exploreMantid->getSelectedWorkspaceNames();
  QString sel;

  if (selNames.size() > 0)
    sel = QString::fromStdString(selNames[0]);

  WorkspaceGroup_const_sptr gWs;
  if (!sel.isEmpty() &&
      AnalysisDataService::Instance().doesExist(sel.toStdString())) {
    try {
      gWs = boost::dynamic_pointer_cast<const WorkspaceGroup>(
          Mantid::API::AnalysisDataService::Instance().retrieve(
              sel.toStdString()));
    } catch (std::exception &) {
      // can happen, nothing to worry about
      gWs = WorkspaceGroup_const_sptr(); // make sure, anyway
    }
  }
  if (gWs) {
    return sel;
  } else {
    return "";
  }
}

/**
 * Sets graph to encompass all data points, then resets user's plot preference
 *
 * The 'setAutoScale' method is used to make sure that the plot initially
 * encompasses all the data points. However, this has the side-effect suggested
 * by its name: all the axes become auto-scaling if the data changes. If, in the
 * plot preferences, autoscaling has been disabled, then the axes are re-fixed.
 *
 * @param graph :: [input, output] Graph to set initial autoscale for
 */
void MantidUI::setInitialAutoscale(Graph *graph) {
  // Set axes to include all data points
  graph->setAutoScale();
  // If option disabled, re-fix the axes
  if (!appWindow()->autoscale2DPlots)
    graph->enableAutoscaling(false);
}

//=========================================================================
//
// This section defines some stuff that is only used on Windows
//
//=========================================================================
#ifdef _WIN32

struct mem_block {
  SIZE_T size;
  int state;
};

///  Assess the virtual memeory of the current process.
void countVirtual(vector<mem_block> &mem, int &total) {

  MEMORYSTATUSEX memStatus;
  memStatus.dwLength = sizeof(MEMORYSTATUSEX);
  GlobalMemoryStatusEx(&memStatus);

  MEMORY_BASIC_INFORMATION info;

  char *addr = 0;
  size_t free = 0;      // total free space
  size_t reserved = 0;  // total reserved space
  size_t committed = 0; // total commited (used) space
  size_t size = 0;
  size_t free_max = 0;      // maximum contiguous block of free memory
  size_t reserved_max = 0;  // maximum contiguous block of reserved memory
  size_t committed_max = 0; // maximum contiguous block of committed memory

  size_t GB2 =
      memStatus.ullTotalVirtual; // Maximum memeory available to the process
  total = static_cast<int>(GB2);

  // Loop over all virtual memory to find out the status of every block.
  do {
    VirtualQuery(addr, &info, sizeof(MEMORY_BASIC_INFORMATION));

    int state = 0;
    if (info.State == MEM_FREE) {
      free += info.RegionSize;
      if (info.RegionSize > free_max)
        free_max = info.RegionSize;
      state = 0;
    }
    if (info.State == MEM_RESERVE) {
      reserved += info.RegionSize;
      if (info.RegionSize > reserved_max)
        reserved_max = info.RegionSize;
      state = 500;
    }
    if (info.State == MEM_COMMIT) {
      committed += info.RegionSize;
      if (info.RegionSize > committed_max)
        committed_max = info.RegionSize;
      state = 1000;
    }

    addr += info.RegionSize;
    size += info.RegionSize;

    mem_block b = {info.RegionSize, state};
    mem.push_back(b);

    /*cerr<<"BaseAddress = "<< info.BaseAddress<<'\n';
    cerr<<"AllocationBase = "<< info.AllocationBase<<'\n';
    cerr<<"AllocationProtect = "<< info.AllocationProtect<<'\n';
    cerr<<"RegionSize = "<< hex << info.RegionSize<<'\n';
    cerr<<"State = "<< state_str(info.State)<<'\n';
    cerr<<"Protect = "<< hex << info.Protect <<' '<<
    protect_str(info.Protect)<<'\n';
    cerr<<"Type = "<< hex << info.Type<<'\n';*/

  } while (size < GB2);

  std::cerr << "count FREE = " << std::dec << double(free) / 1024 / 1024
            << '\n';
  std::cerr << "count RESERVED = " << double(reserved) / 1024 / 1024 << '\n';
  std::cerr << "count COMMITTED = " << double(committed) / 1024 / 1024 << '\n';

  std::cerr << "max FREE = " << std::dec << double(free_max) / 1024 / 1024
            << '\n';
  std::cerr << "max RESERVED = " << double(reserved_max) / 1024 / 1024 << '\n';
  std::cerr << "max COMMITTED = " << double(committed_max) / 1024 / 1024
            << '\n';
  std::cerr << '\n';
}

/// Shows 2D plot of current memory usage.
/// One point is 1K of memory. One row is 1M.
/// Red - used memory block, blue - free, green - reserved.
void MantidUI::memoryImage() {
  // ofstream ofil("memory.txt");
  vector<mem_block> mem;
  int total;
  countVirtual(mem, total);
  int colNum = 1024;
  int rowNum = total / 1024 / colNum;
  Matrix *m = appWindow()->newMatrix(rowNum, colNum);
  m->setCoordinates(0, colNum, 0, rowNum);
  int row = 0;
  int col = 0;
  QImage image(colNum, rowNum, QImage::Format_Mono);
  for (vector<mem_block>::iterator b = mem.begin(); b != mem.end(); ++b) {
    int n = b->size / 1024;
    for (int i = 0; i < n; i++) {
      m->setCell(row, col, b->state);
      // ofil<<b->state<<'\t';
      col++;
      if (col >= colNum) {
        col = 0;
        row++;
        // ofil<<'\n';
      }
    }
  }
  appWindow()->plotSpectrogram(m, GraphOptions::ColorMap);
}

void MantidUI::memoryImage2() {
  // ofstream ofil("memory.txt");
  vector<mem_block> mem;
  int total;
  countVirtual(mem, total);
  int colNum = 1024;
  int rowNum = total / 1024 / colNum;
  int row = 0;
  int col = 0;
  QImage image(colNum, rowNum, QImage::Format_Mono);
  for (vector<mem_block>::iterator b = mem.begin(); b != mem.end(); ++b) {
    int n = b->size / 1024;
    for (int i = 0; i < n; i++) {
      if (row < rowNum && col < colNum) {
        image.setPixel(col, row, b->state > 600);
      }
      // ofil<<b->state<<'\t';
      col++;
      if (col >= colNum) {
        col = 0;
        row++;
        // ofil<<'\n';
      }
    }
  }
  image.save("memory_image.jpg");
}

#endif
//=======================================================================
// End of Windows specfic stuff
//=======================================================================

#include "MantidGeometry/Instrument.h"
#include "MantidGeometry/Instrument/CompAssembly.h"

void MantidUI::test() {
  std::cerr << "\nTest\n\n";

  MatrixWorkspace_const_sptr ws =
      boost::dynamic_pointer_cast<const MatrixWorkspace>(
          getSelectedWorkspace());
  if (ws) {
    boost::shared_ptr<const Mantid::Geometry::Instrument> instr =
        ws->getInstrument()->baseInstrument();
    boost::shared_ptr<Mantid::Geometry::CompAssembly> both =
        boost::dynamic_pointer_cast<Mantid::Geometry::CompAssembly>(
            (*instr)[3]);
    if (both) {
      boost::shared_ptr<Mantid::Geometry::CompAssembly> first =
          boost::dynamic_pointer_cast<Mantid::Geometry::CompAssembly>(
              (*both)[0]);
      if (first) {
        static int i = 0;
        Mantid::Kernel::V3D u =
            i++ ? Mantid::Kernel::V3D(1, 0, 0) : Mantid::Kernel::V3D(0, 1, 0);
        Mantid::Kernel::Quat q(30, u);
        first->rotate(q);
        return;
      }
    }
  }
  std::cerr << "Failed...\n";
}

void MantidUI::updateRecentFilesList(const QString &fname) {
  m_appWindow->updateRecentFilesList(fname);
}

MantidSurfacePlotDialog *
MantidUI::createSurfacePlotDialog(int flags, QStringList wsNames,
                                  const QString &plotType) {
  QList<QString> names;

  for (auto &name : wsNames)
    names.append(name);

  return new MantidSurfacePlotDialog(this, static_cast<Qt::WFlags>(flags),
                                     names, plotType);
}

/**
 * Create a new MantidWSIndexDialog
 * @param flags :: [input] Qt::WindowFlags enum as an integer
 * @param wsNames :: [input] Names of workspaces
 * @param showWaterfall :: [input] Whether to show "plot as waterfall" option
 * @param showPlotAll :: [input] Whether to show "plot all" button
 * @param showTiledOpt :: [input] Whether to show "tiled plot" option
 * @returns :: New dialog
 */
MantidWSIndexDialog *MantidUI::createWorkspaceIndexDialog(int flags,
                                                          QStringList wsNames,
                                                          bool showWaterfall,
                                                          bool showPlotAll,
                                                          bool showTiledOpt) {
  return new MantidWSIndexDialog(m_appWindow, static_cast<Qt::WFlags>(flags),
                                 wsNames, showWaterfall, showPlotAll,
                                 showTiledOpt);
}

void MantidUI::showSurfacePlot() {
  // find the workspace group clicked on
  auto wksp = m_exploreMantid->getSelectedWorkspace();

  if (wksp) {
    const auto wsGroup =
        boost::dynamic_pointer_cast<const WorkspaceGroup>(wksp);
    if (wsGroup) {
      auto options = m_exploreMantid->chooseSurfacePlotOptions(
          wsGroup->getNumberOfEntries());

      // TODO: Figure out how to get rid of MantidUI dependency here.
      auto plotter =
          Mantid::Kernel::make_unique<MantidGroupPlotGenerator>(this);
      plotter->plotSurface(wsGroup, options);
    }
  }
}

void MantidUI::showContourPlot() {
  auto wksp = m_exploreMantid->getSelectedWorkspace();

  if (wksp) {
    const auto wsGroup =
        boost::dynamic_pointer_cast<const WorkspaceGroup>(wksp);
    if (wsGroup) {
      auto options = m_exploreMantid->chooseContourPlotOptions(
          wsGroup->getNumberOfEntries());

      // TODO: Figure out how to remove the MantidUI dependency
      auto plotter =
          Mantid::Kernel::make_unique<MantidGroupPlotGenerator>(this);
      plotter->plotContour(wsGroup, options);
    }
  }
}

QWidget *MantidUI::getParent() { return m_appWindow; }

#ifdef MAKE_VATES
bool MantidUI::doesVatesSupportOpenGL() {
  return vtkPVDisplayInformation::SupportsOpenGLLocally();
}
#endif