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

0001 /***************************************************************************
0002     File                 : matrixcommands.h
0003     Project              : LabPlot
0004     Description          : Commands used in Matrix (part of the undo/redo framework)
0005     --------------------------------------------------------------------
0006     Copyright            : (C) 2008 Tilman Benkert (thzs@gmx.net)
0007     Copyright            : (C) 2015 Alexander Semke (alexander.semke@web.de)
0008     Copyright            : (C) 2017 Stefan Gerlach (stefan.gerlach@uni.kn)
0009 
0010  ***************************************************************************/
0011 
0012 /***************************************************************************
0013  *                                                                         *
0014  *  This program is free software; you can redistribute it and/or modify   *
0015  *  it under the terms of the GNU General Public License as published by   *
0016  *  the Free Software Foundation; either version 2 of the License, or      *
0017  *  (at your option) any later version.                                    *
0018  *                                                                         *
0019  *  This program is distributed in the hope that it will be useful,        *
0020  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0021  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0022  *  GNU General Public License for more details.                           *
0023  *                                                                         *
0024  *   You should have received a copy of the GNU General Public License     *
0025  *   along with this program; if not, write to the Free Software           *
0026  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0027  *   Boston, MA  02110-1301  USA                                           *
0028  *                                                                         *
0029  ***************************************************************************/
0030 
0031 #ifndef MATRIX_COMMANDS_H
0032 #define MATRIX_COMMANDS_H
0033 
0034 #include <QUndoCommand>
0035 #include <KLocalizedString>
0036 #include "Matrix.h"
0037 #include "MatrixPrivate.h"
0038 
0039 //! Insert columns
0040 class MatrixInsertColumnsCmd : public QUndoCommand {
0041 public:
0042     MatrixInsertColumnsCmd(MatrixPrivate*, int before, int count, QUndoCommand* = nullptr);
0043     void redo() override;
0044     void undo() override;
0045 
0046 private:
0047     MatrixPrivate* m_private_obj;
0048     int m_before; //! Column to insert before
0049     int m_count; //! The number of new columns
0050 };
0051 
0052 //! Insert rows
0053 class MatrixInsertRowsCmd : public QUndoCommand {
0054 public:
0055     MatrixInsertRowsCmd(MatrixPrivate*, int before, int count, QUndoCommand* = nullptr);
0056     void redo() override;
0057     void undo() override;
0058 
0059 private:
0060     MatrixPrivate* m_private_obj;
0061     int m_before; //! Row to insert before
0062     int m_count; //! The number of new rows
0063 };
0064 
0065 //! Remove columns
0066 template <typename T>
0067 class MatrixRemoveColumnsCmd : public QUndoCommand {
0068 public:
0069     MatrixRemoveColumnsCmd(MatrixPrivate* private_obj, int first, int count, QUndoCommand* parent = nullptr)
0070             : QUndoCommand(parent), m_private_obj(private_obj), m_first(first), m_count(count) {
0071         setText(i18np("%1: remove %2 column", "%1: remove %2 columns", m_private_obj->name(), m_count));
0072     }
0073     void redo() override {
0074         if(m_backups.isEmpty()) {
0075             int last_row = m_private_obj->rowCount-1;
0076             for (int i = 0; i < m_count; i++)
0077                 m_backups.append(m_private_obj->columnCells<T>(m_first+i, 0, last_row));
0078         }
0079         m_private_obj->removeColumns(m_first, m_count);
0080         emit m_private_obj->q->columnCountChanged(m_private_obj->columnCount);
0081     }
0082     void undo() override {
0083         m_private_obj->insertColumns(m_first, m_count);
0084         int last_row = m_private_obj->rowCount-1;
0085         //TODO: use memcopy to copy from the backup vector
0086         for (int i = 0; i < m_count; i++)
0087             m_private_obj->setColumnCells(m_first+i, 0, last_row, m_backups.at(i));
0088 
0089         emit m_private_obj->q->columnCountChanged(m_private_obj->columnCount);
0090     }
0091 
0092 private:
0093     MatrixPrivate* m_private_obj;
0094 
0095     int m_first; //! First column to remove
0096     int m_count; //! The number of columns to remove
0097     QVector<QVector<T>> m_backups; //! Backups of the removed columns
0098 };
0099 
0100 //! Remove rows
0101 template <typename T>
0102 class MatrixRemoveRowsCmd : public QUndoCommand {
0103 public:
0104     MatrixRemoveRowsCmd(MatrixPrivate* private_obj, int first, int count, QUndoCommand* parent = nullptr)
0105             : QUndoCommand(parent), m_private_obj(private_obj), m_first(first), m_count(count) {
0106         setText(i18np("%1: remove %2 row", "%1: remove %2 rows", m_private_obj->name(), m_count));
0107     }
0108     void redo() override {
0109         if(m_backups.isEmpty()) {
0110             int last_row = m_first+m_count-1;
0111                 for (int col = 0; col < m_private_obj->columnCount; col++)
0112                     m_backups.append(m_private_obj->columnCells<T>(col, m_first, last_row));
0113         }
0114         m_private_obj->removeRows(m_first, m_count);
0115         emit m_private_obj->q->rowCountChanged(m_private_obj->rowCount);
0116     }
0117     void undo() override {
0118         m_private_obj->insertRows(m_first, m_count);
0119         int last_row = m_first+m_count-1;
0120         for (int col = 0; col < m_private_obj->columnCount; col++)
0121             m_private_obj->setColumnCells(col, m_first, last_row, m_backups.at(col));
0122         emit m_private_obj->q->rowCountChanged(m_private_obj->rowCount);
0123     }
0124 
0125 private:
0126     MatrixPrivate* m_private_obj;
0127     int m_first; //! First row to remove
0128     int m_count; //! The number of rows to remove
0129     QVector< QVector<T> > m_backups; //! Backups of the removed rows
0130 };
0131 
0132 //! Clear matrix
0133 template <typename T>
0134 class MatrixClearCmd : public QUndoCommand {
0135 public:
0136     explicit MatrixClearCmd(MatrixPrivate* private_obj, QUndoCommand* parent = nullptr)
0137             : QUndoCommand(parent), m_private_obj(private_obj) {
0138         setText(i18n("%1: clear", m_private_obj->name()));
0139     }
0140     void redo() override {
0141         if(m_backups.isEmpty()) {
0142             int last_row = m_private_obj->rowCount-1;
0143 
0144             for (int i = 0; i < m_private_obj->columnCount; i++)
0145                 m_backups.append(m_private_obj->columnCells<T>(i, 0, last_row));
0146         }
0147 
0148         for (int i = 0; i < m_private_obj->columnCount; i++)
0149             m_private_obj->clearColumn(i);
0150     }
0151     void undo() override {
0152         int last_row = m_private_obj->rowCount-1;
0153         for (int i = 0; i < m_private_obj->columnCount; i++)
0154             m_private_obj->setColumnCells(i, 0, last_row, m_backups.at(i));
0155     }
0156 
0157 private:
0158     MatrixPrivate* m_private_obj;
0159     QVector<QVector<T>> m_backups; //! Backups of the cleared cells
0160 };
0161 
0162 //! Clear matrix column
0163 template <typename T>
0164 class MatrixClearColumnCmd : public QUndoCommand {
0165 public:
0166     MatrixClearColumnCmd(MatrixPrivate* private_obj, int col, QUndoCommand* parent = nullptr)
0167             : QUndoCommand(parent), m_private_obj(private_obj), m_col(col) {
0168         setText(i18n("%1: clear column %2", m_private_obj->name(), m_col+1));
0169     }
0170     void redo() override {
0171         if(m_backup.isEmpty())
0172             m_backup = m_private_obj->columnCells<T>(m_col, 0, m_private_obj->rowCount-1);
0173         m_private_obj->clearColumn(m_col);
0174     }
0175     void undo() override {
0176         m_private_obj->setColumnCells(m_col, 0, m_private_obj->rowCount-1, m_backup);
0177     }
0178 
0179 private:
0180     MatrixPrivate* m_private_obj;
0181     int m_col; //! The index of the column
0182     QVector<T> m_backup; //! Backup of the cleared column
0183 };
0184 
0185 // Set cell value
0186 template <typename T>
0187 class MatrixSetCellValueCmd : public QUndoCommand {
0188 public:
0189     MatrixSetCellValueCmd(MatrixPrivate* private_obj, int row, int col, T value, QUndoCommand* parent = nullptr)
0190             : QUndoCommand(parent), m_private_obj(private_obj), m_row(row), m_col(col), m_value(value), m_old_value(value) {
0191         // remark: don't use many QString::arg() calls in ctors of commands that might be called often,
0192         // they use a lot of execution time
0193         setText(i18n("%1: set cell value", m_private_obj->name()));
0194     }
0195     void redo() override {
0196         m_old_value = m_private_obj->cell<T>(m_row, m_col);
0197         m_private_obj->setCell(m_row, m_col, m_value);
0198     }
0199     void undo() override {
0200         m_private_obj->setCell(m_row, m_col, m_old_value);
0201     }
0202 
0203 private:
0204     MatrixPrivate* m_private_obj;
0205     int m_row; //! The index of the row
0206     int m_col; //! The index of the column
0207     T m_value; //! New cell value
0208     T m_old_value; //! Backup of the changed value
0209 };
0210 
0211 // Set matrix coordinates
0212 class MatrixSetCoordinatesCmd : public QUndoCommand {
0213 public:
0214     MatrixSetCoordinatesCmd(MatrixPrivate*, double x1, double x2, double y1, double y2, QUndoCommand* = nullptr);
0215     void redo() override;
0216     void undo() override;
0217 
0218 private:
0219     MatrixPrivate* m_private_obj;
0220     double m_new_x1;
0221     double m_new_x2;
0222     double m_new_y1;
0223     double m_new_y2;
0224     double m_old_x1{-1};
0225     double m_old_x2{-1};
0226     double m_old_y1{-1};
0227     double m_old_y2{-1};
0228 };
0229 
0230 //! Set matrix formula
0231 class MatrixSetFormulaCmd : public QUndoCommand {
0232 public:
0233     MatrixSetFormulaCmd(MatrixPrivate*, QString formula);
0234     void redo() override;
0235     void undo() override;
0236 
0237 private:
0238     MatrixPrivate* m_private_obj;
0239     QString m_other_formula;
0240 };
0241 
0242 // Set cell values for (a part of) a column at once
0243 template <typename T>
0244 class MatrixSetColumnCellsCmd : public QUndoCommand {
0245 public:
0246     MatrixSetColumnCellsCmd(MatrixPrivate* private_obj, int col, int first_row, int last_row, const QVector<T>& values, QUndoCommand* parent = nullptr)
0247             : QUndoCommand(parent), m_private_obj(private_obj), m_col(col), m_first_row(first_row), m_last_row(last_row), m_values(values) {
0248         setText(i18n("%1: set cell values", m_private_obj->name()));
0249     }
0250     void redo() override {
0251         if (m_old_values.isEmpty())
0252             m_old_values = m_private_obj->columnCells<T>(m_col, m_first_row, m_last_row);
0253         m_private_obj->setColumnCells(m_col, m_first_row, m_last_row, m_values);
0254     }
0255     void undo() override {
0256         m_private_obj->setColumnCells(m_col, m_first_row, m_last_row, m_old_values);
0257     }
0258 
0259 private:
0260     MatrixPrivate* m_private_obj;
0261     int m_col; //! The index of the column
0262     int m_first_row; //! The index of the first row
0263     int m_last_row; //! The index of the last row
0264     QVector<T> m_values; //! New cell values
0265     QVector<T> m_old_values; //! Backup of the changed values
0266 };
0267 
0268 //! Set cell values for (a part of) a row at once
0269 template <typename T>
0270 class MatrixSetRowCellsCmd : public QUndoCommand {
0271 public:
0272     MatrixSetRowCellsCmd(MatrixPrivate* private_obj, int row, int first_column, int last_column, const QVector<T>& values, QUndoCommand* parent = nullptr)
0273             : QUndoCommand(parent), m_private_obj(private_obj), m_row(row), m_first_column(first_column),
0274                 m_last_column(last_column), m_values(values) {
0275         setText(i18n("%1: set cell values", m_private_obj->name()));
0276     }
0277     void redo() override {
0278         if (m_old_values.isEmpty())
0279             m_old_values = m_private_obj->rowCells<T>(m_row, m_first_column, m_last_column);
0280         m_private_obj->setRowCells(m_row, m_first_column, m_last_column, m_values);
0281     }
0282     void undo() override {
0283         m_private_obj->setRowCells(m_row, m_first_column, m_last_column, m_old_values);
0284     }
0285 
0286 private:
0287     MatrixPrivate* m_private_obj;
0288     int m_row; //! The index of the row
0289     int m_first_column; //! The index of the first column
0290     int m_last_column; //! The index of the last column
0291     QVector<T> m_values; //! New cell values
0292     QVector<T> m_old_values; //! Backup of the changed values
0293 };
0294 
0295 //! Transpose the matrix
0296 template <typename T>
0297 class MatrixTransposeCmd : public QUndoCommand {
0298 public:
0299     explicit MatrixTransposeCmd(MatrixPrivate* private_obj, QUndoCommand* parent = nullptr)
0300             : QUndoCommand(parent), m_private_obj(private_obj) {
0301         setText(i18n("%1: transpose", m_private_obj->name()));
0302     }
0303     void redo() override {
0304         int rows = m_private_obj->rowCount;
0305         int cols = m_private_obj->columnCount;
0306         int temp_size = qMax(rows, cols);
0307         m_private_obj->suppressDataChange = true;
0308         if (cols < rows)
0309             m_private_obj->insertColumns(cols, temp_size - cols);
0310         else if (cols > rows)
0311             m_private_obj->insertRows(rows, temp_size - rows);
0312 
0313         for (int i = 1; i < temp_size; i++) {
0314             QVector<T> row = m_private_obj->rowCells<T>(i, 0, i-1);
0315             QVector<T> col = m_private_obj->columnCells<T>(i, 0, i-1);
0316             m_private_obj->setRowCells(i, 0, i-1, col);
0317             m_private_obj->setColumnCells(i, 0, i-1, row);
0318         }
0319 
0320         if (cols < rows)
0321             m_private_obj->removeRows(cols, temp_size - cols);
0322         else if (cols > rows)
0323             m_private_obj->removeColumns(rows, temp_size - rows);
0324         m_private_obj->suppressDataChange = false;
0325         m_private_obj->emitDataChanged(0, 0, m_private_obj->rowCount-1, m_private_obj->columnCount-1);
0326     }
0327     void undo() override {
0328         redo();
0329     }
0330 
0331 private:
0332     MatrixPrivate* m_private_obj;
0333 };
0334 
0335 //! Mirror the matrix horizontally
0336 template <typename T>
0337 class MatrixMirrorHorizontallyCmd : public QUndoCommand {
0338 public:
0339     explicit MatrixMirrorHorizontallyCmd(MatrixPrivate* private_obj, QUndoCommand* parent = nullptr)
0340             : QUndoCommand(parent), m_private_obj(private_obj) {
0341         setText(i18n("%1: mirror horizontally", m_private_obj->name()));
0342     }
0343     void redo() override {
0344         int rows = m_private_obj->rowCount;
0345         int cols = m_private_obj->columnCount;
0346         int middle = cols/2;
0347         m_private_obj->suppressDataChange = true;
0348 
0349         for (int i = 0; i<middle; i++) {
0350             QVector<T> temp = m_private_obj->columnCells<T>(i, 0, rows-1);
0351             m_private_obj->setColumnCells(i, 0, rows-1, m_private_obj->columnCells<T>(cols-i-1, 0, rows-1));
0352             m_private_obj->setColumnCells(cols-i-1, 0, rows-1, temp);
0353         }
0354         m_private_obj->suppressDataChange = false;
0355         m_private_obj->emitDataChanged(0, 0, rows-1, cols-1);
0356     }
0357     void undo() override {
0358         redo();
0359     }
0360 
0361 private:
0362     MatrixPrivate* m_private_obj;
0363 };
0364 
0365 // Mirror the matrix vertically
0366 template <typename T>
0367 class MatrixMirrorVerticallyCmd : public QUndoCommand {
0368 public:
0369     explicit MatrixMirrorVerticallyCmd(MatrixPrivate* private_obj, QUndoCommand* parent = nullptr)
0370         : QUndoCommand(parent), m_private_obj(private_obj) {
0371             setText(i18n("%1: mirror vertically", m_private_obj->name()));
0372     }
0373     void redo() override {
0374         int rows = m_private_obj->rowCount;
0375         int cols = m_private_obj->columnCount;
0376         int middle = rows/2;
0377         m_private_obj->suppressDataChange = true;
0378 
0379         for (int i = 0; i < middle; i++) {
0380             QVector<T> temp = m_private_obj->rowCells<T>(i, 0, cols-1);
0381             m_private_obj->setRowCells(i, 0, cols-1, m_private_obj->rowCells<T>(rows-i-1, 0, cols-1));
0382             m_private_obj->setRowCells(rows-i-1, 0, cols-1, temp);
0383         }
0384 
0385         m_private_obj->suppressDataChange = false;
0386         m_private_obj->emitDataChanged(0, 0, rows-1, cols-1);
0387     }
0388     void undo() override {
0389         redo();
0390     }
0391 
0392 private:
0393     MatrixPrivate* m_private_obj;
0394 };
0395 
0396 // Replace matrix values
0397 class MatrixReplaceValuesCmd : public QUndoCommand {
0398 public:
0399     explicit MatrixReplaceValuesCmd(MatrixPrivate*, void* new_values, QUndoCommand* = nullptr);
0400     void redo() override;
0401     void undo() override;
0402 
0403 private:
0404     MatrixPrivate* m_private_obj;
0405     void* m_old_values{nullptr};
0406     void* m_new_values;
0407 };
0408 
0409 #endif // MATRIX_COMMANDS_H