Skip to content
Snippets Groups Projects
FitScriptGeneratorDataTable.cpp 9.9 KiB
Newer Older
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2020 ISIS Rutherford Appleton Laboratory UKRI,
//   NScD Oak Ridge National Laboratory, European Spallation Source,
//   Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
#include "MantidQtWidgets/Common/FitScriptGeneratorDataTable.h"

#include <QAbstractItemView>
#include <QDoubleValidator>
#include <QHeaderView>
#include <QHoverEvent>
#include <QLineEdit>
#include <QStringList>
#include <QValidator>

namespace {

int DOUBLE_PRECISION(5);

QValidator *createDoubleValidator() {
  auto validator = new QDoubleValidator(-100000.0, 100000.0, DOUBLE_PRECISION);
  validator->setNotation(QDoubleValidator::Notation::StandardNotation);
  return validator;
}

QValidator *createIntValidator() { return new QIntValidator(0, 100000); }

QTableWidgetItem *createTableItem(QString const &value,
                                  Qt::AlignmentFlag const &alignment,
                                  bool editable) {
  auto item = new QTableWidgetItem(value);
  item->setTextAlignment(alignment);
  if (!editable)
    item->setFlags(item->flags() ^ Qt::ItemIsEditable);
  return item;
}

QTableWidgetItem *createIntTableItem(int value,
                                     Qt::AlignmentFlag const &alignment,
                                     bool editable) {
  return createTableItem(QString::number(value), alignment, editable);
}

QTableWidgetItem *createDoubleTableItem(double value,
                                        Qt::AlignmentFlag const &alignment,
                                        bool editable) {
  return createTableItem(QString::number(value, 'f', DOUBLE_PRECISION),
                         alignment, editable);
}

} // namespace

namespace MantidQt {
namespace MantidWidgets {

using DelegateType = CustomItemDelegate::DelegateType;

/**
 * FitScriptGeneratorDataTable class methods.
 */

FitScriptGeneratorDataTable::FitScriptGeneratorDataTable(QWidget *parent)
    : QTableWidget(parent), m_selectedRow(-1), m_selectedColumn(-1),
      m_selectedValue(0.0) {
  this->setSelectionBehavior(QAbstractItemView::SelectRows);
  this->setSelectionMode(QAbstractItemView::ExtendedSelection);
  this->setShowGrid(false);
  this->setColumnCount(4);
  this->setRowCount(0);
  this->verticalHeader()->setVisible(false);
  this->horizontalHeader()->setHighlightSections(false);
  this->horizontalHeader()->setStretchLastSection(true);

  this->setColumnWidth(ColumnIndex::WorkspaceName, 280);
  this->setColumnWidth(ColumnIndex::WorkspaceIndex, 80);
  this->setColumnWidth(ColumnIndex::StartX, 100);
  this->setColumnWidth(ColumnIndex::EndX, 100);

  this->setHorizontalHeaderLabels(
      QStringList({"Name", "WS Index", "StartX", "EndX"}));

  this->setStyleSheet("QTableWidget {\n"
                      "    font-size: 8pt;\n"
                      "    border: 1px solid #828790;\n"
                      "}\n"
                      "\n"
                      "QTableWidget::item:selected {\n"
                      "    background-color: #c7e0ff;\n"
                      "    color: #000000;\n"
                      "}"
                      "\n"
                      "QTableWidget::item:hover {\n"
                      "    background-color: #c7e0ff;\n"
                      "    color: #000000;\n"
                      "}");

  m_lastIndex = QPersistentModelIndex();
  this->viewport()->installEventFilter(this);

  this->setItemDelegateForColumn(
      ColumnIndex::WorkspaceName,
      new CustomItemDelegate(this, DelegateType::String));
  this->setItemDelegateForColumn(
      ColumnIndex::WorkspaceIndex,
      new CustomItemDelegate(this, DelegateType::Int));
  this->setItemDelegateForColumn(
      ColumnIndex::StartX, new CustomItemDelegate(this, DelegateType::Double));
  this->setItemDelegateForColumn(
      ColumnIndex::EndX, new CustomItemDelegate(this, DelegateType::Double));

  connect(this, SIGNAL(itemClicked(QTableWidgetItem *)), this,
          SLOT(handleItemClicked(QTableWidgetItem *)));
}

bool FitScriptGeneratorDataTable::eventFilter(QObject *widget, QEvent *event) {
  if (widget == this->viewport()) {
    auto index = hoveredRowIndex(event);

    if (index != m_lastIndex) {
      if (this->item(m_lastIndex.row(), m_lastIndex.column()))
        emit itemExited(index.isValid() ? index.row() : -1);
      m_lastIndex = QPersistentModelIndex(index);
    }
  }
  return QTableWidget::eventFilter(widget, event);
}

void FitScriptGeneratorDataTable::handleItemClicked(QTableWidgetItem *item) {
  m_selectedRow = item->row();
  m_selectedColumn = item->column();
  if (m_selectedColumn == ColumnIndex::StartX ||
      m_selectedColumn == ColumnIndex::EndX)
    m_selectedValue = item->text().toDouble();
}

QPersistentModelIndex
FitScriptGeneratorDataTable::hoveredRowIndex(QEvent *event) {
  auto index = m_lastIndex;
  auto const eventType = event->type();
  if (eventType == QEvent::HoverMove)
    index = QPersistentModelIndex(
        this->indexAt(static_cast<QHoverEvent *>(event)->pos()));
  else if (eventType == QEvent::Leave)
    index = QPersistentModelIndex(QModelIndex());
std::string
FitScriptGeneratorDataTable::workspaceName(FitDomainIndex row) const {
  return getText(row, ColumnIndex::WorkspaceName).toStdString();
}

WorkspaceIndex
FitScriptGeneratorDataTable::workspaceIndex(FitDomainIndex row) const {
  return getText(row, ColumnIndex::WorkspaceIndex).toInt();
}

double FitScriptGeneratorDataTable::startX(FitDomainIndex row) const {
  return getText(row, ColumnIndex::StartX).toDouble();
}

double FitScriptGeneratorDataTable::endX(FitDomainIndex row) const {
  return getText(row, ColumnIndex::EndX).toDouble();
}

std::vector<FitDomainIndex> FitScriptGeneratorDataTable::selectedRows() const {
  std::vector<FitDomainIndex> rowIndices;

  auto const selectionModel = this->selectionModel();
  if (selectionModel->hasSelection()) {

    for (auto const &rowIndex : selectionModel->selectedRows())
      rowIndices.emplace_back(
          FitDomainIndex(static_cast<std::size_t>(rowIndex.row())));

    std::sort(rowIndices.rbegin(), rowIndices.rend());
  }
  return rowIndices;
}

void FitScriptGeneratorDataTable::removeDomain(
    std::string const &workspaceName,
    MantidWidgets::WorkspaceIndex workspaceIndex) {
  this->removeRow(indexOfDomain(workspaceName, workspaceIndex));
}

void FitScriptGeneratorDataTable::addDomain(
    QString const &workspaceName, MantidWidgets::WorkspaceIndex workspaceIndex,
    double startX, double endX) {
  this->blockSignals(true);

  auto const rowIndex = this->rowCount();
  this->insertRow(rowIndex);

  this->setItem(rowIndex, ColumnIndex::WorkspaceName,
                createTableItem(workspaceName, Qt::AlignVCenter, false));
  this->setItem(rowIndex, ColumnIndex::WorkspaceIndex,
                createIntTableItem(static_cast<int>(workspaceIndex.value),
                                   Qt::AlignCenter, false));
  this->setItem(rowIndex, ColumnIndex::StartX,
                createDoubleTableItem(startX, Qt::AlignCenter, true));
  this->setItem(rowIndex, ColumnIndex::EndX,
                createDoubleTableItem(endX, Qt::AlignCenter, true));

  this->blockSignals(false);
int FitScriptGeneratorDataTable::indexOfDomain(
    std::string const &workspaceName,
    MantidWidgets::WorkspaceIndex workspaceIndex) const {
  for (auto rowIndex = 0; rowIndex < this->rowCount(); ++rowIndex) {
    if (this->workspaceName(rowIndex) == workspaceName &&
        this->workspaceIndex(rowIndex) == workspaceIndex)
      return rowIndex;
  }
  return -1;
}

QString FitScriptGeneratorDataTable::getText(FitDomainIndex row,
                                             int column) const {
  return this->item(static_cast<int>(row.value), column)->text();
}

void FitScriptGeneratorDataTable::formatSelection() {
  setSelectedXValue(
      this->item(m_selectedRow, m_selectedColumn)->text().toDouble());
}

void FitScriptGeneratorDataTable::resetSelection() {
  setSelectedXValue(m_selectedValue);
}

void FitScriptGeneratorDataTable::setSelectedXValue(double xValue) {
  this->blockSignals(true);
  this->setItem(m_selectedRow, m_selectedColumn,
                createDoubleTableItem(xValue, Qt::AlignCenter, true));
  this->blockSignals(false);
}

/**
 * CustomItemDelegate class methods.
 */

CustomItemDelegate::CustomItemDelegate(FitScriptGeneratorDataTable *parent,
                                       DelegateType const &type)
    : QStyledItemDelegate(parent), m_tableWidget(parent), m_type(type) {
  m_tableWidget = parent;
  m_tableWidget->setMouseTracking(true);

  connect(m_tableWidget, SIGNAL(itemEntered(QTableWidgetItem *)), this,
          SLOT(handleItemEntered(QTableWidgetItem *)));
  connect(m_tableWidget, SIGNAL(itemExited(int)), this,
          SLOT(handleItemExited(int)));

  m_hoveredIndex = -1;
}

void CustomItemDelegate::handleItemEntered(QTableWidgetItem *item) {
  m_hoveredIndex = item->row();
  m_tableWidget->viewport()->update();
}

void CustomItemDelegate::handleItemExited(int newRowIndex) {
  m_hoveredIndex = newRowIndex;
}

QWidget *CustomItemDelegate::createEditor(QWidget *parent,
                                          QStyleOptionViewItem const &option,
                                          QModelIndex const &index) const {
  Q_UNUSED(option);
  Q_UNUSED(index);
  auto lineEdit = new QLineEdit(parent);
  switch (m_type) {
  case DelegateType::Double:
    lineEdit->setValidator(createDoubleValidator());
    break;
  case DelegateType::Int:
    lineEdit->setValidator(createIntValidator());
    break;
  case DelegateType::String:
    break;
  }

  return lineEdit;
}

void CustomItemDelegate::paint(QPainter *painter,
                               QStyleOptionViewItem const &option,
                               QModelIndex const &index) const {
  auto opt = QStyleOptionViewItem(option);
  if (index.row() == m_hoveredIndex)
    opt.state |= QStyle::State_MouseOver;
  QStyledItemDelegate::paint(painter, opt, index);
}

} // namespace MantidWidgets
} // namespace MantidQt