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 }