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

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