File indexing completed on 2024-05-12 03:47:48

0001 /*
0002     File                 : MatrixModel.cpp
0003     Project              : LabPlot
0004     Description          : Matrix data model
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2015-2016 Alexander Semke <alexander.semke@web.de>
0007     SPDX-FileCopyrightText: 2008-2009 Tilman Benkert <thzs@gmx.net>
0008     SPDX-FileCopyrightText: 2018-2020 Stefan Gerlach <stefan.gerlach@uni.kn>
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include "backend/matrix/MatrixModel.h"
0013 #include "backend/matrix/Matrix.h"
0014 
0015 #include <QBrush>
0016 
0017 /*!
0018   \class MatrixModel
0019   \brief Model for the access to data of a Matrix-object.
0020 
0021     This is a model in the sense of Qt4 model/view framework which is used
0022     to access a Matrix object from any of Qt4s view classes, typically a QMatrixView.
0023     Its main purposes are translating Matrix signals into QAbstractItemModel signals
0024     and translating calls to the QAbstractItemModel read/write API into calls
0025     in the public API of Matrix.
0026 
0027     \ingroup backend
0028 */
0029 MatrixModel::MatrixModel(Matrix* matrix)
0030     : QAbstractItemModel(nullptr)
0031     , m_matrix(matrix) {
0032     connect(m_matrix, &Matrix::columnsAboutToBeInserted, this, &MatrixModel::handleColumnsAboutToBeInserted);
0033     connect(m_matrix, &Matrix::columnsInserted, this, &MatrixModel::handleColumnsInserted);
0034     connect(m_matrix, &Matrix::columnsAboutToBeRemoved, this, &MatrixModel::handleColumnsAboutToBeRemoved);
0035     connect(m_matrix, &Matrix::columnsRemoved, this, &MatrixModel::handleColumnsRemoved);
0036     connect(m_matrix, &Matrix::rowsAboutToBeInserted, this, &MatrixModel::handleRowsAboutToBeInserted);
0037     connect(m_matrix, &Matrix::rowsInserted, this, &MatrixModel::handleRowsInserted);
0038     connect(m_matrix, &Matrix::rowsAboutToBeRemoved, this, &MatrixModel::handleRowsAboutToBeRemoved);
0039     connect(m_matrix, &Matrix::rowsRemoved, this, &MatrixModel::handleRowsRemoved);
0040     connect(m_matrix, &Matrix::dataChanged, this, &MatrixModel::handleDataChanged);
0041     connect(m_matrix, &Matrix::coordinatesChanged, this, &MatrixModel::handleCoordinatesChanged);
0042     connect(m_matrix, &Matrix::numericFormatChanged, this, &MatrixModel::handleFormatChanged);
0043     connect(m_matrix, &Matrix::precisionChanged, this, &MatrixModel::handleFormatChanged);
0044 }
0045 
0046 void MatrixModel::setSuppressDataChangedSignal(bool b) {
0047     m_suppressDataChangedSignal = b;
0048 }
0049 
0050 void MatrixModel::setChanged() {
0051     Q_EMIT changed();
0052 }
0053 
0054 Qt::ItemFlags MatrixModel::flags(const QModelIndex& index) const {
0055     if (index.isValid())
0056         return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
0057     else
0058         return Qt::ItemIsEnabled;
0059 }
0060 
0061 QVariant MatrixModel::data(const QModelIndex& index, int role) const {
0062     if (!index.isValid())
0063         return {};
0064 
0065     int row = index.row();
0066     int col = index.column();
0067 
0068     switch (role) {
0069     case Qt::ToolTipRole:
0070     case Qt::EditRole:
0071     case Qt::DisplayRole: {
0072         auto mode = m_matrix->mode();
0073         // DEBUG(Q_FUNC_INFO << ", DisplayRole, mode = " << mode);
0074         switch (mode) {
0075         case AbstractColumn::ColumnMode::Double:
0076             return {m_matrix->text<double>(row, col)};
0077         case AbstractColumn::ColumnMode::Integer:
0078             return {m_matrix->text<int>(row, col)};
0079         case AbstractColumn::ColumnMode::BigInt:
0080             return {m_matrix->text<qint64>(row, col)};
0081         case AbstractColumn::ColumnMode::DateTime:
0082         case AbstractColumn::ColumnMode::Month:
0083         case AbstractColumn::ColumnMode::Day:
0084             return {m_matrix->text<QDateTime>(row, col)};
0085         case AbstractColumn::ColumnMode::Text: // should not happen
0086             return {m_matrix->text<QString>(row, col)};
0087         }
0088         break;
0089     }
0090     case Qt::BackgroundRole:
0091         // use bluish background color to distinguish Matrix from Spreadsheet
0092         return {QColor(192, 255, 255)};
0093     case Qt::ForegroundRole:
0094         // ignore current theme settings and always use black foreground color so Matrix is usable with dark themes, too.
0095         return {QColor(Qt::black)};
0096     }
0097 
0098     return {};
0099 }
0100 
0101 QVariant MatrixModel::headerData(int section, Qt::Orientation orientation, int role) const {
0102     QString result;
0103     auto headerFormat = m_matrix->headerFormat();
0104     const auto numberLocale = QLocale();
0105     switch (orientation) {
0106     case Qt::Horizontal:
0107         switch (role) {
0108         case Qt::DisplayRole:
0109         case Qt::ToolTipRole:
0110             if (headerFormat == Matrix::HeaderFormat::HeaderRowsColumns) {
0111                 result = QString::number(section + 1);
0112             } else if (headerFormat == Matrix::HeaderFormat::HeaderValues) {
0113                 double diff = m_matrix->xEnd() - m_matrix->xStart();
0114                 double step = 0.0;
0115                 if (m_matrix->columnCount() > 1)
0116                     step = diff / double(m_matrix->columnCount() - 1);
0117                 result = numberLocale.toString(m_matrix->xStart() + double(section) * step, m_matrix->numericFormat(), m_matrix->precision());
0118             } else {
0119                 result = QString::number(section + 1) + QLatin1String(" (");
0120                 double diff = m_matrix->xEnd() - m_matrix->xStart();
0121                 double step = 0.0;
0122                 if (m_matrix->columnCount() > 1)
0123                     step = diff / double(m_matrix->columnCount() - 1);
0124                 result += numberLocale.toString(m_matrix->xStart() + double(section) * step, m_matrix->numericFormat(), m_matrix->precision());
0125 
0126                 result += QLatin1Char(')');
0127             }
0128             return {result};
0129         }
0130         break;
0131     case Qt::Vertical:
0132         switch (role) {
0133         case Qt::DisplayRole:
0134         case Qt::ToolTipRole:
0135             if (headerFormat == Matrix::HeaderFormat::HeaderRowsColumns) {
0136                 result = QString::number(section + 1);
0137             } else if (headerFormat == Matrix::HeaderFormat::HeaderValues) {
0138                 double diff = m_matrix->yEnd() - m_matrix->yStart();
0139                 double step = 0.0;
0140                 if (m_matrix->rowCount() > 1)
0141                     step = diff / double(m_matrix->rowCount() - 1);
0142                 // TODO: implement decent double == 0 check
0143                 //                      if (diff < 1e-10)
0144                 //                          result += numberLocale.toString(m_matrix->yStart(),
0145                 //                                  m_matrix->numericFormat(), m_matrix->displayedDigits());
0146                 result += numberLocale.toString(m_matrix->yStart() + double(section) * step, m_matrix->numericFormat(), m_matrix->precision());
0147             } else {
0148                 result = QString::number(section + 1) + QStringLiteral(" (");
0149                 double diff = m_matrix->yEnd() - m_matrix->yStart();
0150                 double step = 0.0;
0151                 if (m_matrix->rowCount() > 1)
0152                     step = diff / double(m_matrix->rowCount() - 1);
0153 
0154                 result += numberLocale.toString(m_matrix->yStart() + double(section) * step, m_matrix->numericFormat(), m_matrix->precision());
0155                 result += QLatin1Char(')');
0156             }
0157             return {result};
0158         }
0159     }
0160     return {};
0161 }
0162 
0163 int MatrixModel::rowCount(const QModelIndex& /*parent*/) const {
0164     return m_matrix->rowCount();
0165 }
0166 
0167 int MatrixModel::columnCount(const QModelIndex& /*parent*/) const {
0168     return m_matrix->columnCount();
0169 }
0170 
0171 bool MatrixModel::setData(const QModelIndex& index, const QVariant& value, int role) {
0172     if (!index.isValid())
0173         return false;
0174 
0175     int row = index.row();
0176     int column = index.column();
0177 
0178     if (role == Qt::EditRole) {
0179         const auto mode = m_matrix->mode();
0180         switch (mode) {
0181         case AbstractColumn::ColumnMode::Double:
0182             m_matrix->setCell(row, column, value.toDouble());
0183             break;
0184         case AbstractColumn::ColumnMode::Integer:
0185             m_matrix->setCell(row, column, value.toInt());
0186             break;
0187         case AbstractColumn::ColumnMode::BigInt:
0188             m_matrix->setCell(row, column, value.toLongLong());
0189             break;
0190         case AbstractColumn::ColumnMode::DateTime:
0191         case AbstractColumn::ColumnMode::Month:
0192         case AbstractColumn::ColumnMode::Day:
0193             DEBUG(" WARNING: DateTime format not supported yet"); // should not happen
0194             // TODO: m_matrix->setCell(row, column, value.toDateTime());
0195             break;
0196         case AbstractColumn::ColumnMode::Text:
0197             DEBUG(" WARNING: Text format not supported yet"); // should not happen
0198             m_matrix->setCell(row, column, value.toString());
0199             break;
0200         }
0201 
0202         if (!m_suppressDataChangedSignal)
0203             Q_EMIT changed();
0204         return true;
0205     }
0206     return false;
0207 }
0208 
0209 QModelIndex MatrixModel::index(int row, int column, const QModelIndex& /*parent*/) const {
0210     return createIndex(row, column);
0211 }
0212 
0213 QModelIndex MatrixModel::parent(const QModelIndex& /*child*/) const {
0214     return QModelIndex{};
0215 }
0216 
0217 void MatrixModel::updateHeader() {
0218     Q_EMIT headerDataChanged(Qt::Horizontal, 0, m_matrix->columnCount());
0219     Q_EMIT headerDataChanged(Qt::Vertical, 0, m_matrix->rowCount());
0220 }
0221 
0222 void MatrixModel::handleColumnsAboutToBeInserted(int before, int count) {
0223     beginInsertColumns(QModelIndex(), before, before + count - 1);
0224 }
0225 
0226 void MatrixModel::handleColumnsInserted(int /*first*/, int /*count*/) {
0227     endInsertColumns();
0228     if (!m_suppressDataChangedSignal)
0229         Q_EMIT changed();
0230 }
0231 
0232 void MatrixModel::handleColumnsAboutToBeRemoved(int first, int count) {
0233     beginRemoveColumns(QModelIndex(), first, first + count - 1);
0234 }
0235 
0236 void MatrixModel::handleColumnsRemoved(int /*first*/, int /*count*/) {
0237     endRemoveColumns();
0238     if (!m_suppressDataChangedSignal)
0239         Q_EMIT changed();
0240 }
0241 
0242 void MatrixModel::handleRowsAboutToBeInserted(int before, int count) {
0243     beginInsertRows(QModelIndex(), before, before + count - 1);
0244 }
0245 
0246 void MatrixModel::handleRowsInserted(int /*first*/, int /*count*/) {
0247     endInsertRows();
0248     if (!m_suppressDataChangedSignal)
0249         Q_EMIT changed();
0250 }
0251 
0252 void MatrixModel::handleRowsAboutToBeRemoved(int first, int count) {
0253     beginRemoveRows(QModelIndex(), first, first + count - 1);
0254 }
0255 
0256 void MatrixModel::handleRowsRemoved(int /*first*/, int /*count*/) {
0257     endRemoveRows();
0258     if (!m_suppressDataChangedSignal)
0259         Q_EMIT changed();
0260 }
0261 
0262 void MatrixModel::handleDataChanged(int top, int left, int bottom, int right) {
0263     Q_EMIT dataChanged(index(top, left), index(bottom, right));
0264     if (!m_suppressDataChangedSignal)
0265         Q_EMIT changed();
0266 }
0267 
0268 void MatrixModel::handleCoordinatesChanged() {
0269     Q_EMIT headerDataChanged(Qt::Horizontal, 0, columnCount() - 1);
0270     Q_EMIT headerDataChanged(Qt::Vertical, 0, rowCount() - 1);
0271 }
0272 
0273 void MatrixModel::handleFormatChanged() {
0274     handleCoordinatesChanged();
0275     handleDataChanged(0, 0, rowCount() - 1, columnCount() - 1);
0276 }