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 }