File indexing completed on 2024-05-12 03:47:47
0001 /* 0002 File : Matrix.cpp 0003 Project : Matrix 0004 Description : Spreadsheet with a MxN matrix data model 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2008-2009 Tilman Benkert <thzs@gmx.net> 0007 SPDX-FileCopyrightText: 2015-2017 Alexander Semke <alexander.semke@web.de> 0008 SPDX-FileCopyrightText: 2017-2020 Stefan Gerlach <stefan.gerlach@uni.kn> 0009 0010 SPDX-License-Identifier: GPL-2.0-or-later 0011 */ 0012 0013 #include "Matrix.h" 0014 #include "MatrixPrivate.h" 0015 #include "backend/core/Folder.h" 0016 #include "backend/lib/XmlStreamReader.h" 0017 #include "backend/lib/commandtemplates.h" 0018 #include "backend/matrix/MatrixModel.h" 0019 #include "commonfrontend/matrix/MatrixView.h" 0020 #include "kdefrontend/spreadsheet/ExportSpreadsheetDialog.h" 0021 #include "matrixcommands.h" 0022 0023 #include <KConfig> 0024 #include <KConfigGroup> 0025 #include <KLocalizedString> 0026 0027 #include <QHeaderView> 0028 #include <QPrintDialog> 0029 #include <QPrintPreviewDialog> 0030 #include <QPrinter> 0031 0032 /*! 0033 This class manages matrix based data (i.e., mathematically 0034 a MxN matrix with M rows, N columns). This data is typically 0035 used to for 3D plots. 0036 0037 The values of the matrix are stored as generic values. Each column 0038 of the matrix is stored in a QVector<T> objects. 0039 0040 \ingroup backend 0041 */ 0042 Matrix::Matrix(int rows, int cols, const QString& name, const AbstractColumn::ColumnMode mode) 0043 : AbstractDataSource(name, AspectType::Matrix) 0044 , d_ptr(new MatrixPrivate(this, mode)) { 0045 // set initial number of rows and columns 0046 appendColumns(cols); 0047 appendRows(rows); 0048 0049 init(); 0050 } 0051 0052 Matrix::Matrix(const QString& name, bool loading, const AbstractColumn::ColumnMode mode) 0053 : AbstractDataSource(name, AspectType::Matrix) 0054 , d_ptr(new MatrixPrivate(this, mode)) { 0055 if (!loading) 0056 init(); 0057 } 0058 0059 Matrix::~Matrix() { 0060 delete d_ptr; 0061 } 0062 0063 void Matrix::init() { 0064 Q_D(Matrix); 0065 KConfig config; 0066 KConfigGroup group = config.group(QStringLiteral("Matrix")); 0067 0068 // matrix dimension 0069 int rows = group.readEntry(QStringLiteral("RowCount"), 10); 0070 int cols = group.readEntry(QStringLiteral("ColumnCount"), 10); 0071 appendRows(rows); 0072 appendColumns(cols); 0073 0074 // mapping to logical x- and y-coordinates 0075 d->xStart = group.readEntry(QStringLiteral("XStart"), 0.0); 0076 d->xEnd = group.readEntry(QStringLiteral("XEnd"), 1.0); 0077 d->yStart = group.readEntry(QStringLiteral("YStart"), 0.0); 0078 d->yEnd = group.readEntry(QStringLiteral("YEnd"), 1.0); 0079 0080 // format 0081 QByteArray formatba = group.readEntry(QStringLiteral("NumericFormat"), QStringLiteral("f")).toLatin1(); 0082 d->numericFormat = *formatba.data(); 0083 d->precision = group.readEntry(QStringLiteral("Precision"), 3); 0084 d->headerFormat = (Matrix::HeaderFormat)group.readEntry(QStringLiteral("HeaderFormat"), static_cast<int>(HeaderFormat::HeaderRowsColumns)); 0085 } 0086 0087 /*! 0088 Returns an icon to be used for decorating my views. 0089 */ 0090 QIcon Matrix::icon() const { 0091 return QIcon::fromTheme(QStringLiteral("labplot-matrix")); 0092 } 0093 0094 /*! 0095 Returns a new context menu. The caller takes ownership of the menu. 0096 */ 0097 QMenu* Matrix::createContextMenu() { 0098 QMenu* menu = AbstractPart::createContextMenu(); 0099 Q_EMIT requestProjectContextMenu(menu); 0100 return menu; 0101 } 0102 0103 QWidget* Matrix::view() const { 0104 if (!m_partView) { 0105 m_view = new MatrixView(const_cast<Matrix*>(this)); 0106 m_partView = m_view; 0107 m_model = m_view->model(); 0108 connect(this, &Matrix::viewAboutToBeDeleted, [this]() { 0109 m_view = nullptr; 0110 }); 0111 } 0112 return m_partView; 0113 } 0114 0115 bool Matrix::exportView() const { 0116 auto* dlg = new ExportSpreadsheetDialog(m_view); 0117 dlg->setFileName(name()); 0118 dlg->setMatrixMode(true); 0119 0120 // TODO FITS filter to decide if it can be exported to both 0121 dlg->setExportTo(QStringList() << i18n("FITS image") << i18n("FITS table")); 0122 if (m_view->selectedColumnCount() == 0) 0123 dlg->setExportSelection(false); 0124 0125 bool ret; 0126 if ((ret = (dlg->exec() == QDialog::Accepted))) { 0127 const QString path = dlg->path(); 0128 WAIT_CURSOR; 0129 0130 if (dlg->format() == ExportSpreadsheetDialog::Format::LaTeX) { 0131 const bool verticalHeader = dlg->matrixVerticalHeader(); 0132 const bool horizontalHeader = dlg->matrixHorizontalHeader(); 0133 const bool latexHeader = dlg->exportHeader(); 0134 const bool gridLines = dlg->gridLines(); 0135 const bool entire = dlg->entireSpreadheet(); 0136 const bool captions = dlg->captions(); 0137 m_view->exportToLaTeX(path, verticalHeader, horizontalHeader, latexHeader, gridLines, entire, captions); 0138 } else if (dlg->format() == ExportSpreadsheetDialog::Format::FITS) { 0139 const int exportTo = dlg->exportToFits(); 0140 m_view->exportToFits(path, exportTo); 0141 } else { 0142 const QString separator = dlg->separator(); 0143 const QLocale::Language format = dlg->numberFormat(); 0144 m_view->exportToFile(path, separator, format); 0145 } 0146 RESET_CURSOR; 0147 } 0148 delete dlg; 0149 0150 return ret; 0151 } 0152 0153 bool Matrix::printView() { 0154 QPrinter printer; 0155 auto* dlg = new QPrintDialog(&printer, m_view); 0156 bool ret; 0157 dlg->setWindowTitle(i18nc("@title:window", "Print Matrix")); 0158 if ((ret = (dlg->exec() == QDialog::Accepted))) 0159 m_view->print(&printer); 0160 0161 delete dlg; 0162 0163 return ret; 0164 } 0165 0166 bool Matrix::printPreview() const { 0167 auto* dlg = new QPrintPreviewDialog(m_view); 0168 connect(dlg, &QPrintPreviewDialog::paintRequested, m_view, &MatrixView::print); 0169 return dlg->exec(); 0170 } 0171 0172 // ############################################################################## 0173 // ########################## getter methods ################################## 0174 // ############################################################################## 0175 void* Matrix::data() const { 0176 Q_D(const Matrix); 0177 return d->data; 0178 } 0179 0180 BASIC_D_READER_IMPL(Matrix, AbstractColumn::ColumnMode, mode, mode) 0181 BASIC_D_READER_IMPL(Matrix, int, rowCount, rowCount) 0182 BASIC_D_READER_IMPL(Matrix, int, columnCount, columnCount) 0183 BASIC_D_READER_IMPL(Matrix, double, xStart, xStart) 0184 BASIC_D_READER_IMPL(Matrix, double, xEnd, xEnd) 0185 BASIC_D_READER_IMPL(Matrix, double, yStart, yStart) 0186 BASIC_D_READER_IMPL(Matrix, double, yEnd, yEnd) 0187 BASIC_D_READER_IMPL(Matrix, char, numericFormat, numericFormat) 0188 BASIC_D_READER_IMPL(Matrix, int, precision, precision) 0189 BASIC_D_READER_IMPL(Matrix, Matrix::HeaderFormat, headerFormat, headerFormat) 0190 BASIC_D_READER_IMPL(Matrix, QString, formula, formula) 0191 0192 void Matrix::setSuppressDataChangedSignal(bool b) { 0193 if (m_model) 0194 m_model->setSuppressDataChangedSignal(b); 0195 } 0196 0197 void Matrix::setChanged() { 0198 if (m_model) 0199 m_model->setChanged(); 0200 } 0201 0202 // ############################################################################## 0203 // ################# setter methods and undo commands ########################## 0204 // ############################################################################## 0205 void Matrix::setRowCount(int count) { 0206 Q_D(const Matrix); 0207 if (count == d->rowCount) 0208 return; 0209 0210 const int diff = count - d->rowCount; 0211 if (diff > 0) 0212 appendRows(diff); 0213 else if (diff < 0) 0214 removeRows(rowCount() + diff, -diff); 0215 } 0216 0217 void Matrix::setColumnCount(int count) { 0218 Q_D(const Matrix); 0219 if (count == d->columnCount) 0220 return; 0221 0222 const int diff = count - columnCount(); 0223 if (diff > 0) 0224 appendColumns(diff); 0225 else if (diff < 0) 0226 removeColumns(columnCount() + diff, -diff); 0227 } 0228 0229 STD_SETTER_CMD_IMPL_F_S(Matrix, SetXStart, double, xStart, updateViewHeader) 0230 void Matrix::setXStart(double xStart) { 0231 Q_D(Matrix); 0232 if (xStart != d->xStart) 0233 exec(new MatrixSetXStartCmd(d, xStart, ki18n("%1: x-start changed"))); 0234 } 0235 0236 STD_SETTER_CMD_IMPL_F_S(Matrix, SetXEnd, double, xEnd, updateViewHeader) 0237 void Matrix::setXEnd(double xEnd) { 0238 Q_D(Matrix); 0239 if (xEnd != d->xEnd) 0240 exec(new MatrixSetXEndCmd(d, xEnd, ki18n("%1: x-end changed"))); 0241 } 0242 0243 STD_SETTER_CMD_IMPL_F_S(Matrix, SetYStart, double, yStart, updateViewHeader) 0244 void Matrix::setYStart(double yStart) { 0245 Q_D(Matrix); 0246 if (yStart != d->yStart) 0247 exec(new MatrixSetYStartCmd(d, yStart, ki18n("%1: y-start changed"))); 0248 } 0249 0250 STD_SETTER_CMD_IMPL_F_S(Matrix, SetYEnd, double, yEnd, updateViewHeader) 0251 void Matrix::setYEnd(double yEnd) { 0252 Q_D(Matrix); 0253 if (yEnd != d->yEnd) 0254 exec(new MatrixSetYEndCmd(d, yEnd, ki18n("%1: y-end changed"))); 0255 } 0256 0257 STD_SETTER_CMD_IMPL_S(Matrix, SetNumericFormat, char, numericFormat) 0258 void Matrix::setNumericFormat(char format) { 0259 Q_D(Matrix); 0260 if (format != d->numericFormat) 0261 exec(new MatrixSetNumericFormatCmd(d, format, ki18n("%1: numeric format changed"))); 0262 } 0263 0264 STD_SETTER_CMD_IMPL_S(Matrix, SetPrecision, int, precision) 0265 void Matrix::setPrecision(int precision) { 0266 Q_D(Matrix); 0267 if (precision != d->precision) 0268 exec(new MatrixSetPrecisionCmd(d, precision, ki18n("%1: precision changed"))); 0269 } 0270 0271 // TODO: make this undoable? 0272 void Matrix::setHeaderFormat(Matrix::HeaderFormat format) { 0273 Q_D(Matrix); 0274 d->headerFormat = format; 0275 m_model->updateHeader(); 0276 0277 if (m_view) 0278 m_view->resizeHeaders(); 0279 0280 Q_EMIT headerFormatChanged(format); 0281 } 0282 0283 // columns 0284 void Matrix::insertColumns(int before, int count) { 0285 Q_D(Matrix); 0286 if (count < 1 || before < 0 || before > columnCount()) 0287 return; 0288 WAIT_CURSOR; 0289 exec(new MatrixInsertColumnsCmd(d, before, count)); 0290 RESET_CURSOR; 0291 } 0292 0293 void Matrix::appendColumns(int count) { 0294 insertColumns(columnCount(), count); 0295 } 0296 0297 void Matrix::removeColumns(int first, int count) { 0298 Q_D(Matrix); 0299 if (count < 1 || first < 0 || first + count > columnCount()) 0300 return; 0301 WAIT_CURSOR; 0302 switch (d->mode) { 0303 case AbstractColumn::ColumnMode::Double: 0304 exec(new MatrixRemoveColumnsCmd<double>(d, first, count)); 0305 break; 0306 case AbstractColumn::ColumnMode::Text: 0307 exec(new MatrixRemoveColumnsCmd<QString>(d, first, count)); 0308 break; 0309 case AbstractColumn::ColumnMode::Integer: 0310 exec(new MatrixRemoveColumnsCmd<int>(d, first, count)); 0311 break; 0312 case AbstractColumn::ColumnMode::BigInt: 0313 exec(new MatrixRemoveColumnsCmd<qint64>(d, first, count)); 0314 break; 0315 case AbstractColumn::ColumnMode::Day: 0316 case AbstractColumn::ColumnMode::Month: 0317 case AbstractColumn::ColumnMode::DateTime: 0318 exec(new MatrixRemoveColumnsCmd<QDateTime>(d, first, count)); 0319 break; 0320 } 0321 RESET_CURSOR; 0322 } 0323 0324 void Matrix::clearColumn(int c) { 0325 WAIT_CURSOR; 0326 Q_D(Matrix); 0327 switch (d->mode) { 0328 case AbstractColumn::ColumnMode::Double: 0329 exec(new MatrixClearColumnCmd<double>(d, c)); 0330 break; 0331 case AbstractColumn::ColumnMode::Text: 0332 exec(new MatrixClearColumnCmd<QString>(d, c)); 0333 break; 0334 case AbstractColumn::ColumnMode::Integer: 0335 exec(new MatrixClearColumnCmd<int>(d, c)); 0336 break; 0337 case AbstractColumn::ColumnMode::BigInt: 0338 exec(new MatrixClearColumnCmd<qint64>(d, c)); 0339 break; 0340 case AbstractColumn::ColumnMode::Day: 0341 case AbstractColumn::ColumnMode::Month: 0342 case AbstractColumn::ColumnMode::DateTime: 0343 exec(new MatrixClearColumnCmd<QDateTime>(d, c)); 0344 break; 0345 } 0346 RESET_CURSOR; 0347 } 0348 0349 // rows 0350 void Matrix::insertRows(int before, int count) { 0351 Q_D(Matrix); 0352 if (count < 1 || before < 0 || before > rowCount()) 0353 return; 0354 WAIT_CURSOR; 0355 exec(new MatrixInsertRowsCmd(d, before, count)); 0356 RESET_CURSOR; 0357 } 0358 0359 void Matrix::appendRows(int count) { 0360 insertRows(rowCount(), count); 0361 } 0362 0363 void Matrix::removeRows(int first, int count) { 0364 if (count < 1 || first < 0 || first + count > rowCount()) 0365 return; 0366 WAIT_CURSOR; 0367 Q_D(Matrix); 0368 switch (d->mode) { 0369 case AbstractColumn::ColumnMode::Double: 0370 exec(new MatrixRemoveRowsCmd<double>(d, first, count)); 0371 break; 0372 case AbstractColumn::ColumnMode::Text: 0373 exec(new MatrixRemoveRowsCmd<QString>(d, first, count)); 0374 break; 0375 case AbstractColumn::ColumnMode::Integer: 0376 exec(new MatrixRemoveRowsCmd<int>(d, first, count)); 0377 break; 0378 case AbstractColumn::ColumnMode::BigInt: 0379 exec(new MatrixRemoveRowsCmd<qint64>(d, first, count)); 0380 break; 0381 case AbstractColumn::ColumnMode::Day: 0382 case AbstractColumn::ColumnMode::Month: 0383 case AbstractColumn::ColumnMode::DateTime: 0384 exec(new MatrixRemoveRowsCmd<QDateTime>(d, first, count)); 0385 break; 0386 } 0387 RESET_CURSOR; 0388 } 0389 0390 void Matrix::clearRow(int r) { 0391 Q_D(Matrix); 0392 switch (d->mode) { 0393 case AbstractColumn::ColumnMode::Double: 0394 for (int c = 0; c < columnCount(); ++c) 0395 exec(new MatrixSetCellValueCmd<double>(d, r, c, 0.0)); 0396 break; 0397 case AbstractColumn::ColumnMode::Text: 0398 for (int c = 0; c < columnCount(); ++c) 0399 exec(new MatrixSetCellValueCmd<QString>(d, r, c, QString())); 0400 break; 0401 case AbstractColumn::ColumnMode::Integer: 0402 for (int c = 0; c < columnCount(); ++c) 0403 exec(new MatrixSetCellValueCmd<int>(d, r, c, 0)); 0404 break; 0405 case AbstractColumn::ColumnMode::BigInt: 0406 for (int c = 0; c < columnCount(); ++c) 0407 exec(new MatrixSetCellValueCmd<qint64>(d, r, c, 0)); 0408 break; 0409 case AbstractColumn::ColumnMode::Day: 0410 case AbstractColumn::ColumnMode::Month: 0411 case AbstractColumn::ColumnMode::DateTime: 0412 for (int c = 0; c < columnCount(); ++c) 0413 exec(new MatrixSetCellValueCmd<QDateTime>(d, r, c, QDateTime())); 0414 break; 0415 } 0416 } 0417 0418 //! Return the value in the given cell (needs explicit instantiation) 0419 template<typename T> 0420 T Matrix::cell(int row, int col) const { 0421 Q_D(const Matrix); 0422 return d->cell<T>(row, col); 0423 } 0424 template double Matrix::cell<double>(int row, int col) const; 0425 template int Matrix::cell<int>(int row, int col) const; 0426 template qint64 Matrix::cell<qint64>(int row, int col) const; 0427 template QDateTime Matrix::cell<QDateTime>(int row, int col) const; 0428 template QString Matrix::cell<QString>(int row, int col) const; 0429 0430 //! Return the text displayed in the given cell (needs explicit instantiation) 0431 template<typename T> 0432 QString Matrix::text(int row, int col) { 0433 return QLocale().toString(cell<T>(row, col)); 0434 } 0435 template<> 0436 QString Matrix::text<double>(int row, int col) { 0437 Q_D(const Matrix); 0438 return QLocale().toString(cell<double>(row, col), d->numericFormat, d->precision); 0439 } 0440 template<> 0441 QString Matrix::text<QString>(int row, int col) { 0442 return cell<QString>(row, col); 0443 } 0444 template QString Matrix::text<int>(int row, int col); 0445 template QString Matrix::text<qint64>(int row, int col); 0446 template QString Matrix::text<QDateTime>(int row, int col); 0447 0448 //! Set the value of the cell (needs explicit instantiation) 0449 template<typename T> 0450 void Matrix::setCell(int row, int col, T value) { 0451 Q_D(Matrix); 0452 if (row < 0 || row >= rowCount()) 0453 return; 0454 if (col < 0 || col >= columnCount()) 0455 return; 0456 exec(new MatrixSetCellValueCmd<T>(d, row, col, value)); 0457 } 0458 template void Matrix::setCell<double>(int row, int col, double value); 0459 template void Matrix::setCell<int>(int row, int col, int value); 0460 template void Matrix::setCell<qint64>(int row, int col, qint64 value); 0461 template void Matrix::setCell<QString>(int row, int col, QString value); 0462 template void Matrix::setCell<QDateTime>(int row, int col, QDateTime value); 0463 0464 void Matrix::clearCell(int row, int col) { 0465 Q_D(Matrix); 0466 switch (d->mode) { 0467 case AbstractColumn::ColumnMode::Double: 0468 exec(new MatrixSetCellValueCmd<double>(d, row, col, 0.0)); 0469 break; 0470 case AbstractColumn::ColumnMode::Text: 0471 exec(new MatrixSetCellValueCmd<QString>(d, row, col, QString())); 0472 break; 0473 case AbstractColumn::ColumnMode::Integer: 0474 exec(new MatrixSetCellValueCmd<int>(d, row, col, 0)); 0475 break; 0476 case AbstractColumn::ColumnMode::BigInt: 0477 exec(new MatrixSetCellValueCmd<qint64>(d, row, col, 0)); 0478 break; 0479 case AbstractColumn::ColumnMode::Day: 0480 case AbstractColumn::ColumnMode::Month: 0481 case AbstractColumn::ColumnMode::DateTime: 0482 exec(new MatrixSetCellValueCmd<QDateTime>(d, row, col, QDateTime())); 0483 break; 0484 } 0485 } 0486 0487 void Matrix::setDimensions(int rows, int cols) { 0488 if ((rows < 0) || (cols < 0) || (rows == rowCount() && cols == columnCount())) 0489 return; 0490 0491 WAIT_CURSOR; 0492 beginMacro(i18n("%1: set matrix size to %2x%3", name(), rows, cols)); 0493 0494 int col_diff = cols - columnCount(); 0495 if (col_diff > 0) 0496 insertColumns(columnCount(), col_diff); 0497 else if (col_diff < 0) 0498 removeColumns(columnCount() + col_diff, -col_diff); 0499 0500 int row_diff = rows - rowCount(); 0501 if (row_diff > 0) 0502 appendRows(row_diff); 0503 else if (row_diff < 0) 0504 removeRows(rowCount() + row_diff, -row_diff); 0505 0506 endMacro(); 0507 RESET_CURSOR; 0508 } 0509 0510 void Matrix::copy(Matrix* other) { 0511 WAIT_CURSOR; 0512 Q_D(Matrix); 0513 beginMacro(i18n("%1: copy %2", name(), other->name())); 0514 0515 int rows = other->rowCount(); 0516 int columns = other->columnCount(); 0517 setDimensions(rows, columns); 0518 0519 for (int i = 0; i < rows; i++) 0520 setRowHeight(i, other->rowHeight(i)); 0521 0522 for (int i = 0; i < columns; i++) 0523 setColumnWidth(i, other->columnWidth(i)); 0524 0525 d->suppressDataChange = true; 0526 switch (d->mode) { 0527 case AbstractColumn::ColumnMode::Double: 0528 for (int i = 0; i < columns; i++) 0529 setColumnCells(i, 0, rows - 1, other->columnCells<double>(i, 0, rows - 1)); 0530 break; 0531 case AbstractColumn::ColumnMode::Text: 0532 for (int i = 0; i < columns; i++) 0533 setColumnCells(i, 0, rows - 1, other->columnCells<QString>(i, 0, rows - 1)); 0534 break; 0535 case AbstractColumn::ColumnMode::Integer: 0536 for (int i = 0; i < columns; i++) 0537 setColumnCells(i, 0, rows - 1, other->columnCells<int>(i, 0, rows - 1)); 0538 break; 0539 case AbstractColumn::ColumnMode::BigInt: 0540 for (int i = 0; i < columns; i++) 0541 setColumnCells(i, 0, rows - 1, other->columnCells<qint64>(i, 0, rows - 1)); 0542 break; 0543 case AbstractColumn::ColumnMode::Day: 0544 case AbstractColumn::ColumnMode::Month: 0545 case AbstractColumn::ColumnMode::DateTime: 0546 for (int i = 0; i < columns; i++) 0547 setColumnCells(i, 0, rows - 1, other->columnCells<QDateTime>(i, 0, rows - 1)); 0548 break; 0549 } 0550 0551 setCoordinates(other->xStart(), other->xEnd(), other->yStart(), other->yEnd()); 0552 setNumericFormat(other->numericFormat()); 0553 setPrecision(other->precision()); 0554 d->formula = other->formula(); 0555 d->suppressDataChange = false; 0556 Q_EMIT dataChanged(0, 0, rows - 1, columns - 1); 0557 if (m_view) 0558 m_view->adjustHeaders(); 0559 0560 endMacro(); 0561 RESET_CURSOR; 0562 } 0563 0564 //! Duplicate the matrix inside its folder 0565 void Matrix::duplicate() { 0566 Matrix* matrix = new Matrix(rowCount(), columnCount(), name()); 0567 matrix->copy(this); 0568 if (folder()) 0569 folder()->addChild(matrix); 0570 } 0571 0572 void Matrix::addRows() { 0573 Q_D(Matrix); 0574 if (!m_view) 0575 return; 0576 WAIT_CURSOR; 0577 int count = m_view->selectedRowCount(false); 0578 beginMacro(i18np("%1: add %2 row", "%1: add %2 rows", name(), count)); 0579 exec(new MatrixInsertRowsCmd(d, rowCount(), count)); 0580 endMacro(); 0581 RESET_CURSOR; 0582 } 0583 0584 void Matrix::addColumns() { 0585 Q_D(Matrix); 0586 if (!m_view) 0587 return; 0588 WAIT_CURSOR; 0589 int count = m_view->selectedRowCount(false); 0590 beginMacro(i18np("%1: add %2 column", "%1: add %2 columns", name(), count)); 0591 exec(new MatrixInsertColumnsCmd(d, columnCount(), count)); 0592 endMacro(); 0593 RESET_CURSOR; 0594 } 0595 0596 void Matrix::setCoordinates(double x1, double x2, double y1, double y2) { 0597 Q_D(Matrix); 0598 exec(new MatrixSetCoordinatesCmd(d, x1, x2, y1, y2)); 0599 } 0600 0601 void Matrix::setFormula(const QString& formula) { 0602 Q_D(Matrix); 0603 exec(new MatrixSetFormulaCmd(d, formula)); 0604 } 0605 0606 //! This method should only be called by the view. 0607 /** This method does not change the view, it only changes the 0608 * values that are saved when the matrix is saved. The view 0609 * has to take care of reading and applying these values */ 0610 void Matrix::setRowHeight(int row, int height) { 0611 Q_D(Matrix); 0612 d->setRowHeight(row, height); 0613 } 0614 0615 //! This method should only be called by the view. 0616 /** This method does not change the view, it only changes the 0617 * values that are saved when the matrix is saved. The view 0618 * has to take care of reading and applying these values */ 0619 void Matrix::setColumnWidth(int col, int width) { 0620 Q_D(Matrix); 0621 d->setColumnWidth(col, width); 0622 } 0623 0624 int Matrix::rowHeight(int row) const { 0625 Q_D(const Matrix); 0626 return d->rowHeight(row); 0627 } 0628 0629 int Matrix::columnWidth(int col) const { 0630 Q_D(const Matrix); 0631 return d->columnWidth(col); 0632 } 0633 0634 //! Return the values in the given cells as vector 0635 template<typename T> 0636 QVector<T> Matrix::columnCells(int col, int first_row, int last_row) { 0637 Q_D(const Matrix); 0638 return d->columnCells<T>(col, first_row, last_row); 0639 } 0640 0641 //! Set the values in the given cells from a type T vector 0642 template<typename T> 0643 void Matrix::setColumnCells(int col, int first_row, int last_row, const QVector<T>& values) { 0644 WAIT_CURSOR; 0645 Q_D(Matrix); 0646 exec(new MatrixSetColumnCellsCmd<T>(d, col, first_row, last_row, values)); 0647 RESET_CURSOR; 0648 } 0649 0650 //! Return the values in the given cells as vector (needs explicit instantiation) 0651 template<typename T> 0652 QVector<T> Matrix::rowCells(int row, int first_column, int last_column) { 0653 Q_D(const Matrix); 0654 return d->rowCells<T>(row, first_column, last_column); 0655 } 0656 template QVector<double> Matrix::rowCells<double>(int row, int first_column, int last_column); 0657 template QVector<QString> Matrix::rowCells<QString>(int row, int first_column, int last_column); 0658 template QVector<int> Matrix::rowCells<int>(int row, int first_column, int last_column); 0659 template QVector<QDateTime> Matrix::rowCells<QDateTime>(int row, int first_column, int last_column); 0660 0661 //! Set the values in the given cells from a type T vector 0662 template<typename T> 0663 void Matrix::setRowCells(int row, int first_column, int last_column, const QVector<T>& values) { 0664 WAIT_CURSOR; 0665 Q_D(Matrix); 0666 exec(new MatrixSetRowCellsCmd<T>(d, row, first_column, last_column, values)); 0667 RESET_CURSOR; 0668 } 0669 0670 void Matrix::setData(void* data) { 0671 bool isEmpty = false; 0672 Q_D(Matrix); 0673 switch (d->mode) { 0674 case AbstractColumn::ColumnMode::Double: 0675 if (static_cast<QVector<QVector<double>>*>(data)->isEmpty()) 0676 isEmpty = true; 0677 break; 0678 case AbstractColumn::ColumnMode::Text: 0679 if (static_cast<QVector<QVector<QString>>*>(data)->isEmpty()) 0680 isEmpty = true; 0681 break; 0682 case AbstractColumn::ColumnMode::Integer: 0683 if (static_cast<QVector<QVector<int>>*>(data)->isEmpty()) 0684 isEmpty = true; 0685 break; 0686 case AbstractColumn::ColumnMode::BigInt: 0687 if (static_cast<QVector<QVector<qint64>>*>(data)->isEmpty()) 0688 isEmpty = true; 0689 break; 0690 case AbstractColumn::ColumnMode::Day: 0691 case AbstractColumn::ColumnMode::Month: 0692 case AbstractColumn::ColumnMode::DateTime: 0693 if (static_cast<QVector<QVector<QDateTime>>*>(data)->isEmpty()) 0694 isEmpty = true; 0695 break; 0696 } 0697 0698 if (!isEmpty) 0699 exec(new MatrixReplaceValuesCmd(d, data)); 0700 } 0701 0702 QVector<AspectType> Matrix::dropableOn() const { 0703 auto vec = AbstractPart::dropableOn(); 0704 vec << AspectType::Workbook; 0705 return vec; 0706 } 0707 0708 // ############################################################################## 0709 // ######################### Public slots ##################################### 0710 // ############################################################################## 0711 //! Clear the whole matrix (i.e. reset all cells) 0712 void Matrix::clear() { 0713 WAIT_CURSOR; 0714 Q_D(Matrix); 0715 beginMacro(i18n("%1: clear", name())); 0716 switch (d->mode) { 0717 case AbstractColumn::ColumnMode::Double: 0718 exec(new MatrixClearCmd<double>(d)); 0719 break; 0720 case AbstractColumn::ColumnMode::Text: 0721 exec(new MatrixClearCmd<QString>(d)); 0722 break; 0723 case AbstractColumn::ColumnMode::Integer: 0724 exec(new MatrixClearCmd<int>(d)); 0725 break; 0726 case AbstractColumn::ColumnMode::BigInt: 0727 exec(new MatrixClearCmd<qint64>(d)); 0728 break; 0729 case AbstractColumn::ColumnMode::Day: 0730 case AbstractColumn::ColumnMode::Month: 0731 case AbstractColumn::ColumnMode::DateTime: 0732 exec(new MatrixClearCmd<QDateTime>(d)); 0733 break; 0734 } 0735 endMacro(); 0736 RESET_CURSOR; 0737 } 0738 0739 void Matrix::transpose() { 0740 WAIT_CURSOR; 0741 Q_D(Matrix); 0742 switch (d->mode) { 0743 case AbstractColumn::ColumnMode::Double: 0744 exec(new MatrixTransposeCmd<double>(d)); 0745 break; 0746 case AbstractColumn::ColumnMode::Text: 0747 exec(new MatrixTransposeCmd<QString>(d)); 0748 break; 0749 case AbstractColumn::ColumnMode::Integer: 0750 exec(new MatrixTransposeCmd<int>(d)); 0751 break; 0752 case AbstractColumn::ColumnMode::BigInt: 0753 exec(new MatrixTransposeCmd<qint64>(d)); 0754 break; 0755 case AbstractColumn::ColumnMode::Day: 0756 case AbstractColumn::ColumnMode::Month: 0757 case AbstractColumn::ColumnMode::DateTime: 0758 exec(new MatrixTransposeCmd<QDateTime>(d)); 0759 break; 0760 } 0761 RESET_CURSOR; 0762 } 0763 0764 void Matrix::mirrorHorizontally() { 0765 WAIT_CURSOR; 0766 Q_D(Matrix); 0767 switch (d->mode) { 0768 case AbstractColumn::ColumnMode::Double: 0769 exec(new MatrixMirrorHorizontallyCmd<double>(d)); 0770 break; 0771 case AbstractColumn::ColumnMode::Text: 0772 exec(new MatrixMirrorHorizontallyCmd<QString>(d)); 0773 break; 0774 case AbstractColumn::ColumnMode::Integer: 0775 exec(new MatrixMirrorHorizontallyCmd<int>(d)); 0776 break; 0777 case AbstractColumn::ColumnMode::BigInt: 0778 exec(new MatrixMirrorHorizontallyCmd<qint64>(d)); 0779 break; 0780 case AbstractColumn::ColumnMode::Day: 0781 case AbstractColumn::ColumnMode::Month: 0782 case AbstractColumn::ColumnMode::DateTime: 0783 exec(new MatrixMirrorHorizontallyCmd<QDateTime>(d)); 0784 break; 0785 } 0786 RESET_CURSOR; 0787 } 0788 0789 void Matrix::mirrorVertically() { 0790 WAIT_CURSOR; 0791 Q_D(Matrix); 0792 switch (d->mode) { 0793 case AbstractColumn::ColumnMode::Double: 0794 exec(new MatrixMirrorVerticallyCmd<double>(d)); 0795 break; 0796 case AbstractColumn::ColumnMode::Text: 0797 exec(new MatrixMirrorVerticallyCmd<QString>(d)); 0798 break; 0799 case AbstractColumn::ColumnMode::Integer: 0800 exec(new MatrixMirrorVerticallyCmd<int>(d)); 0801 break; 0802 case AbstractColumn::ColumnMode::BigInt: 0803 exec(new MatrixMirrorVerticallyCmd<qint64>(d)); 0804 break; 0805 case AbstractColumn::ColumnMode::Day: 0806 case AbstractColumn::ColumnMode::Month: 0807 case AbstractColumn::ColumnMode::DateTime: 0808 exec(new MatrixMirrorVerticallyCmd<QDateTime>(d)); 0809 break; 0810 } 0811 RESET_CURSOR; 0812 } 0813 0814 // ############################################################################## 0815 // ###################### Private implementation ############################### 0816 // ############################################################################## 0817 0818 MatrixPrivate::MatrixPrivate(Matrix* owner, const AbstractColumn::ColumnMode m) 0819 : q(owner) 0820 , data(nullptr) 0821 , mode(m) 0822 , rowCount(0) 0823 , columnCount(0) 0824 , suppressDataChange(false) { 0825 switch (mode) { 0826 case AbstractColumn::ColumnMode::Double: 0827 data = new QVector<QVector<double>>(); 0828 break; 0829 case AbstractColumn::ColumnMode::Text: 0830 data = new QVector<QVector<QString>>(); 0831 break; 0832 case AbstractColumn::ColumnMode::Integer: 0833 data = new QVector<QVector<int>>(); 0834 break; 0835 case AbstractColumn::ColumnMode::BigInt: 0836 data = new QVector<QVector<qint64>>(); 0837 break; 0838 case AbstractColumn::ColumnMode::Month: 0839 case AbstractColumn::ColumnMode::Day: 0840 case AbstractColumn::ColumnMode::DateTime: 0841 data = new QVector<QVector<QDateTime>>(); 0842 break; 0843 } 0844 } 0845 0846 MatrixPrivate::~MatrixPrivate() { 0847 if (data) { 0848 switch (mode) { 0849 case AbstractColumn::ColumnMode::Double: 0850 delete static_cast<QVector<QVector<double>>*>(data); 0851 break; 0852 case AbstractColumn::ColumnMode::Text: 0853 delete static_cast<QVector<QVector<QString>>*>(data); 0854 break; 0855 case AbstractColumn::ColumnMode::Integer: 0856 delete static_cast<QVector<QVector<int>>*>(data); 0857 break; 0858 case AbstractColumn::ColumnMode::BigInt: 0859 delete static_cast<QVector<QVector<qint64>>*>(data); 0860 break; 0861 case AbstractColumn::ColumnMode::Day: 0862 case AbstractColumn::ColumnMode::Month: 0863 case AbstractColumn::ColumnMode::DateTime: 0864 delete static_cast<QVector<QVector<QDateTime>>*>(data); 0865 break; 0866 } 0867 } 0868 } 0869 0870 void MatrixPrivate::updateViewHeader() { 0871 q->m_view->model()->updateHeader(); 0872 } 0873 0874 /*! 0875 Insert \p count columns before column number \c before 0876 */ 0877 void MatrixPrivate::insertColumns(int before, int count) { 0878 Q_ASSERT(before >= 0); 0879 Q_ASSERT(before <= columnCount); 0880 0881 Q_EMIT q->columnsAboutToBeInserted(before, count); 0882 switch (mode) { 0883 case AbstractColumn::ColumnMode::Double: 0884 for (int i = 0; i < count; i++) { 0885 static_cast<QVector<QVector<double>>*>(data)->insert(before + i, QVector<double>(rowCount)); 0886 columnWidths.insert(before + i, 0); 0887 } 0888 break; 0889 case AbstractColumn::ColumnMode::Text: 0890 for (int i = 0; i < count; i++) { 0891 static_cast<QVector<QVector<QString>>*>(data)->insert(before + i, QVector<QString>(rowCount)); 0892 columnWidths.insert(before + i, 0); 0893 } 0894 break; 0895 case AbstractColumn::ColumnMode::Integer: 0896 for (int i = 0; i < count; i++) { 0897 static_cast<QVector<QVector<int>>*>(data)->insert(before + i, QVector<int>(rowCount)); 0898 columnWidths.insert(before + i, 0); 0899 } 0900 break; 0901 case AbstractColumn::ColumnMode::BigInt: 0902 for (int i = 0; i < count; i++) { 0903 static_cast<QVector<QVector<qint64>>*>(data)->insert(before + i, QVector<qint64>(rowCount)); 0904 columnWidths.insert(before + i, 0); 0905 } 0906 break; 0907 case AbstractColumn::ColumnMode::Day: 0908 case AbstractColumn::ColumnMode::Month: 0909 case AbstractColumn::ColumnMode::DateTime: 0910 for (int i = 0; i < count; i++) { 0911 static_cast<QVector<QVector<QDateTime>>*>(data)->insert(before + i, QVector<QDateTime>(rowCount)); 0912 columnWidths.insert(before + i, 0); 0913 } 0914 break; 0915 } 0916 0917 columnCount += count; 0918 Q_EMIT q->columnsInserted(before, count); 0919 } 0920 0921 /*! 0922 Remove \c count columns starting with column index \c first 0923 */ 0924 void MatrixPrivate::removeColumns(int first, int count) { 0925 Q_EMIT q->columnsAboutToBeRemoved(first, count); 0926 Q_ASSERT(first >= 0); 0927 Q_ASSERT(first + count <= columnCount); 0928 0929 switch (mode) { 0930 case AbstractColumn::ColumnMode::Double: 0931 (static_cast<QVector<QVector<double>>*>(data))->remove(first, count); 0932 break; 0933 case AbstractColumn::ColumnMode::Text: 0934 (static_cast<QVector<QVector<QString>>*>(data))->remove(first, count); 0935 break; 0936 case AbstractColumn::ColumnMode::Integer: 0937 (static_cast<QVector<QVector<int>>*>(data))->remove(first, count); 0938 break; 0939 case AbstractColumn::ColumnMode::BigInt: 0940 (static_cast<QVector<QVector<qint64>>*>(data))->remove(first, count); 0941 break; 0942 case AbstractColumn::ColumnMode::Day: 0943 case AbstractColumn::ColumnMode::Month: 0944 case AbstractColumn::ColumnMode::DateTime: 0945 (static_cast<QVector<QVector<QDateTime>>*>(data))->remove(first, count); 0946 break; 0947 } 0948 0949 for (int i = 0; i < count; i++) 0950 columnWidths.remove(first); 0951 columnCount -= count; 0952 Q_EMIT q->columnsRemoved(first, count); 0953 } 0954 0955 /*! 0956 Insert \c count rows before row with the index \c before 0957 */ 0958 void MatrixPrivate::insertRows(int before, int count) { 0959 Q_EMIT q->rowsAboutToBeInserted(before, count); 0960 Q_ASSERT(before >= 0); 0961 Q_ASSERT(before <= rowCount); 0962 0963 switch (mode) { 0964 case AbstractColumn::ColumnMode::Double: 0965 for (int col = 0; col < columnCount; col++) 0966 for (int i = 0; i < count; i++) 0967 (static_cast<QVector<QVector<double>>*>(data))->operator[](col).insert(before + i, 0.0); 0968 break; 0969 case AbstractColumn::ColumnMode::Text: 0970 for (int col = 0; col < columnCount; col++) 0971 for (int i = 0; i < count; i++) 0972 (static_cast<QVector<QVector<QString>>*>(data))->operator[](col).insert(before + i, QString()); 0973 break; 0974 case AbstractColumn::ColumnMode::Integer: 0975 for (int col = 0; col < columnCount; col++) 0976 for (int i = 0; i < count; i++) 0977 (static_cast<QVector<QVector<int>>*>(data))->operator[](col).insert(before + i, 0); 0978 break; 0979 case AbstractColumn::ColumnMode::BigInt: 0980 for (int col = 0; col < columnCount; col++) 0981 for (int i = 0; i < count; i++) 0982 (static_cast<QVector<QVector<qint64>>*>(data))->operator[](col).insert(before + i, 0); 0983 break; 0984 case AbstractColumn::ColumnMode::Day: 0985 case AbstractColumn::ColumnMode::Month: 0986 case AbstractColumn::ColumnMode::DateTime: 0987 for (int col = 0; col < columnCount; col++) 0988 for (int i = 0; i < count; i++) 0989 (static_cast<QVector<QVector<QDateTime>>*>(data))->operator[](col).insert(before + i, QDateTime()); 0990 } 0991 0992 for (int i = 0; i < count; i++) 0993 rowHeights.insert(before + i, 0); 0994 0995 rowCount += count; 0996 Q_EMIT q->rowsInserted(before, count); 0997 } 0998 0999 /*! 1000 Remove \c count columns starting from the column with index \c first 1001 */ 1002 void MatrixPrivate::removeRows(int first, int count) { 1003 Q_EMIT q->rowsAboutToBeRemoved(first, count); 1004 Q_ASSERT(first >= 0); 1005 Q_ASSERT(first + count <= rowCount); 1006 1007 switch (mode) { 1008 case AbstractColumn::ColumnMode::Double: 1009 for (int col = 0; col < columnCount; col++) 1010 (static_cast<QVector<QVector<double>>*>(data))->operator[](col).remove(first, count); 1011 break; 1012 case AbstractColumn::ColumnMode::Text: 1013 for (int col = 0; col < columnCount; col++) 1014 (static_cast<QVector<QVector<QString>>*>(data))->operator[](col).remove(first, count); 1015 break; 1016 case AbstractColumn::ColumnMode::Integer: 1017 for (int col = 0; col < columnCount; col++) 1018 (static_cast<QVector<QVector<int>>*>(data))->operator[](col).remove(first, count); 1019 break; 1020 case AbstractColumn::ColumnMode::BigInt: 1021 for (int col = 0; col < columnCount; col++) 1022 (static_cast<QVector<QVector<qint64>>*>(data))->operator[](col).remove(first, count); 1023 break; 1024 case AbstractColumn::ColumnMode::Day: 1025 case AbstractColumn::ColumnMode::Month: 1026 case AbstractColumn::ColumnMode::DateTime: 1027 for (int col = 0; col < columnCount; col++) 1028 (static_cast<QVector<QVector<QDateTime>>*>(data))->operator[](col).remove(first, count); 1029 break; 1030 } 1031 1032 for (int i = 0; i < count; i++) 1033 rowHeights.remove(first); 1034 1035 rowCount -= count; 1036 Q_EMIT q->rowsRemoved(first, count); 1037 } 1038 1039 //! Fill column with zeroes 1040 void MatrixPrivate::clearColumn(int col) { 1041 switch (mode) { 1042 case AbstractColumn::ColumnMode::Double: 1043 static_cast<QVector<QVector<double>>*>(data)->operator[](col).fill(0.0); 1044 break; 1045 case AbstractColumn::ColumnMode::Text: 1046 static_cast<QVector<QVector<QString>>*>(data)->operator[](col).fill(QString()); 1047 break; 1048 case AbstractColumn::ColumnMode::Integer: 1049 static_cast<QVector<QVector<int>>*>(data)->operator[](col).fill(0); 1050 break; 1051 case AbstractColumn::ColumnMode::BigInt: 1052 static_cast<QVector<QVector<qint64>>*>(data)->operator[](col).fill(0); 1053 break; 1054 case AbstractColumn::ColumnMode::Day: 1055 case AbstractColumn::ColumnMode::Month: 1056 case AbstractColumn::ColumnMode::DateTime: 1057 static_cast<QVector<QVector<QDateTime>>*>(data)->operator[](col).fill(QDateTime()); 1058 break; 1059 } 1060 1061 if (!suppressDataChange) 1062 Q_EMIT q->dataChanged(0, col, rowCount - 1, col); 1063 } 1064 1065 // ############################################################################## 1066 // ################## Serialization/Deserialization ########################### 1067 // ############################################################################## 1068 void Matrix::save(QXmlStreamWriter* writer) const { 1069 Q_D(const Matrix); 1070 DEBUG(Q_FUNC_INFO); 1071 writer->writeStartElement(QStringLiteral("matrix")); 1072 writeBasicAttributes(writer); 1073 writeCommentElement(writer); 1074 1075 // formula 1076 writer->writeStartElement(QStringLiteral("formula")); 1077 writer->writeCharacters(d->formula); 1078 writer->writeEndElement(); 1079 1080 // format 1081 writer->writeStartElement(QStringLiteral("format")); 1082 writer->writeAttribute(QStringLiteral("mode"), QString::number(static_cast<int>(d->mode))); 1083 writer->writeAttribute(QStringLiteral("headerFormat"), QString::number(static_cast<int>(d->headerFormat))); 1084 writer->writeAttribute(QStringLiteral("numericFormat"), QChar::fromLatin1(d->numericFormat)); 1085 writer->writeAttribute(QStringLiteral("precision"), QString::number(d->precision)); 1086 writer->writeEndElement(); 1087 1088 // dimensions 1089 writer->writeStartElement(QStringLiteral("dimension")); 1090 writer->writeAttribute(QStringLiteral("columns"), QString::number(d->columnCount)); 1091 writer->writeAttribute(QStringLiteral("rows"), QString::number(d->rowCount)); 1092 writer->writeAttribute(QStringLiteral("x_start"), QString::number(d->xStart)); 1093 writer->writeAttribute(QStringLiteral("x_end"), QString::number(d->xEnd)); 1094 writer->writeAttribute(QStringLiteral("y_start"), QString::number(d->yStart)); 1095 writer->writeAttribute(QStringLiteral("y_end"), QString::number(d->yEnd)); 1096 writer->writeEndElement(); 1097 1098 // vector with row heights 1099 writer->writeStartElement(QStringLiteral("row_heights")); 1100 const char* data = reinterpret_cast<const char*>(d->rowHeights.constData()); 1101 int size = d->rowHeights.size() * sizeof(int); 1102 writer->writeCharacters(QLatin1String(QByteArray::fromRawData(data, size).toBase64())); 1103 writer->writeEndElement(); 1104 1105 // vector with column widths 1106 writer->writeStartElement(QStringLiteral("column_widths")); 1107 data = reinterpret_cast<const char*>(d->columnWidths.constData()); 1108 size = d->columnWidths.size() * sizeof(int); 1109 writer->writeCharacters(QLatin1String(QByteArray::fromRawData(data, size).toBase64())); 1110 writer->writeEndElement(); 1111 1112 // columns 1113 DEBUG(" mode = " << static_cast<int>(d->mode)) 1114 switch (d->mode) { 1115 case AbstractColumn::ColumnMode::Double: 1116 size = d->rowCount * sizeof(double); 1117 for (int i = 0; i < d->columnCount; ++i) { 1118 data = reinterpret_cast<const char*>(static_cast<QVector<QVector<double>>*>(d->data)->at(i).constData()); 1119 writer->writeStartElement(QStringLiteral("column")); 1120 writer->writeCharacters(QLatin1String(QByteArray::fromRawData(data, size).toBase64())); 1121 writer->writeEndElement(); 1122 } 1123 break; 1124 case AbstractColumn::ColumnMode::Text: 1125 size = d->rowCount * sizeof(QString); 1126 for (int i = 0; i < d->columnCount; ++i) { 1127 QDEBUG(" string: " << static_cast<QVector<QVector<QString>>*>(d->data)->at(i)); 1128 data = reinterpret_cast<const char*>(static_cast<QVector<QVector<QString>>*>(d->data)->at(i).constData()); 1129 writer->writeStartElement(QStringLiteral("column")); 1130 writer->writeCharacters(QLatin1String(QByteArray::fromRawData(data, size).toBase64())); 1131 writer->writeEndElement(); 1132 } 1133 break; 1134 case AbstractColumn::ColumnMode::Integer: 1135 size = d->rowCount * sizeof(int); 1136 for (int i = 0; i < d->columnCount; ++i) { 1137 data = reinterpret_cast<const char*>(static_cast<QVector<QVector<int>>*>(d->data)->at(i).constData()); 1138 writer->writeStartElement(QStringLiteral("column")); 1139 writer->writeCharacters(QLatin1String(QByteArray::fromRawData(data, size).toBase64())); 1140 writer->writeEndElement(); 1141 } 1142 break; 1143 case AbstractColumn::ColumnMode::BigInt: 1144 size = d->rowCount * sizeof(qint64); 1145 for (int i = 0; i < d->columnCount; ++i) { 1146 data = reinterpret_cast<const char*>(static_cast<QVector<QVector<qint64>>*>(d->data)->at(i).constData()); 1147 writer->writeStartElement(QStringLiteral("column")); 1148 writer->writeCharacters(QLatin1String(QByteArray::fromRawData(data, size).toBase64())); 1149 writer->writeEndElement(); 1150 } 1151 break; 1152 case AbstractColumn::ColumnMode::Day: 1153 case AbstractColumn::ColumnMode::Month: 1154 case AbstractColumn::ColumnMode::DateTime: 1155 size = d->rowCount * sizeof(QDateTime); 1156 for (int i = 0; i < d->columnCount; ++i) { 1157 data = reinterpret_cast<const char*>(static_cast<QVector<QVector<QDateTime>>*>(d->data)->at(i).constData()); 1158 writer->writeStartElement(QStringLiteral("column")); 1159 writer->writeCharacters(QLatin1String(QByteArray::fromRawData(data, size).toBase64())); 1160 writer->writeEndElement(); 1161 } 1162 break; 1163 } 1164 1165 writer->writeEndElement(); // "matrix" 1166 } 1167 1168 bool Matrix::load(XmlStreamReader* reader, bool preview) { 1169 DEBUG(Q_FUNC_INFO) 1170 if (!readBasicAttributes(reader)) 1171 return false; 1172 1173 Q_D(Matrix); 1174 QXmlStreamAttributes attribs; 1175 QString str; 1176 1177 // read child elements 1178 while (!reader->atEnd()) { 1179 reader->readNext(); 1180 1181 if (reader->isEndElement() && reader->name() == QStringLiteral("matrix")) 1182 break; 1183 1184 if (!reader->isStartElement()) 1185 continue; 1186 1187 if (reader->name() == QLatin1String("comment")) { 1188 if (!readCommentElement(reader)) 1189 return false; 1190 } else if (!preview && reader->name() == QLatin1String("formula")) { 1191 d->formula = reader->text().toString().trimmed(); 1192 } else if (!preview && reader->name() == QLatin1String("format")) { 1193 attribs = reader->attributes(); 1194 1195 str = attribs.value(QStringLiteral("mode")).toString(); 1196 if (str.isEmpty()) 1197 reader->raiseMissingAttributeWarning(QStringLiteral("mode")); 1198 else 1199 d->mode = AbstractColumn::ColumnMode(str.toInt()); 1200 1201 str = attribs.value(QStringLiteral("headerFormat")).toString(); 1202 if (str.isEmpty()) 1203 reader->raiseMissingAttributeWarning(QStringLiteral("headerFormat")); 1204 else 1205 d->headerFormat = Matrix::HeaderFormat(str.toInt()); 1206 1207 str = attribs.value(QStringLiteral("numericFormat")).toString(); 1208 if (str.isEmpty()) 1209 reader->raiseMissingAttributeWarning(QStringLiteral("numericFormat")); 1210 else { 1211 QByteArray formatba = str.toLatin1(); 1212 d->numericFormat = *formatba.data(); 1213 } 1214 1215 str = attribs.value(QStringLiteral("precision")).toString(); 1216 if (str.isEmpty()) 1217 reader->raiseMissingAttributeWarning(QStringLiteral("precision")); 1218 else 1219 d->precision = str.toInt(); 1220 1221 } else if (!preview && reader->name() == QLatin1String("dimension")) { 1222 attribs = reader->attributes(); 1223 1224 str = attribs.value(QStringLiteral("columns")).toString(); 1225 if (str.isEmpty()) 1226 reader->raiseMissingAttributeWarning(QStringLiteral("columns")); 1227 else 1228 d->columnCount = str.toInt(); 1229 1230 str = attribs.value(QStringLiteral("rows")).toString(); 1231 if (str.isEmpty()) 1232 reader->raiseMissingAttributeWarning(QStringLiteral("rows")); 1233 else 1234 d->rowCount = str.toInt(); 1235 1236 str = attribs.value(QStringLiteral("x_start")).toString(); 1237 if (str.isEmpty()) 1238 reader->raiseMissingAttributeWarning(QStringLiteral("x_start")); 1239 else 1240 d->xStart = str.toDouble(); 1241 1242 str = attribs.value(QStringLiteral("x_end")).toString(); 1243 if (str.isEmpty()) 1244 reader->raiseMissingAttributeWarning(QStringLiteral("x_end")); 1245 else 1246 d->xEnd = str.toDouble(); 1247 1248 str = attribs.value(QStringLiteral("y_start")).toString(); 1249 if (str.isEmpty()) 1250 reader->raiseMissingAttributeWarning(QStringLiteral("y_start")); 1251 else 1252 d->yStart = str.toDouble(); 1253 1254 str = attribs.value(QStringLiteral("y_end")).toString(); 1255 if (str.isEmpty()) 1256 reader->raiseMissingAttributeWarning(QStringLiteral("y_end")); 1257 else 1258 d->yEnd = str.toDouble(); 1259 } else if (!preview && reader->name() == QLatin1String("row_heights")) { 1260 reader->readNext(); 1261 QString content = reader->text().toString().trimmed(); 1262 QByteArray bytes = QByteArray::fromBase64(content.toLatin1()); 1263 int count = bytes.size() / sizeof(int); 1264 d->rowHeights.resize(count); 1265 memcpy(d->rowHeights.data(), bytes.data(), count * sizeof(int)); 1266 } else if (!preview && reader->name() == QLatin1String("column_widths")) { 1267 reader->readNext(); 1268 QString content = reader->text().toString().trimmed(); 1269 QByteArray bytes = QByteArray::fromBase64(content.toLatin1()); 1270 int count = bytes.size() / sizeof(int); 1271 d->columnWidths.resize(count); 1272 memcpy(d->columnWidths.data(), bytes.data(), count * sizeof(int)); 1273 } else if (!preview && reader->name() == QLatin1String("column")) { 1274 // TODO: parallelize reading of columns? 1275 reader->readNext(); 1276 QString content = reader->text().toString().trimmed(); 1277 QByteArray bytes = QByteArray::fromBase64(content.toLatin1()); 1278 1279 switch (d->mode) { 1280 case AbstractColumn::ColumnMode::Double: { 1281 int count = bytes.size() / sizeof(double); 1282 QVector<double> column; 1283 column.resize(count); 1284 memcpy(column.data(), bytes.data(), count * sizeof(double)); 1285 static_cast<QVector<QVector<double>>*>(d->data)->append(column); 1286 break; 1287 } 1288 case AbstractColumn::ColumnMode::Text: { 1289 int count = bytes.size() / sizeof(char); 1290 QVector<QString> column; 1291 column.resize(count); 1292 // TODO: warning (GCC8): writing to an object of type 'class QString' with no trivial copy-assignment; use copy-assignment or 1293 // copy-initialization instead memcpy(column.data(), bytes.data(), count*sizeof(QString)); QDEBUG(" string: " << column.data()); 1294 static_cast<QVector<QVector<QString>>*>(d->data)->append(column); 1295 break; 1296 } 1297 case AbstractColumn::ColumnMode::Integer: { 1298 int count = bytes.size() / sizeof(int); 1299 QVector<int> column; 1300 column.resize(count); 1301 memcpy(column.data(), bytes.data(), count * sizeof(int)); 1302 static_cast<QVector<QVector<int>>*>(d->data)->append(column); 1303 break; 1304 } 1305 case AbstractColumn::ColumnMode::BigInt: { 1306 int count = bytes.size() / sizeof(qint64); 1307 QVector<qint64> column; 1308 column.resize(count); 1309 memcpy(column.data(), bytes.data(), count * sizeof(qint64)); 1310 static_cast<QVector<QVector<qint64>>*>(d->data)->append(column); 1311 break; 1312 } 1313 case AbstractColumn::ColumnMode::Day: 1314 case AbstractColumn::ColumnMode::Month: 1315 case AbstractColumn::ColumnMode::DateTime: { 1316 int count = bytes.size() / sizeof(QDateTime); 1317 QVector<QDateTime> column; 1318 column.resize(count); 1319 // TODO: warning (GCC8): writing to an object of type 'class QDateTime' with no trivial copy-assignment; use copy-assignment or 1320 // copy-initialization instead memcpy(column.data(), bytes.data(), count*sizeof(QDateTime)); 1321 static_cast<QVector<QVector<QDateTime>>*>(d->data)->append(column); 1322 break; 1323 } 1324 } 1325 } else { // unknown element 1326 reader->raiseUnknownElementWarning(); 1327 if (!reader->skipToEndElement()) 1328 return false; 1329 } 1330 } 1331 1332 return true; 1333 } 1334 1335 // ############################################################################## 1336 // ######################## Data Import ####################################### 1337 // ############################################################################## 1338 int Matrix::prepareImport(std::vector<void*>& dataContainer, 1339 AbstractFileFilter::ImportMode mode, 1340 int actualRows, 1341 int actualCols, 1342 QStringList /*colNameList*/, 1343 QVector<AbstractColumn::ColumnMode> columnMode, 1344 bool initializeDataContainer) { 1345 Q_D(Matrix); 1346 auto newColumnMode = columnMode.at(0); // only first column mode used 1347 DEBUG(Q_FUNC_INFO << ", rows = " << actualRows << " cols = " << actualCols << ", mode = " << ENUM_TO_STRING(AbstractFileFilter, ImportMode, mode) 1348 << ", column mode = " << ENUM_TO_STRING(AbstractColumn, ColumnMode, newColumnMode)) 1349 // QDEBUG(" column modes = " << columnMode); 1350 int columnOffset = 0; 1351 setUndoAware(false); 1352 1353 setSuppressDataChangedSignal(true); 1354 1355 // resize the matrix 1356 if (mode == AbstractFileFilter::ImportMode::Replace) { 1357 clear(); 1358 setDimensions(actualRows, actualCols); 1359 } else { // Append 1360 // handle mismatch of modes 1361 DEBUG(Q_FUNC_INFO << ", TODO: matrix mode = " << ENUM_TO_STRING(AbstractColumn, ColumnMode, d->mode) 1362 << ", columnMode = " << ENUM_TO_STRING(AbstractColumn, ColumnMode, columnMode.at(0))) 1363 // TODO: no way to convert types yet! 1364 if (d->mode != newColumnMode) { 1365 DEBUG(Q_FUNC_INFO << ", WARNING mismatch of types in append mode!") 1366 } 1367 // catch some cases 1368 if ((d->mode == AbstractColumn::ColumnMode::Integer || d->mode == AbstractColumn::ColumnMode::BigInt) 1369 && newColumnMode == AbstractColumn::ColumnMode::Double) 1370 d->mode = newColumnMode; 1371 1372 columnOffset = columnCount(); 1373 actualCols += columnOffset; 1374 DEBUG(Q_FUNC_INFO << ", col count = " << columnCount() << ", actualCols = " << actualCols) 1375 if (rowCount() < actualRows) 1376 setDimensions(actualRows, actualCols); 1377 else 1378 setDimensions(rowCount(), actualCols); 1379 } 1380 1381 DEBUG(Q_FUNC_INFO << ", actual rows/cols = " << actualRows << "/" << actualCols) 1382 // data() returns a void* which is a pointer to a matrix of any data type (see ColumnPrivate.cpp) 1383 if (initializeDataContainer) { 1384 dataContainer.resize(actualCols); 1385 1386 switch (newColumnMode) { // prepare all columns 1387 case AbstractColumn::ColumnMode::Double: 1388 for (int n = 0; n < actualCols; n++) { 1389 QVector<double>* vector = &(static_cast<QVector<QVector<double>>*>(data())->operator[](n)); 1390 vector->resize(actualRows); 1391 dataContainer[n] = static_cast<void*>(vector); 1392 } 1393 d->mode = AbstractColumn::ColumnMode::Double; 1394 break; 1395 case AbstractColumn::ColumnMode::Integer: 1396 for (int n = 0; n < actualCols; n++) { 1397 QVector<int>* vector = &(static_cast<QVector<QVector<int>>*>(data())->operator[](n)); 1398 vector->resize(actualRows); 1399 dataContainer[n] = static_cast<void*>(vector); 1400 } 1401 d->mode = AbstractColumn::ColumnMode::Integer; 1402 break; 1403 case AbstractColumn::ColumnMode::BigInt: 1404 for (int n = 0; n < actualCols; n++) { 1405 QVector<qint64>* vector = &(static_cast<QVector<QVector<qint64>>*>(data())->operator[](n)); 1406 vector->resize(actualRows); 1407 dataContainer[n] = static_cast<void*>(vector); 1408 } 1409 d->mode = AbstractColumn::ColumnMode::BigInt; 1410 break; 1411 case AbstractColumn::ColumnMode::Text: 1412 for (int n = 0; n < actualCols; n++) { 1413 QVector<QString>* vector = &(static_cast<QVector<QVector<QString>>*>(data())->operator[](n)); 1414 vector->resize(actualRows); 1415 dataContainer[n] = static_cast<void*>(vector); 1416 } 1417 d->mode = AbstractColumn::ColumnMode::Text; 1418 break; 1419 case AbstractColumn::ColumnMode::Day: 1420 case AbstractColumn::ColumnMode::Month: 1421 case AbstractColumn::ColumnMode::DateTime: 1422 for (int n = 0; n < actualCols; n++) { 1423 QVector<QDateTime>* vector = &(static_cast<QVector<QVector<QDateTime>>*>(data())->operator[](n)); 1424 vector->resize(actualRows); 1425 dataContainer[n] = static_cast<void*>(vector); 1426 } 1427 d->mode = AbstractColumn::ColumnMode::DateTime; 1428 break; 1429 } 1430 } 1431 1432 return columnOffset; 1433 } 1434 1435 void Matrix::finalizeImport(size_t /*columnOffset*/, 1436 size_t /*startColumn*/, 1437 size_t /*endColumn*/, 1438 const QString& /*dateTimeFormat*/, 1439 AbstractFileFilter::ImportMode) { 1440 DEBUG(Q_FUNC_INFO) 1441 1442 setSuppressDataChangedSignal(false); 1443 setChanged(); 1444 setUndoAware(true); 1445 }