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