Skip to content
Snippets Groups Projects
WorkspaceTreeWidget.cpp 59.6 KiB
Newer Older
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
//     NScD Oak Ridge National Laboratory, European Spallation Source
//     & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
#include "MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidget.h"
#include "MantidGeometry/Instrument.h"
#include "MantidQtWidgets/Common/AlgorithmDialog.h"
#include "MantidQtWidgets/Common/AlgorithmInputHistory.h"
#include "MantidQtWidgets/Common/FlowLayout.h"
#include "MantidQtWidgets/Common/InterfaceManager.h"
#include "MantidQtWidgets/Common/LineEditWithClear.h"
#include "MantidQtWidgets/Common/MantidDisplayBase.h"
#include "MantidQtWidgets/Common/MantidTreeWidget.h"
#include "MantidQtWidgets/Common/MantidTreeWidgetItem.h"
#include "MantidQtWidgets/Common/WorkspaceIcons.h"
#include "MantidQtWidgets/Common/WorkspacePresenter/ADSAdapter.h"
#include "MantidQtWidgets/Common/WorkspacePresenter/WorkspacePresenter.h"
#include "MantidQtWidgets/Common/pixmaps.h"
#include "MantidAPI/Axis.h"
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/IMDEventWorkspace.h"
#include "MantidAPI/IMDWorkspace.h"
#include "MantidAPI/IPeaksWorkspace.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/WorkspaceGroup.h"

#include <Poco/Path.h>
#include <QKeyEvent>
#include <QMainWindow>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
using namespace MantidQt::API;
using namespace Mantid::API;
using namespace Mantid::Kernel;
namespace {
/// static logger for dock widget
Mantid::Kernel::Logger docklog("MantidDockWidget");

WorkspaceIcons WORKSPACE_ICONS = WorkspaceIcons();
} // namespace

namespace MantidQt {
namespace MantidWidgets {
WorkspaceTreeWidget::WorkspaceTreeWidget(MantidDisplayBase *mdb, bool viewOnly,
Elliot Oram's avatar
Elliot Oram committed
                                         QWidget *parent)
    : QWidget(parent), m_mantidDisplayModel(mdb), m_viewOnly(viewOnly),
      m_updateCount(0), m_treeUpdating(false), m_promptDelete(false),
Moore's avatar
Moore committed
      m_saveFileType(SaveFileType::Nexus), m_sortCriteria(SortCriteria::ByName),
      m_sortDirection(SortDirection::Ascending), m_mutex(QMutex::Recursive) {
  setObjectName(
      "exploreMantid"); // this is needed for QMainWindow::restoreState()
  m_saveMenu = new QMenu(this);

  setupWidgetLayout();
  setupLoadButtonMenu();

  // Dialog box used for user to specify folder to save multiple workspaces into
  m_saveFolderDialog = new QFileDialog;
  m_saveFolderDialog->setFileMode(QFileDialog::DirectoryOnly);
  m_saveFolderDialog->setOption(QFileDialog::ShowDirsOnly);

  // 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<TopLevelItems>();
  }

  // SET UP SORT
  createSortMenuActions();
  createWorkspaceMenuActions();

  setupConnections();

  m_tree->setDragEnabled(true);

Elliot Oram's avatar
Elliot Oram committed
  auto presenter = boost::make_shared<WorkspacePresenter>(this);
Elliot Oram's avatar
Elliot Oram committed
  m_presenter = boost::dynamic_pointer_cast<ViewNotifiable>(presenter);
  presenter->init();
  if (m_viewOnly)
    hideButtonToolbar();
WorkspaceTreeWidget::~WorkspaceTreeWidget() {}

 * Accept a drag drop event and process the data appropriately
 * @param de :: The drag drop event
 */
void WorkspaceTreeWidget::dropEvent(QDropEvent *de) { m_tree->dropEvent(de); }
void WorkspaceTreeWidget::setupWidgetLayout() {
  m_tree = new MantidTreeWidget(m_mantidDisplayModel, this);
  m_tree->setHeaderLabel("Workspaces");

  FlowLayout *buttonLayout = new FlowLayout();
  m_loadButton = new QPushButton("Load");
  m_saveButton = new QPushButton("Save");
  m_deleteButton = new QPushButton("Delete");
  m_groupButton = new QPushButton("Group");
  m_sortButton = new QPushButton("Sort");

  if (m_groupButton)
    m_groupButton->setEnabled(false);
  m_deleteButton->setEnabled(false);
  m_saveButton->setEnabled(false);

  buttonLayout->addWidget(m_loadButton);
  buttonLayout->addWidget(m_deleteButton);
  buttonLayout->addWidget(m_groupButton);
  buttonLayout->addWidget(m_sortButton);
  buttonLayout->addWidget(m_saveButton);

  m_workspaceFilter = new MantidQt::MantidWidgets::LineEditWithClear();
  m_workspaceFilter->setPlaceholderText("Filter Workspaces");
  m_workspaceFilter->setToolTip("Type here to filter the workspaces");

  QVBoxLayout *layout = new QVBoxLayout();
  layout->setSpacing(0);
  layout->setMargin(0);
  layout->addLayout(buttonLayout);
  layout->addWidget(m_workspaceFilter);
  layout->addWidget(m_tree);
  this->setLayout(layout);
void WorkspaceTreeWidget::setupLoadButtonMenu() {
  m_loadMenu = new QMenu(this);

  QAction *loadFileAction = new QAction("File", this);
  QAction *liveDataAction = new QAction("Live Data", this);
  connect(loadFileAction, SIGNAL(triggered()), this, SLOT(onClickLoad()));
  connect(liveDataAction, SIGNAL(triggered()), this, SLOT(onClickLiveData()));

  m_loadMenu->addAction(loadFileAction);
  m_loadMenu->addAction(liveDataAction);
  m_loadButton->setMenu(m_loadMenu);
}

void WorkspaceTreeWidget::setupConnections() {
  connect(m_workspaceFilter, SIGNAL(textChanged(const QString &)), this,
          SLOT(filterWorkspaceTree(const QString &)));
  connect(m_deleteButton, SIGNAL(clicked()), this,
          SLOT(onClickDeleteWorkspaces()));
  connect(m_tree, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this,
          SLOT(clickedWorkspace(QTreeWidgetItem *, int)));
  connect(m_tree, SIGNAL(itemSelectionChanged()), this,
          SLOT(workspaceSelected()));
  connect(m_groupButton, SIGNAL(clicked()), this, SLOT(onClickGroupButton()));

  m_tree->setContextMenuPolicy(Qt::CustomContextMenu);
  connect(m_tree, SIGNAL(customContextMenuRequested(const QPoint &)), this,
          SLOT(popupMenu(const QPoint &)));
  connect(this, SIGNAL(signalUpdateTree(const TopLevelItems &)), this,
          SLOT(handleUpdateTree(const TopLevelItems &)), Qt::QueuedConnection);

  connect(this, SIGNAL(signalClearView()), this, SLOT(handleClearView()),
          Qt::QueuedConnection);
  connect(m_tree, SIGNAL(itemSelectionChanged()), this,
          SLOT(onTreeSelectionChanged()));
  connect(m_tree, SIGNAL(itemExpanded(QTreeWidgetItem *)), this,
          SLOT(populateChildData(QTreeWidgetItem *)));
}

 * Flips the flag indicating whether a tree update is in progress. Actions such
 * as sorting
 * are disabled while an update is in progress.
 * @param state The required state for the flag
 */
void WorkspaceTreeWidget::setTreeUpdating(const bool state) {
void WorkspaceTreeWidget::incrementUpdateCount() { m_updateCount.ref(); }
WorkspacePresenterWN_wptr WorkspaceTreeWidget::getPresenterWeakPtr() {
  return boost::dynamic_pointer_cast<WorkspacePresenter>(m_presenter);
/** Returns the names of the selected workspaces
 *   in the dock.
 */
StringList WorkspaceTreeWidget::getSelectedWorkspaceNames() const {
  auto items = m_tree->selectedItems();
  names.reserve(static_cast<size_t>(items.size()));
  for (auto &item : items)
    names.emplace_back(item->text(0).toStdString());
QStringList WorkspaceTreeWidget::getSelectedWorkspaceNamesAsQList() const {
  auto items = m_tree->selectedItems();
  QStringList names;

  for (auto &item : items) {
    names.append(item->text(0));
  }
  return names;
}

/** Returns a pointer to the selected workspace (the first if multiple
 *   workspaces selected)
 */
Mantid::API::Workspace_sptr WorkspaceTreeWidget::getSelectedWorkspace() const {
  auto items = m_tree->selectedItems();
  auto data = items[0]->data(0, Qt::UserRole).value<Workspace_sptr>();

  return data;
bool WorkspaceTreeWidget::askUserYesNo(const std::string &caption,
Elliot Oram's avatar
Elliot Oram committed
                                       const std::string &message) const {
  return QMessageBox::question(parentWidget(), QString::fromStdString(caption),
                               QString::fromStdString(message),
                               QMessageBox::Yes,
                               QMessageBox::No) == QMessageBox::Yes;
}

void WorkspaceTreeWidget::showCriticalUserMessage(
    const std::string &caption, const std::string &message) const {
Elliot Oram's avatar
Elliot Oram committed
  QMessageBox::critical(parentWidget(), QString::fromStdString(caption),
                        QString::fromStdString(message));
}

void WorkspaceTreeWidget::onLoadAccept() {
  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");
  m_mantidDisplayModel->updateRecentFilesList(fn);
void WorkspaceTreeWidget::showLoadDialog() {
  QMetaObject::invokeMethod(dynamic_cast<QObject *>(m_mantidDisplayModel),
                            "showAlgorithmDialog", Qt::QueuedConnection,
                            Q_ARG(QString, "Load"));
void WorkspaceTreeWidget::showLiveDataDialog() {
  QMetaObject::invokeMethod(dynamic_cast<QObject *>(m_mantidDisplayModel),
                            "showAlgorithmDialog", Qt::QueuedConnection,
                            Q_ARG(QString, "StartLiveData"));
void WorkspaceTreeWidget::renameWorkspace() {
  m_presenter->notifyFromView(ViewNotifiable::Flag::RenameWorkspace);
}

void WorkspaceTreeWidget::showRenameDialog(const StringList &wsNames) {
  QStringList names;

  for (auto &ws : wsNames)
    names.append(QString::fromStdString(ws));
  m_mantidDisplayModel->renameWorkspace(names);
 * Save the old and the new name in m_renameMap. This is needed to restore
 * selection
 *   of the renamed workspace (if it was selected before renaming).
 * @param oldName :: Old name of a renamed workspace.
 * @param newName :: New name of a renamed workspace.
 */
void WorkspaceTreeWidget::recordWorkspaceRename(const std::string &oldName,
Elliot Oram's avatar
Elliot Oram committed
                                                const std::string &newName) {
  QString qs_oldName = QString::fromStdString(oldName);
  QString qs_newName = QString::fromStdString(newName);

  QMutexLocker renameMapLock(&m_mutex);
  // check if old_name has been recently a new name
  QList<QString> oldNames = m_renameMap.keys(qs_oldName);
  // non-empty list of oldNames become new_name
  if (!oldNames.isEmpty()) {
    for (auto &name : oldNames)
      m_renameMap[name] = qs_newName;
  } else {
    // record a new rename pair
    m_renameMap[qs_oldName] = qs_newName;
void WorkspaceTreeWidget::refreshWorkspaces() {
  m_presenter->notifyFromView(ViewNotifiable::Flag::RefreshWorkspaces);
}

void WorkspaceTreeWidget::enableDeletePrompt(bool enable) {
bool WorkspaceTreeWidget::isPromptDelete() const { return m_promptDelete; }
bool WorkspaceTreeWidget::deleteConfirmation() const {
  std::string message =
      "Are you sure you want to delete the selected Workspaces?";
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
  message += "\n\nThis prompt can be disabled from:\nFile->Settings->General";
#else
  message += "\n\nThis prompt can be disabled "
             "from:\nPreferences->General->Confirmations";
#endif
  return askUserYesNo("Delete Workspaces", message);
void WorkspaceTreeWidget::deleteWorkspaces(const StringList &wsNames) {
  QStringList names;
  for (auto &ws : wsNames)
    names.append(QString::fromStdString(ws));
  m_mantidDisplayModel->deleteWorkspaces(names);
void WorkspaceTreeWidget::clearView() { emit signalClearView(); }
void WorkspaceTreeWidget::sortAscending() {
Moore's avatar
Moore committed
  m_sortDirection = SortDirection::Ascending;
  m_presenter->notifyFromView(ViewNotifiable::Flag::SortWorkspaces);
}

void WorkspaceTreeWidget::sortDescending() {
Moore's avatar
Moore committed
  m_sortDirection = SortDirection::Descending;
  m_presenter->notifyFromView(ViewNotifiable::Flag::SortWorkspaces);
}

void WorkspaceTreeWidget::chooseByName() {
Moore's avatar
Moore committed
  m_sortCriteria = SortCriteria::ByName;
  m_presenter->notifyFromView(ViewNotifiable::Flag::SortWorkspaces);
}

void WorkspaceTreeWidget::chooseByLastModified() {
Moore's avatar
Moore committed
  m_sortCriteria = SortCriteria::ByLastModified;
  m_presenter->notifyFromView(ViewNotifiable::Flag::SortWorkspaces);
}

Sam's avatar
Sam committed
void WorkspaceTreeWidget::chooseByMemorySize() {
  m_sortCriteria = SortCriteria::ByMemorySize;
  m_presenter->notifyFromView(ViewNotifiable::Flag::SortWorkspaces);
}

void WorkspaceTreeWidget::excludeItemFromSort(MantidTreeWidgetItem *item) {
Moore's avatar
Moore committed
  static int counter = 1;

  item->setSortPos(counter);

  counter++;
}

Elliot Oram's avatar
Elliot Oram committed
WorkspaceTreeWidget::SortDirection
WorkspaceTreeWidget::getSortDirection() const {
  return m_sortDirection;
WorkspaceTreeWidget::SortCriteria WorkspaceTreeWidget::getSortCriteria() const {
void WorkspaceTreeWidget::sortWorkspaces(SortCriteria criteria,
Elliot Oram's avatar
Elliot Oram committed
                                         SortDirection direction) {
Moore's avatar
Moore committed
  if (isTreeUpdating())
    return;
  m_tree->setSortScheme(whichCriteria(criteria));
Moore's avatar
Moore committed
  m_tree->setSortOrder(direction == SortDirection::Ascending
                           ? Qt::AscendingOrder
                           : Qt::DescendingOrder);
  m_tree->sort();
}
Sam's avatar
Sam committed
MantidQt::MantidWidgets::MantidItemSortScheme
WorkspaceTreeWidget::whichCriteria(SortCriteria criteria) {
  switch (criteria) {
  case SortCriteria::ByName:
    return MantidItemSortScheme::ByName;
  case SortCriteria::ByLastModified:
    return MantidItemSortScheme::ByLastModified;
  case SortCriteria::ByMemorySize:
    return MantidItemSortScheme::ByMemorySize;
  default:
    // Handle if someone adds a new Enum and it falls through by defaulting to
    // name
    return MantidItemSortScheme::ByName;
void WorkspaceTreeWidget::saveWorkspaceCollection() {
Moore's avatar
Moore committed
  m_presenter->notifyFromView(ViewNotifiable::Flag::SaveWorkspaceCollection);
}

void WorkspaceTreeWidget::handleShowSaveAlgorithm() {
Moore's avatar
Moore committed
  QAction *sendingAction = dynamic_cast<QAction *>(sender());

  if (sendingAction) {
    QString actionName = sendingAction->text();

    if (actionName.compare("Nexus") == 0)
      m_saveFileType = SaveFileType::Nexus;
    else if (actionName.compare("ASCII") == 0)
      m_saveFileType = SaveFileType::ASCII;
  }

  m_presenter->notifyFromView(ViewNotifiable::Flag::SaveSingleWorkspace);
}

WorkspaceTreeWidget::SaveFileType WorkspaceTreeWidget::getSaveFileType() const {
Moore's avatar
Moore committed
  return m_saveFileType;
}

void WorkspaceTreeWidget::saveWorkspace(const std::string &wsName,
  QHash<QString, QString> presets;
  if (!wsName.empty()) {
    presets["InputWorkspace"] = QString::fromStdString(wsName);
  }
Moore's avatar
Moore committed
  int version = -1;
  std::string algorithmName;

  switch (type) {
  case SaveFileType::Nexus:
    algorithmName = "SaveNexus";
    break;
  case SaveFileType::ASCII:
    algorithmName = "SaveAscii";
    break;
  }

Elliot Oram's avatar
Elliot Oram committed
  m_mantidDisplayModel->showAlgorithmDialog(
      QString::fromStdString(algorithmName), presets, nullptr, version);
Moore's avatar
Moore committed
}
void WorkspaceTreeWidget::saveWorkspaces(const StringList &wsNames) {
Moore's avatar
Moore committed
  QList<QTreeWidgetItem *> items = m_tree->selectedItems();
  if (items.size() < 2)
    return;

  m_saveFolderDialog->setWindowTitle("Select save folder");
  m_saveFolderDialog->setLabelText(QFileDialog::Accept, "Select");
  auto res = m_saveFolderDialog->exec();
  auto folder = m_saveFolderDialog->selectedFiles()[0].toStdString();

  IAlgorithm_sptr saveAlg = AlgorithmManager::Instance().create("SaveNexus");
  saveAlg->initialize();

  if (res == QFileDialog::Accepted) {
    for (auto &wsName : wsNames) {
      std::string filename = folder + "/" + wsName + ".nxs";
      try {
        saveAlg->setProperty("InputWorkspace", wsName);
        saveAlg->setProperty("Filename", filename);
        saveAlg->execute();
      } catch (std::exception &ex) {
Moore's avatar
Moore committed
        docklog.error() << "Error saving workspace " << wsName << ": "
                        << ex.what() << '\n';
std::string WorkspaceTreeWidget::getFilterText() const {
  return m_workspaceFilter->text().toStdString();
}

void WorkspaceTreeWidget::filterWorkspaces(const std::string &filterText) {
  const QString text = QString::fromStdString(filterText).trimmed();
  QRegExp filterRegEx(text, Qt::CaseInsensitive);

  // show all items
  QTreeWidgetItemIterator unhideIter(m_tree);
  while (*unhideIter) {
    (*unhideIter)->setHidden(false);
    ++unhideIter;
  }

  int hiddenCount = 0;
  QList<QTreeWidgetItem *> visibleGroups;
  if (!text.isEmpty()) {
    // Loop over everything (currently loaded) and top level
    // find out what is already expanded
    QStringList expanded;
    int n = m_tree->topLevelItemCount();
    for (int i = 0; i < n; ++i) {
      auto item = m_tree->topLevelItem(i);
      if (item->isExpanded()) {
        expanded << item->text(0);
      } else {
        // expand everything that is at the top level (as we lazy load this is
        // required)
        item->setExpanded(true);
      }
    }

    // filter based on the string
    QTreeWidgetItemIterator it(m_tree, QTreeWidgetItemIterator::All);
    while (*it) {
      QTreeWidgetItem *item = (*it);
      QVariant userData = item->data(0, Qt::UserRole);

      if (!userData.isNull()) {
        Workspace_sptr workspace = userData.value<Workspace_sptr>();
        if (workspace) {
          // I am a workspace
          if (item->text(0).contains(filterRegEx)) {
            // my name does match the filter
            if (auto group =
                    boost::dynamic_pointer_cast<WorkspaceGroup>(workspace)) {
              // I am a group, I will want my children to be visible
              // but I cannot do that until this iterator has finished
              // store this pointer in a list for processing later
              visibleGroups.append(item);
              item->setHidden(false);
            }

            if (item->parent() == nullptr) {
              // No parent, I am a top level workspace - show me
              item->setHidden(false);
            } else {
              // I am a child workspace of a group
              // I match, so I want my parent to remain visible as well.
              item->setHidden(false);
              if (item->parent()->isHidden()) {
                // I was previously hidden, show me and set to be expanded
                --hiddenCount;
                item->parent()->setHidden(false);
                expanded << item->parent()->text(0);
              }
            }
          } else {
            // my name does not match the filter - hide me
            item->setHidden(true);
            ++hiddenCount;
          }
        }
      }
      ++it;
    }

    // make children of visible groups visible
    for (auto group : visibleGroups) {
      for (int i = 0; i < group->childCount(); i++) {
        QTreeWidgetItem *child = group->child(i);
        if (child->isHidden()) {
          // I was previously hidden, show me
          --hiddenCount;
          child->setHidden(false);
        }
      }
    }

    // set the expanded state
    for (int i = 0; i < n; ++i) {
      auto item = m_tree->topLevelItem(i);
      item->setExpanded(expanded.contains(item->text(0)));
    }
  }

  // display a message if items are hidden
  if (hiddenCount > 0) {
    QString headerString =
        QString("Workspaces (%1 filtered)").arg(QString::number(hiddenCount));
    m_tree->headerItem()->setText(0, headerString);
  } else {
    m_tree->headerItem()->setText(0, "Workspaces");
  }
}

 * Set tree item's icon based on the ID of the workspace.
 * @param item :: A workspace tree item.
 * @param wsID :: An icon type code.
 */
void WorkspaceTreeWidget::setItemIcon(QTreeWidgetItem *item,
Elliot Oram's avatar
Elliot Oram committed
                                      const std::string &wsID) {
  try {
    item->setIcon(0, QIcon(WORKSPACE_ICONS.getIcon(wsID)));
  } catch (std::runtime_error &) {
    docklog.warning() << "Cannot find icon for workspace ID '" << wsID << "'\n";
  }
}

/**
 * Create the action items associated with the dock
 */
void WorkspaceTreeWidget::createWorkspaceMenuActions() {
  m_showData = new QAction(tr("Show Data"), this);
  connect(m_showData, SIGNAL(triggered()), this, SLOT(onClickShowData()));

  m_showInst = new QAction(tr("Show Instrument"), this);
  connect(m_showInst, SIGNAL(triggered()), this, SLOT(onClickShowInstrument()));

  m_plotSpec = new QAction(tr("Plot Spectrum..."), this);
  connect(m_plotSpec, SIGNAL(triggered()), this, SLOT(onClickPlotSpectra()));

  m_plotSpecErr = new QAction(tr("Plot Spectrum with Errors..."), this);
  connect(m_plotSpecErr, SIGNAL(triggered()), this,
          SLOT(onClickPlotSpectraErr()));
  m_plotAdvanced = new QAction(tr("Plot Advanced..."), this);
  connect(m_plotAdvanced, SIGNAL(triggered()), this,
          SLOT(onClickPlotAdvanced()));
  m_colorFill = new QAction(tr("Color Fill Plot"), this);
  connect(m_colorFill, SIGNAL(triggered()), this,
          SLOT(onClickDrawColorFillPlot()));

  m_showDetectors = new QAction(tr("Show Detectors"), this);
  connect(m_showDetectors, SIGNAL(triggered()), this,
          SLOT(onClickShowDetectorTable()));

  m_showBoxData = new QAction(tr("Show Box Data Table"), this);
  connect(m_showBoxData, SIGNAL(triggered()), this, SLOT(onClickShowBoxData()));

  m_showVatesGui = new QAction(tr("Show Vates Simple Interface"), this);
  {
    QIcon icon;
    icon.addFile(
        QString::fromUtf8(":/VatesSimpleGuiViewWidgets/icons/pvIcon.png"),
        QSize(), QIcon::Normal, QIcon::Off);
    m_showVatesGui->setIcon(icon);
  }
  connect(m_showVatesGui, SIGNAL(triggered()), this, SLOT(onClickShowVates()));

  m_showMDPlot = new QAction(tr("Plot MD"), this);
  connect(m_showMDPlot, SIGNAL(triggered()), this, SLOT(onClickShowMDPlot()));

  m_showListData = new QAction(tr("List Data"), this);
  connect(m_showListData, SIGNAL(triggered()), this,
          SLOT(onClickShowListData()));

  m_showSpectrumViewer = new QAction(tr("Show Spectrum Viewer"), this);
  connect(m_showSpectrumViewer, SIGNAL(triggered()), this,
          SLOT(onClickShowSpectrumViewer()));

  m_showSliceViewer = new QAction(tr("Show Slice Viewer"), this);
  {
    QIcon icon;
    icon.addFile(
        QString::fromUtf8(":/SliceViewer/icons/SliceViewerWindow_icon.png"),
        QSize(), QIcon::Normal, QIcon::Off);
    m_showSliceViewer->setIcon(icon);
  }
  connect(m_showSliceViewer, SIGNAL(triggered()), this,
          SLOT(onClickShowSliceViewer()));

  m_showLogs = new QAction(tr("Sample Logs..."), this);
  connect(m_showLogs, SIGNAL(triggered()), this, SLOT(onClickShowFileLog()));

  m_showSampleMaterial = new QAction(tr("Sample Material..."), this);
  connect(m_showSampleMaterial, SIGNAL(triggered()), this,
          SLOT(onClickShowSampleMaterial()));

  m_showHist = new QAction(tr("Show History"), this);
  connect(m_showHist, SIGNAL(triggered()), this, SLOT(onClickShowAlgHistory()));
  m_saveNexus = new QAction(tr("Save NeXus"), this);
  connect(m_saveNexus, SIGNAL(triggered()), this,
          SLOT(onClickSaveNexusWorkspace()));

  m_rename = new QAction(tr("Rename"), this);
  connect(m_rename, SIGNAL(triggered()), this, SLOT(renameWorkspace()));

  m_delete = new QAction(tr("Delete"), this);
  connect(m_delete, SIGNAL(triggered()), this, SLOT(onClickDeleteWorkspaces()));

  m_showTransposed = new QAction(tr("Show Transposed"), this);
  connect(m_showTransposed, SIGNAL(triggered()), this,
          SLOT(onClickShowTransposed()));

  m_convertToMatrixWorkspace =
      new QAction(tr("Convert to MatrixWorkspace"), this);
  m_convertToMatrixWorkspace->setIcon(QIcon(getQPixmap("mantid_matrix_xpm")));
  connect(m_convertToMatrixWorkspace, SIGNAL(triggered()), this,
          SLOT(onClickConvertToMatrixWorkspace()));

  m_convertMDHistoToMatrixWorkspace =
      new QAction(tr("Convert to MatrixWorkspace"), this);
  m_convertMDHistoToMatrixWorkspace->setIcon(
      QIcon(getQPixmap("mantid_matrix_xpm")));
  connect(m_convertMDHistoToMatrixWorkspace, SIGNAL(triggered()), this,
          SLOT(onClickConvertMDHistoToMatrixWorkspace()));

  m_clearUB = new QAction(tr("Clear UB Matrix"), this);
  connect(m_clearUB, SIGNAL(triggered()), this, SLOT(onClickClearUB()));
 * Create actions for sorting.
 */
void WorkspaceTreeWidget::createSortMenuActions() {
  m_sortCriteria = SortCriteria::ByName;
  m_sortMenu = new QMenu(this);

  QAction *m_ascendingSortAction = new QAction("Ascending", this);
  QAction *m_descendingSortAction = new QAction("Descending", this);
  QAction *m_byNameChoice = new QAction("Name", this);
  QAction *m_byLastModifiedChoice = new QAction("Last Modified", this);
  QAction *m_byMemorySize = new QAction("Size", this);

  m_ascendingSortAction->setCheckable(true);
  m_ascendingSortAction->setEnabled(true);

  m_descendingSortAction->setCheckable(true);
  m_descendingSortAction->setEnabled(true);

  QActionGroup *sortDirectionGroup = new QActionGroup(m_sortMenu);
  sortDirectionGroup->addAction(m_ascendingSortAction);
  sortDirectionGroup->addAction(m_descendingSortAction);
  sortDirectionGroup->setExclusive(true);
  m_ascendingSortAction->setChecked(true);

  m_byNameChoice->setCheckable(true);
  m_byNameChoice->setEnabled(true);

  m_byLastModifiedChoice->setCheckable(true);
  m_byLastModifiedChoice->setEnabled(true);

  m_byMemorySize->setCheckable(true);
  m_byMemorySize->setEnabled(true);

  m_sortChoiceGroup = new QActionGroup(m_sortMenu);
  m_sortChoiceGroup->addAction(m_byNameChoice);
  m_sortChoiceGroup->addAction(m_byLastModifiedChoice);
  m_sortChoiceGroup->addAction(m_byMemorySize);
  m_sortChoiceGroup->setExclusive(true);
  m_byNameChoice->setChecked(true);

  connect(m_ascendingSortAction, SIGNAL(triggered()), this,
          SLOT(sortAscending()));
  connect(m_descendingSortAction, SIGNAL(triggered()), this,
          SLOT(sortDescending()));
  connect(m_byNameChoice, SIGNAL(triggered()), this, SLOT(chooseByName()));
  connect(m_byLastModifiedChoice, SIGNAL(triggered()), this,
Sam's avatar
Sam committed
          SLOT(chooseByLastModified()));
  connect(m_byMemorySize, SIGNAL(triggered()), this,
          SLOT(chooseByMemorySize()));

  m_sortMenu->addActions(sortDirectionGroup->actions());
  m_sortMenu->addSeparator();
  m_sortMenu->addActions(m_sortChoiceGroup->actions());
  m_sortButton->setMenu(m_sortMenu);
}

 * When an item is expanded, populate the child data for this item
 * @param item :: The tree item being expanded
 */
void WorkspaceTreeWidget::populateChildData(QTreeWidgetItem *item) {
  QVariant userData = item->data(0, Qt::UserRole);
  if (userData.isNull())
    return;

  // Clear it first
  while (item->childCount() > 0) {
    auto *widgetItem = item->takeChild(0);
    delete widgetItem;
  }

  Workspace_sptr workspace = userData.value<Workspace_sptr>();

  if (auto group = boost::dynamic_pointer_cast<WorkspaceGroup>(workspace)) {
    auto members = group->getAllItems();
    for (const auto &ws : members) {
      auto *node = addTreeEntry(std::make_pair(ws->getName(), ws), item);
      excludeItemFromSort(node);
      if (shouldBeSelected(node->text(0)))
        node->setSelected(true);
    }
  } else {
    QString details;
    try {
      details = workspace->toString().c_str();
    } catch (std::runtime_error &e) {
      details = QString("Error: %1").arg(e.what());
    }
    QStringList rows =
        details.split(QLatin1Char('\n'), QString::SkipEmptyParts);
    rows.append(QString("Memory used: ") +
                workspace->getMemorySizeAsStr().c_str());

    auto iend = rows.constEnd();
    for (auto itr = rows.constBegin(); itr != iend; ++itr) {
      MantidTreeWidgetItem *data =
          new MantidTreeWidgetItem(QStringList(*itr), m_tree);
      data->setFlags(Qt::NoItemFlags);
      excludeItemFromSort(data);
      item->addChild(data);
    }
  }
}

 * Update the workspace tree to match the current state of the ADS.
 * It is important that the workspace tree is modified only by this method.
 * @param items Items which are currently in the ADS.
 */
void WorkspaceTreeWidget::updateTree(const TopLevelItems &items) {
  incrementUpdateCount();
  emit signalUpdateTree(items);
 * Clears the tree and re-populates it with the given top level items
 * @param topLevelItems The map of names to workspaces
 * @param expanded Names of items who should expanded after being populated
 */
void WorkspaceTreeWidget::populateTopLevel(const TopLevelItems &topLevelItems,
Elliot Oram's avatar
Elliot Oram committed
                                           const QStringList &expanded) {
  {
    QMutexLocker lock(&m_mutex);
    // collect names of selected workspaces
    QList<QTreeWidgetItem *> selected = m_tree->selectedItems();
    m_selectedNames.clear(); // just in case
    foreach (QTreeWidgetItem *item, selected) {
      m_selectedNames << item->text(0);
    }
    // populate the tree from scratch
    m_tree->clear();
    auto iend = topLevelItems.end();
    for (auto it = topLevelItems.begin(); it != iend; ++it) {
      auto *node = addTreeEntry(*it);
      QString name = node->text(0);
      if (expanded.contains(name))
        node->setExpanded(true);
      // see if item must be selected
      if (shouldBeSelected(name))
        node->setSelected(true);
    }
    m_selectedNames.clear();
    m_renameMap.clear();
  }
  // apply any filtering
  filterWorkspaceTree(m_workspaceFilter->text());
}

 * Adds a node for the given named item, including a single child ID item to
 * make each node have a expandable button and allowing plotting to work from
 * non-expanded items
 * @param item A name/workspace pair to add.
 * @param parent If not null then add the new items as a child of the given item
 */
MantidTreeWidgetItem *WorkspaceTreeWidget::addTreeEntry(
    const std::pair<std::string, Mantid::API::Workspace_sptr> &item,
    QTreeWidgetItem *parent) {
  MantidTreeWidgetItem *node =
      new MantidTreeWidgetItem(QStringList(item.first.c_str()), m_tree);
  node->setData(0, Qt::UserRole, QVariant::fromValue(item.second));

  // A a child ID item so that it becomes expandable. Using the correct ID is
  // needed when plotting from non-expanded groups.
  const std::string wsID = item.second->id();
  MantidTreeWidgetItem *idNode =
      new MantidTreeWidgetItem(QStringList(wsID.c_str()), m_tree);
  idNode->setFlags(Qt::NoItemFlags);
  node->addChild(idNode);
  setItemIcon(node, wsID);

  if (parent) {
    parent->addChild(node);
  } else {
    m_tree->addTopLevelItem(node);
  }
  return node;
}

 * Check if a workspace should be selected after dock update.
 * @param name :: Name of a workspace to check.
 */
bool WorkspaceTreeWidget::shouldBeSelected(QString name) const {
  QMutexLocker lock(&m_mutex);
  QStringList renamed = m_renameMap.keys(name);
  if (!renamed.isEmpty()) {
    foreach (QString oldName, renamed) {
      if (m_selectedNames.contains(oldName)) {
        return true;
      }
    }
  } else if (m_selectedNames.contains(name)) {
    return true;
  }
  return false;
}

void WorkspaceTreeWidget::onTreeSelectionChanged() {
  // get selected workspaces
  auto items = m_tree->selectedItems();

  if (m_groupButton) {
    if (items.size() == 1) {
      // check it's group
      auto wsSptr =
          items.first()->data(0, Qt::UserRole).value<Workspace_sptr>();
      auto grpSptr = boost::dynamic_pointer_cast<WorkspaceGroup>(wsSptr);
      if (grpSptr) {
        m_groupButton->setText("Ungroup");
        m_groupButton->setEnabled(true);
      } else
        m_groupButton->setEnabled(false);

    } else if (items.size() >= 2) {
      m_groupButton->setText("Group");
      m_groupButton->setEnabled(true);
    } else if (items.size() == 0) {
      m_groupButton->setText("Group");
      m_groupButton->setEnabled(false);
    }
  }

  if (m_deleteButton)
    m_deleteButton->setEnabled(items.size() > 0);

  if (m_saveButton)
    m_saveButton->setEnabled(items.size() > 0);

  if (items.size() > 0) {
    auto item = *(items.begin());
Elliot Oram's avatar
Elliot Oram committed
    m_mantidDisplayModel->enableSaveNexus(item->text(0));
Elliot Oram's avatar
Elliot Oram committed
    m_mantidDisplayModel->disableSaveNexus();
 * Add the actions that are appropriate for a MatrixWorkspace
 * @param menu :: The menu to store the items
 * @param matrixWS :: The workspace related to the menu
 */
void WorkspaceTreeWidget::addMatrixWorkspaceMenuItems(
    QMenu *menu,
    const Mantid::API::MatrixWorkspace_const_sptr &matrixWS) const {
  // Add all options except plot of we only have 1 value
  menu->addAction(m_showData);
  menu->addAction(m_showInst);
  // Disable the 'show instrument' option if a workspace doesn't have an
  // instrument attached or if it does not have a spectra axis
  m_showInst->setEnabled(matrixWS->getInstrument() &&
                         !matrixWS->getInstrument()->getName().empty() &&
                         matrixWS->getAxis(1)->isSpectra());
  menu->addSeparator();
  menu->addAction(m_plotSpec);
  menu->addAction(m_plotSpecErr);
  menu->addAction(m_plotAdvanced);

  // Don't plot a spectrum if only one X value
  bool multipleBins = false;
  try {
    multipleBins = (matrixWS->blocksize() > 1);
  } catch (...) {
    const size_t numHist = matrixWS->getNumberHistograms();
    for (size_t i = 0; i < numHist; ++i) {
      if (matrixWS->y(i).size() > 1) {
        multipleBins = true;
        break;
      }
    }
  }
  m_plotSpec->setEnabled(multipleBins);
  m_plotSpecErr->setEnabled(multipleBins);
  m_plotAdvanced->setEnabled(multipleBins);

  menu->addAction(m_showSpectrumViewer); // The 2D spectrum viewer

  menu->addAction(m_colorFill);
  // Show the color fill plot if you have more than one histogram
  m_colorFill->setEnabled(
      (matrixWS->axes() > 1 && matrixWS->getNumberHistograms() > 1));
  menu->addAction(m_showSliceViewer); // The 2D slice viewer
  menu->addSeparator();
  menu->addAction(m_showDetectors);
  menu->addAction(m_showLogs);
  menu->addAction(m_showSampleMaterial);
  menu->addAction(m_showHist);
  menu->addAction(m_saveNexus);
}

/**
 * Add the actions that are appropriate for a MDEventWorkspace
 * @param menu :: The menu to store the items
 * @param WS :: The workspace related to the menu
 */
void WorkspaceTreeWidget::addMDEventWorkspaceMenuItems(