File indexing completed on 2024-05-12 15:27:03

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