1 /***************************************************************************
2     File                 : Matrix.h
3     Project              : SciDAVis
4     Description          : Aspect providing a spreadsheet to manage MxN matrix data
5     --------------------------------------------------------------------
6     Copyright            : (C) 2006-2009 Tilman Benkert (thzs*gmx.net)
7     Copyright            : (C) 2006-2009 Knut Franke (knut.franke*gmx.de)
8     Copyright            : (C) 2006-2007 Ion Vasilief (ion_vasilief*yahoo.fr)
9                            (replace * with @ in the email addresses)
10 
11  ***************************************************************************/
12 
13 /***************************************************************************
14  *                                                                         *
15  *  This program is free software; you can redistribute it and/or modify   *
16  *  it under the terms of the GNU General Public License as published by   *
17  *  the Free Software Foundation; either version 2 of the License, or      *
18  *  (at your option) any later version.                                    *
19  *                                                                         *
20  *  This program is distributed in the hope that it will be useful,        *
21  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
22  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
23  *  GNU General Public License for more details.                           *
24  *                                                                         *
25  *   You should have received a copy of the GNU General Public License     *
26  *   along with this program; if not, write to the Free Software           *
27  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
28  *   Boston, MA  02110-1301  USA                                           *
29  *                                                                         *
30  ***************************************************************************/
31 #ifndef FUTURE_MATRIX_H
32 #define FUTURE_MATRIX_H
33 
34 #ifndef LEGACY_CODE_0_2_x
35 #include "core/AbstractScriptingEngine.h"
36 #endif
37 #include "core/AbstractPart.h"
38 #include "matrix/MatrixView.h"
39 #include "lib/macros.h"
40 
41 #include <QPointer>
42 
43 class QContextMenuEvent;
44 class QEvent;
45 class ActionManager;
46 
47 // (maximum) initial matrix size (this is the size of the window, not the number of cells)
48 #define _Matrix_initial_rows_ 10
49 #define _Matrix_initial_columns_ 3
50 
51 // TODO: move all selection related stuff to the primary view
52 
53 namespace future {
54 #ifndef LEGACY_CODE_0_2_x
55 //! Aspect providing a spreadsheet to manage MxN matrix data
56 class Matrix : public AbstractPart, public scripted
57 #else
58 class Matrix : public AbstractPart
59 #endif
60 {
61     Q_OBJECT
62 
63 public:
64     class Private;
65     friend class Private;
66 
67     /*!
68      * \brief Constructor
69      *
70      * \param engine scripting engine
71      * \param rows initial number of rows
72      * \param cols initial number of columns
73      * \param name object name
74      */
75 #ifndef LEGACY_CODE_0_2_x
76     Matrix(AbstractScriptingEngine *engine, int rows, int cols, const QString &name);
77 #else
78     Matrix(void *, int rows, int cols, const QString &name);
79     void setView(MatrixView *view);
80 #endif
81     ~Matrix();
82 
83     //! Return an icon to be used for decorating my views.
84     virtual QIcon icon() const;
85     //! Return a new context menu.
86     /**
87      * The caller takes ownership of the menu.
88      */
89     virtual QMenu *createContextMenu() const;
90     //! Construct a primary view on me.
91     /**
92      * This method may be called multiple times during the life time of an Aspect, or it might not
93      * get called at all. Aspects must not depend on the existence of a view for their operation.
94      */
95     virtual QWidget *view();
96     //! Create a menu with selection related operations
97     /**
98      * \param append_to if a pointer to a QMenu is passed
99      * to the function, the actions are appended to
100      * it instead of the creation of a new menu.
101      */
102     QMenu *createSelectionMenu(QMenu *append_to = 0);
103     //! Create a menu with column related operations
104     /**
105      * \param append_to if a pointer to a QMenu is passed
106      * to the function, the actions are appended to
107      * it instead of the creation of a new menu.
108      */
109     QMenu *createColumnMenu(QMenu *append_to = 0);
110     //! Create a menu with row related operations
111     /**
112      * \param append_to if a pointer to a QMenu is passed
113      * to the function, the actions are appended to
114      * it instead of the creation of a new menu.
115      */
116     QMenu *createRowMenu(QMenu *append_to = 0);
117     //! Create a menu with table related operations
118     /**
119      * \param append_to if a pointer to a QMenu is passed
120      * to the function, the actions are appended to
121      * it instead of the creation of a new menu.
122      */
123     QMenu *createMatrixMenu(QMenu *append_to = 0);
124     //! Fill the part specific menu for the main window including setting the title
125     /**
126      * \return true on success, otherwise false (e.g. part has no actions).
127      */
128     virtual bool fillProjectMenu(QMenu *menu);
129 
130     void insertColumns(int before, int count);
appendColumns(int count)131     void appendColumns(int count) { insertColumns(columnCount(), count); }
132     void removeColumns(int first, int count);
133     void insertRows(int before, int count);
appendRows(int count)134     void appendRows(int count) { insertRows(rowCount(), count); }
135     void removeRows(int first, int count);
136     //! Set the number of rows and columns
137     void setDimensions(int rows, int cols);
138     //! Return the total number of columns
139     int columnCount() const;
140     //! Return the total number of rows
141     int rowCount() const;
142 
143     //! Set a plot menu
144     /**
145      * The matrix takes ownership of the menu.
146      */
147     void setPlotMenu(QMenu *menu);
148     //! Return the value in the given cell
149     double cell(int row, int col) const;
150     //! Set the value of the cell
151     void setCell(int row, int col, double value);
152     //! Set the value of all cells
153     void setCells(const QVector<qreal> &data);
154     //! Return the values in the given cells as double vector
155     QVector<qreal> columnCells(int col, int first_row, int last_row);
156     //! Set the values in the given cells from a double vector
157     void setColumnCells(int col, int first_row, int last_row, const QVector<qreal> &values);
158     //! Return the values in the given cells as double vector
159     QVector<qreal> rowCells(int row, int first_column, int last_column);
160     //! Set the values in the given cells from a double vector
161     void setRowCells(int row, int first_column, int last_column, const QVector<qreal> &values);
162     //! Return the text displayed in the given cell
163     QString text(int row, int col);
164     using AbstractPart::copy;
165     void copy(Matrix *other);
166     double xStart() const;
167     double yStart() const;
168     double xEnd() const;
169     double yEnd() const;
170     QString formula() const;
171     void setFormula(const QString &formula);
172     void setXStart(double x);
173     void setXEnd(double x);
174     void setYStart(double y);
175     void setYEnd(double y);
176     void setCoordinates(double x1, double x2, double y1, double y2);
177     char numericFormat() const;
178     int displayedDigits() const;
179     void setNumericFormat(char format);
180     void setDisplayedDigits(int digits);
181 
182     //! \name serialize/deserialize
183     //@{
184     //! Save as XML
185     virtual void save(QXmlStreamWriter *) const;
186     //! Load from XML
187     virtual bool load(XmlStreamReader *);
188     //@}
189 
190     //! This method should only be called by the view.
191     /** This method does not change the view, it only changes the
192      * values that are saved when the matrix is saved. The view
193      * has to take care of reading and applying these values */
194     void setRowHeight(int row, int height);
195     //! This method should only be called by the view.
196     /** This method does not change the view, it only changes the
197      * values that are saved when the matrix is saved. The view
198      * has to take care of reading and applying these values */
199     void setColumnWidth(int col, int width);
200     int rowHeight(int row) const;
201     int columnWidth(int col) const;
202 
203 public:
204     static ActionManager *actionManager();
205     static void initActionManager();
defaultColumnWidth()206     static int defaultColumnWidth() { return default_column_width; }
defaultRowHeight()207     static int defaultRowHeight() { return default_row_height; }
setDefaultColumnWidth(int width)208     static void setDefaultColumnWidth(int width) { default_column_width = width; }
setDefaultRowHeight(int height)209     static void setDefaultRowHeight(int height) { default_row_height = height; }
210 
211 private:
212     static ActionManager *action_manager;
213     //! Private ctor for initActionManager() only
214     Matrix();
215     // TODO: the default sizes are to be controlled by the global Matrix settings
216     static int default_column_width;
217     static int default_row_height;
218 
219 public:
220     static Matrix *fromImage(const QImage &image);
221 
222 public slots:
223     //! Clear the whole matrix (i.e. set all cells to 0.0)
224     void clear();
225     void transpose();
226 
227     std::vector<std::vector<std::pair<double, bool>>>
228     getCells(const int startRow, const int endRow, const int startCol, const int endCol) const;
229 
230     void setCells(const int startRow, const int startCol,
231                   const std::vector<std::vector<std::pair<double, bool>>> &values);
232     void mirrorVertically();
233     void mirrorHorizontally();
234 
235     void cutSelection();
236     void copySelection();
237     void pasteIntoSelection();
238     void clearSelectedCells();
239     void dimensionsDialog();
240     void goToCell();
241     //! Insert columns depending on the selection
242     void insertEmptyColumns();
243     //! Insert rows depending on the selection
244     void insertEmptyRows();
245     void removeSelectedColumns();
246     void removeSelectedRows();
247     void clearSelectedColumns();
248     void clearSelectedRows();
249     void selectAll();
250     //! Show a context menu for the selected cells
251     /**
252      * \param pos global position of the event
253      */
254     void showMatrixViewContextMenu(const QPoint &pos);
255     //! Show a context menu for the selected columns
256     /**
257      * \param pos global position of the event
258      */
259     void showMatrixViewColumnContextMenu(const QPoint &pos);
260     //! Show a context menu for the selected rows
261     /**
262      * \param pos global position of the event
263      */
264     void showMatrixViewRowContextMenu(const QPoint &pos);
265     void editFormat();
266     void editCoordinates();
267     void editFormula();
268     //! Append as many columns as are selected
269     void addColumns();
270     //! Append as many rows as are selected
271     void addRows();
272     void importImageDialog();
273     //! Duplicate the matrix inside its folder
274     void duplicate();
275 #ifdef LEGACY_CODE_0_2_x
276     void recalculateSelectedCells();
277 #endif
278 
279 signals:
280     void columnsAboutToBeInserted(int before, int count);
281     void columnsInserted(int first, int count);
282     void columnsAboutToBeRemoved(int first, int count);
283     void columnsRemoved(int first, int count);
284     void rowsAboutToBeInserted(int before, int count);
285     void rowsInserted(int first, int count);
286     void rowsAboutToBeRemoved(int first, int count);
287     void rowsRemoved(int first, int count);
288     void dataChanged(int top, int left, int bottom, int right);
289     void coordinatesChanged();
290     void formulaChanged();
291     void formatChanged();
292 #ifdef LEGACY_CODE_0_2_x
293     void recalculate();
294 #endif
295 
296 private slots:
297     void adjustTabBarAction(bool visible);
298 
299 private:
300     void createActions();
301     void connectActions();
302     void addActionsToView();
303     void translateActionsStrings();
304 
305     //! Read XML display element
306     bool readDisplayElement(XmlStreamReader *reader);
307     //! Read XML coodinates element
308     bool readCoordinatesElement(XmlStreamReader *reader);
309     //! Read XML formula element
310     bool readFormulaElement(XmlStreamReader *reader);
311     //! Read XML cell element
312     bool readCellElement(XmlStreamReader *reader);
313     bool readRowHeightElement(XmlStreamReader *reader);
314     bool readColumnWidthElement(XmlStreamReader *reader);
315 
316     QMenu *d_plot_menu;
317 
318     //! \name selection related actions
319     //@{
320     QAction *action_cut_selection;
321     QAction *action_copy_selection;
322     QAction *action_paste_into_selection;
323     QAction *action_clear_selection;
324     //@}
325     //! \name matrix related actions
326     //@{
327     QAction *action_toggle_tabbar;
328     QAction *action_select_all;
329     QAction *action_clear_matrix;
330     QAction *action_go_to_cell;
331     QAction *action_dimensions_dialog;
332     QAction *action_edit_format;
333     QAction *action_edit_coordinates;
334     QAction *action_set_formula;
335     QAction *action_recalculate;
336     QAction *action_import_image;
337     QAction *action_duplicate;
338     QAction *action_transpose;
339     QAction *action_mirror_vertically;
340     QAction *action_mirror_horizontally;
341     //@}
342     //! \name column related actions
343     //@{
344     QAction *action_insert_columns;
345     QAction *action_remove_columns;
346     QAction *action_clear_columns;
347     QAction *action_add_columns;
348     //@}
349     //! \name row related actions
350     //@{
351     QAction *action_insert_rows;
352     QAction *action_remove_rows;
353     QAction *action_clear_rows;
354     QAction *action_add_rows;
355     //@}
356 
357     QPointer<MatrixView> d_view;
358     Private *d_matrix_private;
359 };
360 
361 /**
362   This private class manages matrix based data (i.e., mathematically
363   a MxN matrix with M rows, N columns). These data are typically
364   used to for 3D plots.
365 
366   The API of this private class is to be called by Matrix and matrix
367   commands only. Matrix may only call the reading functions to ensure
368   that undo/redo is possible for all data changing operations.
369 
370   The values of the matrix are stored as double precision values. They
371   are managed by QVector<double> objects. Although rows and columns
372   are equally important in a matrix, the columns are chosen to
373   be contiguous in memory to allow easier copying between
374   column and matrix data.
375   */
376 class Matrix::Private
377 {
378 public:
379     Private(Matrix *owner);
380     //! Insert columns before column number 'before'
381     /**
382      * If 'first' is equal to the current number of columns,
383      * the columns will be appended.
384      * \param before index of the column to insert before
385      * \param count the number of columns to be inserted
386      */
387     void insertColumns(int before, int count);
388     //! Remove Columns
389     /**
390      * \param first index of the first column to be removed
391      * \param count number of columns to remove
392      */
393     void removeColumns(int first, int count);
394     //! Insert rows before row number 'before'
395     /**
396      * If 'first' is equal to the current number of rows,
397      * the rows will be appended.
398      * \param before index of the row to insert before
399      * \param count the number of rows to be inserted
400      */
401     void insertRows(int before, int count);
402     //! Remove Columns
403     /**
404      * \param first index of the first row to be removed
405      * \param count number of rows to remove
406      */
407     void removeRows(int first, int count);
408     //! Return the number of columns in the table
columnCount()409     int columnCount() const { return d_column_count; }
410     //! Return the number of rows in the table
rowCount()411     int rowCount() const { return d_row_count; }
name()412     QString name() const { return d_owner->name(); }
413     //! Return the value in the given cell
414     double cell(int row, int col) const;
415     //! Set the value in the given cell
416     void setCell(int row, int col, double value);
417     //! Set the value of all cells
418     void setCells(const QVector<qreal> &data);
419     //! Return the values in the given cells as double vector
420     QVector<qreal> columnCells(int col, int first_row, int last_row);
421     //! Set the values in the given cells from a double vector
422     void setColumnCells(int col, int first_row, int last_row, const QVector<qreal> &values);
423     //! Return the values in the given cells as double vector
424     QVector<qreal> rowCells(int row, int first_column, int last_column);
425     //! Set the values in the given cells from a double vector
426     void setRowCells(int row, int first_column, int last_column, const QVector<qreal> &values);
numericFormat()427     char numericFormat() const { return d_numeric_format; }
setNumericFormat(char format)428     void setNumericFormat(char format)
429     {
430         d_numeric_format = format;
431         emit d_owner->formatChanged();
432     }
displayedDigits()433     int displayedDigits() const { return d_displayed_digits; }
setDisplayedDigits(int digits)434     void setDisplayedDigits(int digits)
435     {
436         d_displayed_digits = digits;
437         emit d_owner->formatChanged();
438     }
439     //! Fill column with zeroes
440     void clearColumn(int col);
441     double xStart() const;
442     double yStart() const;
443     double xEnd() const;
444     double yEnd() const;
445     QString formula() const;
446     void setFormula(const QString &formula);
447 
448     std::vector<std::vector<std::pair<double, bool>>>
449     getCells(const int startRow, const int endRow, const int startCol, const int endCol) const;
450 
451 	void setCells(const int startRow, const int startCol,
452                 const std::vector<std::vector<std::pair<double, bool>>> &values);
453 
454     void setXStart(double x);
455     void setXEnd(double x);
456     void setYStart(double y);
457     void setYEnd(double y);
setRowHeight(int row,int height)458     void setRowHeight(int row, int height) { d_row_heights[row] = height; }
setColumnWidth(int col,int width)459     void setColumnWidth(int col, int width) { d_column_widths[col] = width; }
rowHeight(int row)460     int rowHeight(int row) const { return d_row_heights.at(row); }
columnWidth(int col)461     int columnWidth(int col) const { return d_column_widths.at(col); }
462     //! Enable/disable the emission of dataChanged signals.
463     /** This can be used to suppress the emission of dataChanged signals
464      * temporally. It does not suppress any other signals however.
465      * Typical code:
466      * <code>
467      * d_matrix_private->blockChangeSignals(true);
468      * for (...)
469      *     for(...)
470      *         setCell(...);
471      * d_matrix_private->blockChangeSignals(false);
472      * emit dataChanged(0, 0, rowCount()-1, columnCount()-1);
473      * </code>
474      */
blockChangeSignals(bool block)475     void blockChangeSignals(bool block) { d_block_change_signals = block; }
476     //! Access to the dataChanged signal for commands
emitDataChanged(int top,int left,int bottom,int right)477     void emitDataChanged(int top, int left, int bottom, int right)
478     {
479         emit d_owner->dataChanged(top, left, bottom, right);
480     }
481 
482 private:
483     //! The owner aspect
484     Matrix *d_owner;
485     //! The number of columns
486     int d_column_count;
487     //! The number of rows
488     int d_row_count;
489     //! The matrix data
490     QVector<QVector<qreal>> d_data;
491     //! Row widths
492     QList<int> d_row_heights;
493     //! Columns widths
494     QList<int> d_column_widths;
495     //! Last formula used to calculate cell values
496     QString d_formula; // TODO: should we support interval/rectangle based formulas?
497     //! Format code for displaying numbers
498     char d_numeric_format;
499     //! Number of significant digits
500     int d_displayed_digits;
501     double d_x_start, //!< X value corresponding to column 1
502             d_x_end, //!< X value corresponding to the last column
503             d_y_start, //!< Y value corresponding to row 1
504             d_y_end; //!< Y value corresponding to the last row
505     bool d_block_change_signals;
506 };
507 
508 } // namespace
509 #endif
510