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 }