1 /***************************************************************************
2     File                 : Matrix.cpp
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 #include "matrix/future_Matrix.h"
32 #include "Matrix.h"
33 #include "core/future_Folder.h"
34 #include "matrixcommands.h"
35 #include "lib/ActionManager.h"
36 #include "lib/XmlStreamReader.h"
37 
38 #include <QtCore>
39 #include <QtGui>
40 #include <QtDebug>
41 #include <QMenu>
42 #include <QInputDialog>
43 #include <QFileDialog>
44 #include <QProgressDialog>
45 
46 #include <stdlib.h>
47 #include <math.h>
48 #include <stdio.h>
49 
50 #include <gsl/gsl_linalg.h>
51 #include <gsl/gsl_math.h>
52 
53 namespace future {
54 
55 #define WAIT_CURSOR QApplication::setOverrideCursor(QCursor(Qt::WaitCursor))
56 #define RESET_CURSOR QApplication::restoreOverrideCursor()
57 
58 int Matrix::default_column_width = 120;
59 int Matrix::default_row_height = 20;
60 
61 #ifndef LEGACY_CODE_0_2_x
Matrix(AbstractScriptingEngine * engine,int rows,int cols,const QString & name)62 Matrix::Matrix(AbstractScriptingEngine *engine, int rows, int cols, const QString &name)
63     : AbstractPart(name), d_plot_menu(0), scripted(engine)
64 #else
65 Matrix::Matrix(void *, int rows, int cols, const QString &name) : AbstractPart(name), d_plot_menu(0)
66 #endif
67 {
68     d_matrix_private = new Private(this);
69     // set initial number of rows and columns
70     appendColumns(cols);
71     appendRows(rows);
72 
73     d_view = NULL;
74     createActions();
75     connectActions();
76 }
77 
78 #ifndef LEGACY_CODE_0_2_x
Matrix()79 Matrix::Matrix() : AbstractPart("temp"), scripted(0)
80 #else
81 Matrix::Matrix() : AbstractPart("temp")
82 #endif
83 {
84     d_view = NULL;
85     createActions();
86 }
87 
~Matrix()88 Matrix::~Matrix()
89 {
90     //  delete d_view.data();
91 }
92 
setView(MatrixView * view)93 void Matrix::setView(MatrixView *view)
94 {
95     d_view = view;
96     addActionsToView();
97     connect(d_view, SIGNAL(controlTabBarStatusChanged(bool)), this, SLOT(adjustTabBarAction(bool)));
98     adjustTabBarAction(true);
99 }
100 
view()101 QWidget *Matrix::view()
102 {
103 #ifndef LEGACY_CODE_0_2_x
104     if (!d_view) {
105         d_view = new MatrixView(this);
106         addActionsToView();
107         connect(d_view, SIGNAL(controlTabBarStatusChanged(bool)), this,
108                 SLOT(adjustTabBarAction(bool)));
109         adjustTabBarAction(true);
110     }
111 #else
112     Q_ASSERT(d_view != NULL);
113 #endif
114     return d_view;
115 }
116 
insertColumns(int before,int count)117 void Matrix::insertColumns(int before, int count)
118 {
119     if (count < 1 || before < 0 || before > columnCount())
120         return;
121     WAIT_CURSOR;
122     beginMacro(QObject::tr("%1: insert %2 column(s)").arg(name()).arg(count));
123     exec(new MatrixInsertColumnsCmd(d_matrix_private, before, count));
124     endMacro();
125     RESET_CURSOR;
126 }
127 
removeColumns(int first,int count)128 void Matrix::removeColumns(int first, int count)
129 {
130     if (count < 1 || first < 0 || first + count > columnCount())
131         return;
132     WAIT_CURSOR;
133     beginMacro(QObject::tr("%1: remove %2 column(s)").arg(name()).arg(count));
134     exec(new MatrixRemoveColumnsCmd(d_matrix_private, first, count));
135     endMacro();
136     RESET_CURSOR;
137 }
138 
removeRows(int first,int count)139 void Matrix::removeRows(int first, int count)
140 {
141     if (count < 1 || first < 0 || first + count > rowCount())
142         return;
143     WAIT_CURSOR;
144     beginMacro(QObject::tr("%1: remove %2 row(s)").arg(name()).arg(count));
145     exec(new MatrixRemoveRowsCmd(d_matrix_private, first, count));
146     endMacro();
147     RESET_CURSOR;
148 }
149 
insertRows(int before,int count)150 void Matrix::insertRows(int before, int count)
151 {
152     if (count < 1 || before < 0 || before > rowCount())
153         return;
154     WAIT_CURSOR;
155     beginMacro(QObject::tr("%1: insert %2 row(s)").arg(name()).arg(count));
156     exec(new MatrixInsertRowsCmd(d_matrix_private, before, count));
157     endMacro();
158     RESET_CURSOR;
159 }
160 
setDimensions(int rows,int cols)161 void Matrix::setDimensions(int rows, int cols)
162 {
163     if ((rows < 0) || (cols < 0) || (rows == rowCount() && cols == columnCount()))
164         return;
165     WAIT_CURSOR;
166     beginMacro(QObject::tr("%1: set matrix size to %2x%3").arg(name()).arg(rows).arg(cols));
167     int col_diff = cols - columnCount();
168     int row_diff = rows - rowCount();
169     if (col_diff > 0)
170         exec(new MatrixInsertColumnsCmd(d_matrix_private, columnCount(), col_diff));
171     else if (col_diff < 0)
172         exec(new MatrixRemoveColumnsCmd(d_matrix_private, columnCount() + col_diff, -col_diff));
173     if (row_diff > 0)
174         exec(new MatrixInsertRowsCmd(d_matrix_private, rowCount(), row_diff));
175     else if (row_diff < 0)
176         exec(new MatrixRemoveRowsCmd(d_matrix_private, rowCount() + row_diff, -row_diff));
177     endMacro();
178     RESET_CURSOR;
179 }
180 
columnCount() const181 int Matrix::columnCount() const
182 {
183     return d_matrix_private->columnCount();
184 }
185 
rowCount() const186 int Matrix::rowCount() const
187 {
188     return d_matrix_private->rowCount();
189 }
190 
clear()191 void Matrix::clear()
192 {
193     WAIT_CURSOR;
194     beginMacro(QObject::tr("%1: clear").arg(name()));
195     exec(new MatrixClearCmd(d_matrix_private));
196     endMacro();
197     RESET_CURSOR;
198 }
199 
cell(int row,int col) const200 double Matrix::cell(int row, int col) const
201 {
202     if (row < 0 || row >= rowCount() || col < 0 || col >= columnCount())
203         return 0.0;
204     return d_matrix_private->cell(row, col);
205 }
206 
cutSelection()207 void Matrix::cutSelection()
208 {
209     if (!d_view)
210         return;
211     int first = d_view->firstSelectedRow();
212     if (first < 0)
213         return;
214 
215     WAIT_CURSOR;
216     beginMacro(tr("%1: cut selected cell(s)").arg(name()));
217     copySelection();
218     clearSelectedCells();
219     endMacro();
220     RESET_CURSOR;
221 }
222 
copySelection()223 void Matrix::copySelection()
224 {
225     if (!d_view)
226         return;
227     int first_col = d_view->firstSelectedColumn(false);
228     if (first_col == -1)
229         return;
230     int last_col = d_view->lastSelectedColumn(false);
231     if (last_col == -2)
232         return;
233     int first_row = d_view->firstSelectedRow(false);
234     if (first_row == -1)
235         return;
236     int last_row = d_view->lastSelectedRow(false);
237     if (last_row == -2)
238         return;
239     int cols = last_col - first_col + 1;
240     int rows = last_row - first_row + 1;
241 
242     WAIT_CURSOR;
243     QString output_str;
244 
245     for (int r = 0; r < rows; r++) {
246         for (int c = 0; c < cols; c++) {
247             if (d_view->isCellSelected(first_row + r, first_col + c))
248                 output_str += QLocale().toString(cell(first_row + r, first_col + c),
249                                                  d_matrix_private->numericFormat(),
250                                                  16); // copy with max. precision
251             if (c < cols - 1)
252                 output_str += "\t";
253         }
254         if (r < rows - 1)
255             output_str += "\n";
256     }
257     QApplication::clipboard()->setText(output_str);
258     RESET_CURSOR;
259 }
260 
pasteIntoSelection()261 void Matrix::pasteIntoSelection()
262 {
263     if (!d_view)
264         return;
265     if (columnCount() < 1 || rowCount() < 1)
266         return;
267 
268     WAIT_CURSOR;
269     beginMacro(tr("%1: paste from clipboard").arg(name()));
270 
271     int first_col = d_view->firstSelectedColumn(false);
272     int last_col = d_view->lastSelectedColumn(false);
273     int first_row = d_view->firstSelectedRow(false);
274     int last_row = d_view->lastSelectedRow(false);
275     int input_row_count = 0;
276     int input_col_count = 0;
277     int rows, cols;
278 
279     const QClipboard *clipboard = QApplication::clipboard();
280     const QMimeData *mimeData = clipboard->mimeData();
281     ;
282     if (mimeData->hasText()) {
283         QString input_str = QString(clipboard->text());
284         QList<QStringList> cell_texts;
285         QStringList input_rows(input_str.split(QRegExp("\\n|\\r\\n|\\r")));
286         input_row_count = input_rows.count();
287         input_col_count = 0;
288         for (int i = 0; i < input_row_count; i++) {
289             cell_texts.append(input_rows.at(i).split("\t"));
290             if (cell_texts.at(i).count() > input_col_count)
291                 input_col_count = cell_texts.at(i).count();
292         }
293 
294         if ((first_col == -1 || first_row == -1)
295             || (last_row == first_row && last_col == first_col))
296         // if the is no selection or only one cell selected, the
297         // selection will be expanded to the needed size from the current cell
298         {
299             int current_row, current_col;
300             d_view->getCurrentCell(&current_row, &current_col);
301             if (current_row == -1)
302                 current_row = 0;
303             if (current_col == -1)
304                 current_col = 0;
305             d_view->setCellSelected(current_row, current_col);
306             first_col = current_col;
307             first_row = current_row;
308             last_row = first_row + input_row_count - 1;
309             last_col = first_col + input_col_count - 1;
310             // resize the matrix if necessary
311             if (last_col >= columnCount())
312                 appendColumns(last_col + 1 - columnCount());
313             if (last_row >= rowCount())
314                 appendRows(last_row + 1 - rowCount());
315             // select the rectangle to be pasted in
316             d_view->setCellsSelected(first_row, first_col, last_row, last_col);
317         }
318 
319         rows = last_row - first_row + 1;
320         cols = last_col - first_col + 1;
321         for (int r = 0; r < rows && r < input_row_count; r++) {
322             for (int c = 0; c < cols && c < input_col_count; c++) {
323                 if (d_view->isCellSelected(first_row + r, first_col + c)
324                     && (c < cell_texts.at(r).count())) {
325                     setCell(first_row + r, first_col + c, cell_texts.at(r).at(c).toDouble());
326                 }
327             }
328         }
329     }
330     endMacro();
331     RESET_CURSOR;
332 }
333 
insertEmptyColumns()334 void Matrix::insertEmptyColumns()
335 {
336     if (!d_view)
337         return;
338     int first = d_view->firstSelectedColumn();
339     int last = d_view->lastSelectedColumn();
340     if (first < 0)
341         return;
342     int count, current = first;
343 
344     WAIT_CURSOR;
345     beginMacro(QObject::tr("%1: insert empty column(s)").arg(name()));
346     while (current <= last) {
347         current = first + 1;
348         while (current <= last && d_view->isColumnSelected(current))
349             current++;
350         count = current - first;
351         insertColumns(first, count);
352         current += count;
353         last += count;
354         while (current <= last && !d_view->isColumnSelected(current))
355             current++;
356         first = current;
357     }
358     endMacro();
359     RESET_CURSOR;
360 }
361 
removeSelectedColumns()362 void Matrix::removeSelectedColumns()
363 {
364     if (!d_view)
365         return;
366     int first = d_view->firstSelectedColumn();
367     int last = d_view->lastSelectedColumn();
368     if (first < 0)
369         return;
370 
371     WAIT_CURSOR;
372     beginMacro(QObject::tr("%1: remove selected column(s)").arg(name()));
373     for (int i = last; i >= first; i--)
374         if (d_view->isColumnSelected(i, false))
375             removeColumns(i, 1);
376     endMacro();
377     RESET_CURSOR;
378 }
379 
clearSelectedColumns()380 void Matrix::clearSelectedColumns()
381 {
382     if (!d_view)
383         return;
384     WAIT_CURSOR;
385     beginMacro(QObject::tr("%1: clear selected column(s)").arg(name()));
386     for (int i = 0; i < columnCount(); i++)
387         if (d_view->isColumnSelected(i, false))
388             exec(new MatrixClearColumnCmd(d_matrix_private, i));
389     endMacro();
390     RESET_CURSOR;
391 }
392 
insertEmptyRows()393 void Matrix::insertEmptyRows()
394 {
395     if (!d_view)
396         return;
397     int first = d_view->firstSelectedRow();
398     int last = d_view->lastSelectedRow();
399     int count, current = first;
400 
401     if (first < 0)
402         return;
403 
404     WAIT_CURSOR;
405     beginMacro(QObject::tr("%1: insert empty rows(s)").arg(name()));
406     while (current <= last) {
407         current = first + 1;
408         while (current <= last && d_view->isRowSelected(current))
409             current++;
410         count = current - first;
411         insertRows(first, count);
412         current += count;
413         last += count;
414         while (current <= last && !d_view->isRowSelected(current))
415             current++;
416         first = current;
417     }
418     endMacro();
419     RESET_CURSOR;
420 }
421 
removeSelectedRows()422 void Matrix::removeSelectedRows()
423 {
424     if (!d_view)
425         return;
426     int first = d_view->firstSelectedRow();
427     int last = d_view->lastSelectedRow();
428     if (first < 0)
429         return;
430 
431     WAIT_CURSOR;
432     beginMacro(QObject::tr("%1: remove selected rows(s)").arg(name()));
433     for (int i = last; i >= first; i--)
434         if (d_view->isRowSelected(i, false))
435             removeRows(i, 1);
436     endMacro();
437     RESET_CURSOR;
438 }
439 
clearSelectedRows()440 void Matrix::clearSelectedRows()
441 {
442     if (!d_view)
443         return;
444     int first = d_view->firstSelectedRow();
445     int last = d_view->lastSelectedRow();
446     if (first < 0)
447         return;
448 
449     WAIT_CURSOR;
450     beginMacro(QObject::tr("%1: clear selected rows(s)").arg(name()));
451     for (int i = first; i <= last; i++) {
452         if (d_view->isRowSelected(i))
453             for (int j = 0; j < columnCount(); j++)
454                 exec(new MatrixSetCellValueCmd(d_matrix_private, i, j, 0.0));
455     }
456     endMacro();
457     RESET_CURSOR;
458 }
459 
clearSelectedCells()460 void Matrix::clearSelectedCells()
461 {
462     if (!d_view)
463         return;
464     int first_row = d_view->firstSelectedRow();
465     int last_row = d_view->lastSelectedRow();
466     if (first_row < 0)
467         return;
468     int first_col = d_view->firstSelectedColumn();
469     int last_col = d_view->lastSelectedColumn();
470     if (first_col < 0)
471         return;
472 
473     WAIT_CURSOR;
474     beginMacro(tr("%1: clear selected cell(s)").arg(name()));
475     for (int i = first_row; i <= last_row; i++)
476         for (int j = first_col; j <= last_col; j++)
477             if (d_view->isCellSelected(i, j))
478                 exec(new MatrixSetCellValueCmd(d_matrix_private, i, j, 0.0));
479     endMacro();
480     RESET_CURSOR;
481 }
482 
createContextMenu() const483 QMenu *Matrix::createContextMenu() const
484 {
485     QMenu *menu = AbstractPart::createContextMenu();
486     Q_ASSERT(menu);
487     menu->addSeparator();
488 
489     menu->addAction(action_duplicate);
490     // TODO menu->addAction( ....
491 
492     return menu;
493 }
494 
createSelectionMenu(QMenu * append_to)495 QMenu *Matrix::createSelectionMenu(QMenu *append_to)
496 {
497     QMenu *menu = append_to;
498     if (!menu)
499         menu = new QMenu();
500 
501     menu->addAction(action_cut_selection);
502     menu->addAction(action_copy_selection);
503     menu->addAction(action_paste_into_selection);
504     menu->addAction(action_clear_selection);
505 
506     return menu;
507 }
508 
createColumnMenu(QMenu * append_to)509 QMenu *Matrix::createColumnMenu(QMenu *append_to)
510 {
511     QMenu *menu = append_to;
512     if (!menu)
513         menu = new QMenu();
514 
515     menu->addAction(action_insert_columns);
516     menu->addAction(action_remove_columns);
517     menu->addAction(action_clear_columns);
518     menu->addSeparator();
519     menu->addAction(action_edit_coordinates);
520 
521     return menu;
522 }
523 
createMatrixMenu(QMenu * append_to)524 QMenu *Matrix::createMatrixMenu(QMenu *append_to)
525 {
526     QMenu *menu = append_to;
527     if (!menu)
528         menu = new QMenu();
529 
530     menu->addAction(action_toggle_tabbar);
531     menu->addSeparator();
532     menu->addAction(action_select_all);
533     menu->addAction(action_clear_matrix);
534     menu->addSeparator();
535     menu->addAction(action_set_formula);
536     menu->addAction(action_recalculate);
537     menu->addSeparator();
538     menu->addAction(action_edit_format);
539     menu->addSeparator();
540     menu->addAction(action_go_to_cell);
541 
542     return menu;
543 }
544 
createRowMenu(QMenu * append_to)545 QMenu *Matrix::createRowMenu(QMenu *append_to)
546 {
547     QMenu *menu = append_to;
548     if (!menu)
549         menu = new QMenu();
550 
551     menu->addAction(action_insert_rows);
552     menu->addAction(action_remove_rows);
553     menu->addAction(action_clear_rows);
554     menu->addSeparator();
555     menu->addAction(action_edit_coordinates);
556 
557     return menu;
558 }
559 
createActions()560 void Matrix::createActions()
561 {
562     QIcon *icon_temp;
563 
564     // selection related actions
565     action_cut_selection = new QAction(QIcon(QPixmap(":/cut.xpm")), tr("Cu&t"), this);
566     actionManager()->addAction(action_cut_selection, "cut_selection");
567 
568     action_copy_selection = new QAction(QIcon(QPixmap(":/copy.xpm")), tr("&Copy"), this);
569     actionManager()->addAction(action_copy_selection, "copy_selection");
570 
571     action_paste_into_selection = new QAction(QIcon(QPixmap(":/paste.xpm")), tr("Past&e"), this);
572     actionManager()->addAction(action_paste_into_selection, "paste_into_selection");
573 
574     icon_temp = new QIcon();
575     icon_temp->addPixmap(QPixmap(":/16x16/clear.png"));
576     icon_temp->addPixmap(QPixmap(":/32x32/clear.png"));
577     action_clear_selection = new QAction(*icon_temp, tr("Clea&r", "clear selection"), this);
578     actionManager()->addAction(action_clear_selection, "clear_selection");
579     delete icon_temp;
580 
581     // matrix related actions
582     icon_temp = new QIcon();
583     icon_temp->addPixmap(QPixmap(":/16x16/fx.png"));
584     icon_temp->addPixmap(QPixmap(":/32x32/fx.png"));
585     action_set_formula = new QAction(*icon_temp, tr("Assign &Formula"), this);
586     action_set_formula->setShortcut(tr("Alt+Q"));
587     actionManager()->addAction(action_set_formula, "set_formula");
588     delete icon_temp;
589 
590     icon_temp = new QIcon();
591     icon_temp->addPixmap(QPixmap(":/16x16/recalculate.png"));
592     icon_temp->addPixmap(QPixmap(":/32x32/recalculate.png"));
593     action_recalculate = new QAction(*icon_temp, tr("Recalculate"), this);
594     action_recalculate->setShortcut(tr("Ctrl+Return"));
595     actionManager()->addAction(action_recalculate, "recalculate");
596     delete icon_temp;
597 
598     icon_temp = new QIcon();
599     icon_temp->addPixmap(QPixmap(":/16x16/table_options.png"));
600     icon_temp->addPixmap(QPixmap(":/32x32/table_options.png"));
601     action_toggle_tabbar =
602             new QAction(*icon_temp, QString("Show/Hide Controls"), this); // show/hide control tabs
603     action_toggle_tabbar->setShortcut(tr("F12"));
604     actionManager()->addAction(action_toggle_tabbar, "toggle_tabbar");
605     delete icon_temp;
606 
607     icon_temp = new QIcon();
608     icon_temp->addPixmap(QPixmap(":/16x16/select_all.png"));
609     icon_temp->addPixmap(QPixmap(":/32x32/select_all.png"));
610     action_select_all = new QAction(*icon_temp, tr("Select All"), this);
611     actionManager()->addAction(action_select_all, "select_all");
612     delete icon_temp;
613 
614     icon_temp = new QIcon();
615     icon_temp->addPixmap(QPixmap(":/16x16/clear_table.png"));
616     icon_temp->addPixmap(QPixmap(":/32x32/clear_table.png"));
617     action_clear_matrix = new QAction(*icon_temp, tr("Clear Matrix"), this);
618     actionManager()->addAction(action_clear_matrix, "clear_matrix");
619     delete icon_temp;
620 
621     icon_temp = new QIcon();
622     icon_temp->addPixmap(QPixmap(":/16x16/go_to_cell.png"));
623     icon_temp->addPixmap(QPixmap(":/32x32/go_to_cell.png"));
624     action_go_to_cell = new QAction(*icon_temp, tr("&Go to Cell"), this);
625     action_go_to_cell->setShortcut(tr("Ctrl+Alt+G"));
626     actionManager()->addAction(action_go_to_cell, "go_to_cell");
627     delete icon_temp;
628 
629     action_transpose = new QAction(tr("&Transpose"), this);
630     actionManager()->addAction(action_transpose, "transpose");
631 
632     action_mirror_horizontally = new QAction(tr("Mirror &Horizontally"), this);
633     actionManager()->addAction(action_mirror_horizontally, "mirror_horizontally");
634 
635     action_mirror_vertically = new QAction(tr("Mirror &Vertically"), this);
636     actionManager()->addAction(action_mirror_vertically, "mirror_vertically");
637 
638     action_import_image = new QAction(tr("&Import Image", "import image as matrix"), this);
639     actionManager()->addAction(action_import_image, "import_image");
640 
641     action_duplicate = new QAction(QIcon(QPixmap(":/duplicate.xpm")),
642                                    tr("&Duplicate", "duplicate matrix"), this);
643     actionManager()->addAction(action_duplicate, "duplicate");
644 
645     action_dimensions_dialog =
646             new QAction(QIcon(QPixmap(":/resize.xpm")), tr("&Dimensions", "matrix size"), this);
647     actionManager()->addAction(action_dimensions_dialog, "dimensions_dialog");
648 
649     action_edit_coordinates = new QAction(tr("Set &Coordinates"), this);
650     actionManager()->addAction(action_edit_coordinates, "edit_coordinates");
651 
652     action_edit_format = new QAction(tr("Set Display &Format"), this);
653     actionManager()->addAction(action_edit_format, "edit_format");
654 
655     // column related actions
656     icon_temp = new QIcon();
657     icon_temp->addPixmap(QPixmap(":/16x16/insert_column.png"));
658     icon_temp->addPixmap(QPixmap(":/32x32/insert_column.png"));
659     action_insert_columns = new QAction(*icon_temp, tr("&Insert Empty Columns"), this);
660     actionManager()->addAction(action_insert_columns, "insert_columns");
661     delete icon_temp;
662 
663     icon_temp = new QIcon();
664     icon_temp->addPixmap(QPixmap(":/16x16/remove_column.png"));
665     icon_temp->addPixmap(QPixmap(":/32x32/remove_column.png"));
666     action_remove_columns = new QAction(*icon_temp, tr("Remo&ve Columns"), this);
667     actionManager()->addAction(action_remove_columns, "remove_columns");
668     delete icon_temp;
669 
670     icon_temp = new QIcon();
671     icon_temp->addPixmap(QPixmap(":/16x16/clear_column.png"));
672     icon_temp->addPixmap(QPixmap(":/32x32/clear_column.png"));
673     action_clear_columns = new QAction(*icon_temp, tr("Clea&r Columns"), this);
674     actionManager()->addAction(action_clear_columns, "clear_columns");
675     delete icon_temp;
676 
677     icon_temp = new QIcon();
678     icon_temp->addPixmap(QPixmap(":/16x16/add_columns.png"));
679     icon_temp->addPixmap(QPixmap(":/32x32/add_columns.png"));
680     action_add_columns = new QAction(*icon_temp, tr("&Add Columns"), this);
681     actionManager()->addAction(action_add_columns, "add_columns");
682     delete icon_temp;
683 
684     // row related actions
685     icon_temp = new QIcon();
686     icon_temp->addPixmap(QPixmap(":/16x16/insert_row.png"));
687     icon_temp->addPixmap(QPixmap(":/32x32/insert_row.png"));
688     action_insert_rows = new QAction(*icon_temp, tr("&Insert Empty Rows"), this);
689     ;
690     actionManager()->addAction(action_insert_rows, "insert_rows");
691     delete icon_temp;
692 
693     icon_temp = new QIcon();
694     icon_temp->addPixmap(QPixmap(":/16x16/remove_row.png"));
695     icon_temp->addPixmap(QPixmap(":/32x32/remove_row.png"));
696     action_remove_rows = new QAction(*icon_temp, tr("Remo&ve Rows"), this);
697     ;
698     actionManager()->addAction(action_remove_rows, "remove_rows");
699     delete icon_temp;
700 
701     icon_temp = new QIcon();
702     icon_temp->addPixmap(QPixmap(":/16x16/clear_row.png"));
703     icon_temp->addPixmap(QPixmap(":/32x32/clear_row.png"));
704     action_clear_rows = new QAction(*icon_temp, tr("Clea&r Rows"), this);
705     ;
706     actionManager()->addAction(action_clear_rows, "clear_rows");
707     delete icon_temp;
708 
709     icon_temp = new QIcon();
710     icon_temp->addPixmap(QPixmap(":/16x16/add_rows.png"));
711     icon_temp->addPixmap(QPixmap(":/32x32/add_rows.png"));
712     action_add_rows = new QAction(*icon_temp, tr("&Add Rows"), this);
713     ;
714     actionManager()->addAction(action_add_rows, "add_rows");
715     delete icon_temp;
716 }
717 
connectActions()718 void Matrix::connectActions()
719 {
720     connect(action_cut_selection, SIGNAL(triggered()), this, SLOT(cutSelection()));
721     connect(action_copy_selection, SIGNAL(triggered()), this, SLOT(copySelection()));
722     connect(action_paste_into_selection, SIGNAL(triggered()), this, SLOT(pasteIntoSelection()));
723     connect(action_set_formula, SIGNAL(triggered()), this, SLOT(editFormula()));
724     connect(action_edit_coordinates, SIGNAL(triggered()), this, SLOT(editCoordinates()));
725     connect(action_edit_format, SIGNAL(triggered()), this, SLOT(editFormat()));
726     connect(action_clear_selection, SIGNAL(triggered()), this, SLOT(clearSelectedCells()));
727 #ifdef LEGACY_CODE_0_2_x
728     connect(action_recalculate, SIGNAL(triggered()), this, SLOT(recalculateSelectedCells()));
729 #endif
730     connect(action_select_all, SIGNAL(triggered()), this, SLOT(selectAll()));
731     connect(action_clear_matrix, SIGNAL(triggered()), this, SLOT(clear()));
732     connect(action_transpose, SIGNAL(triggered()), this, SLOT(transpose()));
733     connect(action_mirror_horizontally, SIGNAL(triggered()), this, SLOT(mirrorHorizontally()));
734     connect(action_mirror_vertically, SIGNAL(triggered()), this, SLOT(mirrorVertically()));
735     connect(action_go_to_cell, SIGNAL(triggered()), this, SLOT(goToCell()));
736     connect(action_dimensions_dialog, SIGNAL(triggered()), this, SLOT(dimensionsDialog()));
737     connect(action_import_image, SIGNAL(triggered()), this, SLOT(importImageDialog()));
738     connect(action_duplicate, SIGNAL(triggered()), this, SLOT(duplicate()));
739     connect(action_insert_columns, SIGNAL(triggered()), this, SLOT(insertEmptyColumns()));
740     connect(action_remove_columns, SIGNAL(triggered()), this, SLOT(removeSelectedColumns()));
741     connect(action_clear_columns, SIGNAL(triggered()), this, SLOT(clearSelectedColumns()));
742     connect(action_insert_rows, SIGNAL(triggered()), this, SLOT(insertEmptyRows()));
743     connect(action_remove_rows, SIGNAL(triggered()), this, SLOT(removeSelectedRows()));
744     connect(action_clear_rows, SIGNAL(triggered()), this, SLOT(clearSelectedRows()));
745     connect(action_add_columns, SIGNAL(triggered()), this, SLOT(addColumns()));
746     connect(action_add_rows, SIGNAL(triggered()), this, SLOT(addRows()));
747 }
748 
addActionsToView()749 void Matrix::addActionsToView()
750 {
751     connect(action_toggle_tabbar, SIGNAL(triggered()), d_view, SLOT(toggleControlTabBar()));
752 
753     d_view->addAction(action_cut_selection);
754     d_view->addAction(action_copy_selection);
755     d_view->addAction(action_paste_into_selection);
756     d_view->addAction(action_set_formula);
757     d_view->addAction(action_edit_coordinates);
758     d_view->addAction(action_edit_format);
759     d_view->addAction(action_clear_selection);
760     d_view->addAction(action_recalculate);
761     d_view->addAction(action_toggle_tabbar);
762     d_view->addAction(action_select_all);
763     d_view->addAction(action_clear_matrix);
764     d_view->addAction(action_transpose);
765     d_view->addAction(action_mirror_horizontally);
766     d_view->addAction(action_mirror_vertically);
767     d_view->addAction(action_go_to_cell);
768     d_view->addAction(action_dimensions_dialog);
769     d_view->addAction(action_import_image);
770 #ifndef LEGACY_CODE_0_2_x
771     d_view->addAction(action_duplicate);
772 #endif
773     d_view->addAction(action_insert_columns);
774     d_view->addAction(action_remove_columns);
775     d_view->addAction(action_clear_columns);
776     d_view->addAction(action_insert_rows);
777     d_view->addAction(action_remove_rows);
778     d_view->addAction(action_clear_rows);
779     d_view->addAction(action_add_columns);
780     d_view->addAction(action_add_rows);
781 }
782 
translateActionsStrings()783 void Matrix::translateActionsStrings()
784 {
785     action_cut_selection->setText(tr("Cu&t"));
786     action_copy_selection->setText(tr("&Copy"));
787     action_paste_into_selection->setText(tr("Past&e"));
788     action_clear_selection->setText(tr("Clea&r", "clear selection"));
789     action_set_formula->setText(tr("Assign &Formula"));
790     action_recalculate->setText(tr("Recalculate"));
791     action_select_all->setText(tr("Select All"));
792     action_clear_matrix->setText(tr("Clear Matrix"));
793     action_go_to_cell->setText(tr("&Go to Cell"));
794     action_transpose->setText(tr("&Transpose"));
795     action_mirror_horizontally->setText(tr("Mirror &Horizontally"));
796     action_mirror_vertically->setText(tr("Mirror &Vertically"));
797     action_import_image->setText(tr("&Import Image", "import image as matrix"));
798 #ifndef LEGACY_CODE_0_2_x
799     action_duplicate->setText(tr("&Duplicate", "duplicate matrix"));
800 #endif
801     action_dimensions_dialog->setText(tr("&Dimensions", "matrix size"));
802     action_edit_coordinates->setText(tr("Set &Coordinates"));
803     action_edit_format->setText(tr("Set Display &Format"));
804     action_insert_columns->setText(tr("&Insert Empty Columns"));
805     action_remove_columns->setText(tr("Remo&ve Columns"));
806     action_clear_columns->setText(tr("Clea&r Columns"));
807     action_add_columns->setText(tr("&Add Columns"));
808     action_insert_rows->setText(tr("&Insert Empty Rows"));
809     action_remove_rows->setText(tr("Remo&ve Rows"));
810     action_clear_rows->setText(tr("Clea&r Rows"));
811     action_add_rows->setText(tr("&Add Rows"));
812 }
813 
fillProjectMenu(QMenu * menu)814 bool Matrix::fillProjectMenu(QMenu *menu)
815 {
816     menu->setTitle(tr("&Matrix"));
817 
818     menu->addAction(action_toggle_tabbar);
819     menu->addSeparator();
820     menu->addAction(action_edit_coordinates);
821     menu->addAction(action_dimensions_dialog);
822     menu->addAction(action_edit_format);
823     menu->addSeparator();
824     menu->addAction(action_set_formula);
825     menu->addAction(action_recalculate);
826     menu->addSeparator();
827     menu->addAction(action_clear_matrix);
828     menu->addAction(action_transpose);
829     menu->addAction(action_mirror_horizontally);
830     menu->addAction(action_mirror_vertically);
831     menu->addSeparator();
832 #ifndef LEGACY_CODE_0_2_x
833     menu->addAction(action_duplicate);
834 #endif
835     menu->addAction(action_import_image);
836     menu->addSeparator();
837     menu->addAction(action_go_to_cell);
838     if (action_clear_matrix->text() != tr("Clear Matrix")) {
839         translateActionsStrings();
840         adjustTabBarAction(d_view->isControlTabBarVisible());
841     }
842 
843     return true;
844 
845     // TODO:
846     // Convert to Table
847     // Export
848 }
849 
showMatrixViewContextMenu(const QPoint & pos)850 void Matrix::showMatrixViewContextMenu(const QPoint &pos)
851 {
852     if (!d_view)
853         return;
854     QMenu context_menu;
855 
856     createSelectionMenu(&context_menu);
857     context_menu.addSeparator();
858     createMatrixMenu(&context_menu);
859     context_menu.addSeparator();
860 
861     context_menu.exec(pos);
862 }
863 
showMatrixViewColumnContextMenu(const QPoint & pos)864 void Matrix::showMatrixViewColumnContextMenu(const QPoint &pos)
865 {
866     QMenu context_menu;
867 
868     createColumnMenu(&context_menu);
869 
870     context_menu.exec(pos);
871 }
872 
showMatrixViewRowContextMenu(const QPoint & pos)873 void Matrix::showMatrixViewRowContextMenu(const QPoint &pos)
874 {
875     QMenu context_menu;
876 
877     createRowMenu(&context_menu);
878 
879     context_menu.exec(pos);
880 }
881 
goToCell()882 void Matrix::goToCell()
883 {
884     if (!d_view)
885         return;
886     bool ok;
887 
888     int col = QInputDialog::getInt(0, tr("Go to Cell"), tr("Enter column"), 1, 1, columnCount(), 1,
889                                    &ok);
890     if (!ok)
891         return;
892 
893     int row = QInputDialog::getInt(0, tr("Go to Cell"), tr("Enter row"), 1, 1, rowCount(), 1, &ok);
894     if (!ok)
895         return;
896 
897     d_view->goToCell(row - 1, col - 1);
898 }
899 
copy(Matrix * other)900 void Matrix::copy(Matrix *other)
901 {
902     WAIT_CURSOR;
903     beginMacro(QObject::tr("%1: copy %2").arg(name()).arg(other->name()));
904     int rows = other->rowCount();
905     int columns = other->columnCount();
906     setDimensions(rows, columns);
907     for (int i = 0; i < rows; i++)
908         setRowHeight(i, other->rowHeight(i));
909     for (int i = 0; i < columns; i++)
910         setColumnWidth(i, other->columnWidth(i));
911     d_matrix_private->blockChangeSignals(true);
912     for (int i = 0; i < columns; i++)
913         setColumnCells(i, 0, rows - 1, other->columnCells(i, 0, rows - 1));
914     setCoordinates(other->xStart(), other->xEnd(), other->yStart(), other->yEnd());
915     setNumericFormat(other->numericFormat());
916     setDisplayedDigits(other->displayedDigits());
917     setFormula(other->formula());
918     d_matrix_private->blockChangeSignals(false);
919     emit dataChanged(0, 0, rows - 1, columns - 1);
920     if (d_view)
921         d_view->rereadSectionSizes();
922     endMacro();
923     RESET_CURSOR;
924 }
925 
setPlotMenu(QMenu * menu)926 void Matrix::setPlotMenu(QMenu *menu)
927 {
928     d_plot_menu = menu;
929 }
930 
icon() const931 QIcon Matrix::icon() const
932 {
933     QIcon ico;
934     ico.addPixmap(QPixmap(":/16x16/matrix.png"));
935     ico.addPixmap(QPixmap(":/24x24/matrix.png"));
936     ico.addPixmap(QPixmap(":/32x32/matrix.png"));
937     return ico;
938 }
939 
text(int row,int col)940 QString Matrix::text(int row, int col)
941 {
942     return QLocale().toString(cell(row, col), d_matrix_private->numericFormat(),
943                               d_matrix_private->displayedDigits());
944 }
945 
selectAll()946 void Matrix::selectAll()
947 {
948     if (!d_view)
949         return;
950     d_view->selectAll();
951 }
952 
setCell(int row,int col,double value)953 void Matrix::setCell(int row, int col, double value)
954 {
955     if (row < 0 || row >= rowCount())
956         return;
957     if (col < 0 || col >= columnCount())
958         return;
959     exec(new MatrixSetCellValueCmd(d_matrix_private, row, col, value));
960 }
961 
setCells(const QVector<qreal> & data)962 void Matrix::setCells(const QVector<qreal> &data)
963 {
964     d_matrix_private->setCells(data);
965 }
966 
dimensionsDialog()967 void Matrix::dimensionsDialog()
968 {
969     bool ok;
970 
971     int cols = QInputDialog::getInt(0, tr("Set Matrix Dimensions"), tr("Enter number of columns"),
972                                     columnCount(), 1, 1e9, 1, &ok);
973     if (!ok)
974         return;
975 
976     int rows = QInputDialog::getInt(0, tr("Set Matrix Dimensions"), tr("Enter number of rows"),
977                                     rowCount(), 1, 1e9, 1, &ok);
978     if (!ok)
979         return;
980 
981     setDimensions(rows, cols);
982 }
983 
importImageDialog()984 void Matrix::importImageDialog()
985 {
986     QList<QByteArray> formats = QImageReader::supportedImageFormats();
987     QString filter = tr("Images") + " (";
988     for (int i = 0; i < formats.count(); i++)
989         filter += " *." + formats.at(i) + " ";
990     filter += ");;";
991     for (int i = 0; i < formats.count(); i++)
992         filter += " *." + formats.at(i) + " (*." + formats.at(i) + ");;";
993 
994     QString images_path = global("images_path").toString();
995     QString file_name =
996             QFileDialog::getOpenFileName(0, tr("Import image from file"), images_path, filter);
997     if (!file_name.isEmpty()) {
998         QFileInfo file_info(file_name);
999         images_path = file_info.canonicalPath();
1000         setGlobal("images_path", images_path);
1001         QImage image(file_name);
1002         Matrix *matrix = NULL;
1003         if (!image.isNull())
1004             matrix = Matrix::fromImage(image);
1005         if (matrix) {
1006             copy(matrix);
1007             delete matrix;
1008         } else
1009             QMessageBox::information(0, tr("Error importing image"),
1010                                      tr("Import of image '%1' failed").arg(file_name));
1011     }
1012 }
1013 
duplicate()1014 void Matrix::duplicate()
1015 {
1016 #ifndef LEGACY_CODE_0_2_x
1017     Matrix *matrix = new Matrix(0, rowCount(), columnCount(), name());
1018     matrix->copy(this);
1019     if (folder())
1020         folder()->addChild(matrix);
1021 #endif
1022 }
1023 
editFormat()1024 void Matrix::editFormat()
1025 {
1026     if (!d_view)
1027         return;
1028     d_view->showControlFormatTab();
1029 }
1030 
editCoordinates()1031 void Matrix::editCoordinates()
1032 {
1033     if (!d_view)
1034         return;
1035     d_view->showControlCoordinatesTab();
1036 }
1037 
editFormula()1038 void Matrix::editFormula()
1039 {
1040     if (!d_view)
1041         return;
1042     d_view->showControlFormulaTab();
1043 }
1044 
addRows()1045 void Matrix::addRows()
1046 {
1047     if (!d_view)
1048         return;
1049     WAIT_CURSOR;
1050     int count = d_view->selectedRowCount(false);
1051     beginMacro(QObject::tr("%1: add %2 rows(s)").arg(name()).arg(count));
1052     exec(new MatrixInsertRowsCmd(d_matrix_private, rowCount(), count));
1053     endMacro();
1054     RESET_CURSOR;
1055 }
1056 
addColumns()1057 void Matrix::addColumns()
1058 {
1059     if (!d_view)
1060         return;
1061     WAIT_CURSOR;
1062     int count = d_view->selectedRowCount(false);
1063     beginMacro(QObject::tr("%1: add %2 column(s)").arg(name()).arg(count));
1064     exec(new MatrixInsertColumnsCmd(d_matrix_private, columnCount(), count));
1065     endMacro();
1066     RESET_CURSOR;
1067 }
1068 
setXStart(double x)1069 void Matrix::setXStart(double x)
1070 {
1071     WAIT_CURSOR;
1072     exec(new MatrixSetCoordinatesCmd(d_matrix_private, x, xEnd(), yStart(), yEnd()));
1073     RESET_CURSOR;
1074 }
1075 
setXEnd(double x)1076 void Matrix::setXEnd(double x)
1077 {
1078     WAIT_CURSOR;
1079     exec(new MatrixSetCoordinatesCmd(d_matrix_private, xStart(), x, yStart(), yEnd()));
1080     RESET_CURSOR;
1081 }
1082 
setYStart(double y)1083 void Matrix::setYStart(double y)
1084 {
1085     WAIT_CURSOR;
1086     exec(new MatrixSetCoordinatesCmd(d_matrix_private, xStart(), xEnd(), y, yEnd()));
1087     RESET_CURSOR;
1088 }
1089 
setYEnd(double y)1090 void Matrix::setYEnd(double y)
1091 {
1092     WAIT_CURSOR;
1093     exec(new MatrixSetCoordinatesCmd(d_matrix_private, xStart(), xEnd(), yStart(), y));
1094     RESET_CURSOR;
1095 }
1096 
setCoordinates(double x1,double x2,double y1,double y2)1097 void Matrix::setCoordinates(double x1, double x2, double y1, double y2)
1098 {
1099     WAIT_CURSOR;
1100     exec(new MatrixSetCoordinatesCmd(d_matrix_private, x1, x2, y1, y2));
1101     RESET_CURSOR;
1102 }
1103 
setNumericFormat(char format)1104 void Matrix::setNumericFormat(char format)
1105 {
1106     if (format == numericFormat())
1107         return;
1108     WAIT_CURSOR;
1109     exec(new MatrixSetFormatCmd(d_matrix_private, format));
1110     RESET_CURSOR;
1111 }
1112 
setDisplayedDigits(int digits)1113 void Matrix::setDisplayedDigits(int digits)
1114 {
1115     if (digits == displayedDigits())
1116         return;
1117     WAIT_CURSOR;
1118     exec(new MatrixSetDigitsCmd(d_matrix_private, digits));
1119     RESET_CURSOR;
1120 }
1121 
xStart() const1122 double Matrix::xStart() const
1123 {
1124     return d_matrix_private->xStart();
1125 }
1126 
yStart() const1127 double Matrix::yStart() const
1128 {
1129     return d_matrix_private->yStart();
1130 }
1131 
xEnd() const1132 double Matrix::xEnd() const
1133 {
1134     return d_matrix_private->xEnd();
1135 }
1136 
yEnd() const1137 double Matrix::yEnd() const
1138 {
1139     return d_matrix_private->yEnd();
1140 }
1141 
formula() const1142 QString Matrix::formula() const
1143 {
1144     return d_matrix_private->formula();
1145 }
1146 
setFormula(const QString & formula)1147 void Matrix::setFormula(const QString &formula)
1148 {
1149     WAIT_CURSOR;
1150     exec(new MatrixSetFormulaCmd(d_matrix_private, formula));
1151     RESET_CURSOR;
1152 }
1153 
numericFormat() const1154 char Matrix::numericFormat() const
1155 {
1156     return d_matrix_private->numericFormat();
1157 }
1158 
displayedDigits() const1159 int Matrix::displayedDigits() const
1160 {
1161     return d_matrix_private->displayedDigits();
1162 }
1163 
save(QXmlStreamWriter * writer) const1164 void Matrix::save(QXmlStreamWriter *writer) const
1165 {
1166     int cols = columnCount();
1167     int rows = rowCount();
1168     writer->writeStartElement("matrix");
1169     writeBasicAttributes(writer);
1170     writer->writeAttribute("columns", QString::number(cols));
1171     writer->writeAttribute("rows", QString::number(rows));
1172     writeCommentElement(writer);
1173     writer->writeStartElement("formula");
1174     writer->writeCharacters(formula());
1175     writer->writeEndElement();
1176     writer->writeStartElement("display");
1177     writer->writeAttribute("numeric_format", QString(QChar(numericFormat())));
1178     writer->writeAttribute("displayed_digits", QString::number(displayedDigits()));
1179     writer->writeEndElement();
1180     writer->writeStartElement("coordinates");
1181     writer->writeAttribute("x_start", QString::number(xStart()));
1182     writer->writeAttribute("x_end", QString::number(xEnd()));
1183     writer->writeAttribute("y_start", QString::number(yStart()));
1184     writer->writeAttribute("y_end", QString::number(yEnd()));
1185     writer->writeEndElement();
1186 
1187     for (int col = 0; col < cols; col++)
1188         for (int row = 0; row < rows; row++) {
1189             writer->writeStartElement("cell");
1190             writer->writeAttribute("row", QString::number(row));
1191             writer->writeAttribute("column", QString::number(col));
1192             writer->writeCharacters(QString::number(cell(row, col), 'e', 16));
1193             writer->writeEndElement();
1194         }
1195     for (int col = 0; col < cols; col++) {
1196         writer->writeStartElement("column_width");
1197         writer->writeAttribute("column", QString::number(col));
1198         writer->writeCharacters(QString::number(columnWidth(col)));
1199         writer->writeEndElement();
1200     }
1201     for (int row = 0; row < rows; row++) {
1202         writer->writeStartElement("row_height");
1203         writer->writeAttribute("row", QString::number(row));
1204         writer->writeCharacters(QString::number(rowHeight(row)));
1205         writer->writeEndElement();
1206     }
1207     writer->writeEndElement(); // "matrix"
1208 }
1209 
load(XmlStreamReader * reader)1210 bool Matrix::load(XmlStreamReader *reader)
1211 {
1212     if (reader->isStartElement() && reader->name() == "matrix") {
1213         setDimensions(0, 0);
1214         setComment("");
1215         setFormula("");
1216         setNumericFormat('f');
1217         setDisplayedDigits(6);
1218         setCoordinates(0.0, 1.0, 0.0, 1.0);
1219 
1220         if (!readBasicAttributes(reader))
1221             return false;
1222 
1223         // read dimensions
1224         bool ok1, ok2;
1225         int rows, cols;
1226         rows = reader->readAttributeInt("rows", &ok1);
1227         cols = reader->readAttributeInt("columns", &ok2);
1228         if (!ok1 || !ok2) {
1229             reader->raiseError(tr("invalid row or column count"));
1230             return false;
1231         }
1232         d_matrix_private->blockChangeSignals(true);
1233         setDimensions(rows, cols);
1234 
1235         // read child elements
1236         while (!reader->atEnd()) {
1237             reader->readNext();
1238 
1239             if (reader->isEndElement())
1240                 break;
1241 
1242             if (reader->isStartElement()) {
1243                 bool ret_val = true;
1244                 if (reader->name() == "comment")
1245                     ret_val = readCommentElement(reader);
1246                 else if (reader->name() == "formula")
1247                     ret_val = readFormulaElement(reader);
1248                 else if (reader->name() == "display")
1249                     ret_val = readDisplayElement(reader);
1250                 else if (reader->name() == "coordinates")
1251                     ret_val = readCoordinatesElement(reader);
1252                 else if (reader->name() == "cell")
1253                     ret_val = readCellElement(reader);
1254                 else if (reader->name() == "row_height")
1255                     ret_val = readRowHeightElement(reader);
1256                 else if (reader->name() == "column_width")
1257                     ret_val = readColumnWidthElement(reader);
1258                 else // unknown element
1259                 {
1260                     reader->raiseWarning(tr("unknown element '%1'").arg(reader->name().toString()));
1261                     if (!reader->skipToEndElement())
1262                         return false;
1263                 }
1264                 if (!ret_val)
1265                     return false;
1266             }
1267         }
1268         d_matrix_private->blockChangeSignals(false);
1269     } else // no matrix element
1270         reader->raiseError(tr("no matrix element found"));
1271 
1272     return !reader->hasError();
1273 }
1274 
readDisplayElement(XmlStreamReader * reader)1275 bool Matrix::readDisplayElement(XmlStreamReader *reader)
1276 {
1277     Q_ASSERT(reader->isStartElement() && reader->name() == "display");
1278     QXmlStreamAttributes attribs = reader->attributes();
1279 
1280     QString str = attribs.value(reader->namespaceUri().toString(), "numeric_format").toString();
1281     if (str.isEmpty() || str.length() != 1) {
1282         reader->raiseError(tr("invalid or missing numeric format"));
1283         return false;
1284     }
1285     setNumericFormat(str.at(0).toLatin1());
1286 
1287     bool ok;
1288     int digits = reader->readAttributeInt("displayed_digits", &ok);
1289     if (!ok) {
1290         reader->raiseError(tr("invalid or missing number of displayed digits"));
1291         return false;
1292     }
1293     setDisplayedDigits(digits);
1294     if (!reader->skipToEndElement())
1295         return false;
1296 
1297     return true;
1298 }
1299 
readCoordinatesElement(XmlStreamReader * reader)1300 bool Matrix::readCoordinatesElement(XmlStreamReader *reader)
1301 {
1302     Q_ASSERT(reader->isStartElement() && reader->name() == "coordinates");
1303 
1304     bool ok;
1305     double val;
1306 
1307     val = reader->readAttributeDouble("x_start", &ok);
1308     if (!ok) {
1309         reader->raiseError(tr("invalid x start value"));
1310         return false;
1311     }
1312     setXStart(val);
1313 
1314     val = reader->readAttributeDouble("x_end", &ok);
1315     if (!ok) {
1316         reader->raiseError(tr("invalid x end value"));
1317         return false;
1318     }
1319     setXEnd(val);
1320 
1321     val = reader->readAttributeDouble("y_start", &ok);
1322     if (!ok) {
1323         reader->raiseError(tr("invalid y start value"));
1324         return false;
1325     }
1326     setYStart(val);
1327 
1328     val = reader->readAttributeDouble("y_end", &ok);
1329     if (!ok) {
1330         reader->raiseError(tr("invalid y end value"));
1331         return false;
1332     }
1333     setYEnd(val);
1334     if (!reader->skipToEndElement())
1335         return false;
1336 
1337     return true;
1338 }
1339 
readFormulaElement(XmlStreamReader * reader)1340 bool Matrix::readFormulaElement(XmlStreamReader *reader)
1341 {
1342     Q_ASSERT(reader->isStartElement() && reader->name() == "formula");
1343     setFormula(reader->readElementText());
1344     return true;
1345 }
1346 
readRowHeightElement(XmlStreamReader * reader)1347 bool Matrix::readRowHeightElement(XmlStreamReader *reader)
1348 {
1349     Q_ASSERT(reader->isStartElement() && reader->name() == "row_height");
1350     bool ok;
1351     int row = reader->readAttributeInt("row", &ok);
1352     if (!ok) {
1353         reader->raiseError(tr("invalid or missing row index"));
1354         return false;
1355     }
1356     QString str = reader->readElementText();
1357     int value = str.toInt(&ok);
1358     if (!ok) {
1359         reader->raiseError(tr("invalid row height"));
1360         return false;
1361     }
1362     if (d_view)
1363         d_view->setRowHeight(row, value);
1364     else
1365         setRowHeight(row, value);
1366     return true;
1367 }
1368 
readColumnWidthElement(XmlStreamReader * reader)1369 bool Matrix::readColumnWidthElement(XmlStreamReader *reader)
1370 {
1371     Q_ASSERT(reader->isStartElement() && reader->name() == "column_width");
1372     bool ok;
1373     int col = reader->readAttributeInt("column", &ok);
1374     if (!ok) {
1375         reader->raiseError(tr("invalid or missing column index"));
1376         return false;
1377     }
1378     QString str = reader->readElementText();
1379     int value = str.toInt(&ok);
1380     if (!ok) {
1381         reader->raiseError(tr("invalid column width"));
1382         return false;
1383     }
1384     if (d_view)
1385         d_view->setColumnWidth(col, value);
1386     else
1387         setColumnWidth(col, value);
1388     return true;
1389 }
1390 
readCellElement(XmlStreamReader * reader)1391 bool Matrix::readCellElement(XmlStreamReader *reader)
1392 {
1393     Q_ASSERT(reader->isStartElement() && reader->name() == "cell");
1394 
1395     QString str;
1396     int row, col;
1397     bool ok;
1398 
1399     QXmlStreamAttributes attribs = reader->attributes();
1400     row = reader->readAttributeInt("row", &ok);
1401     if (!ok) {
1402         reader->raiseError(tr("invalid or missing row index"));
1403         return false;
1404     }
1405     col = reader->readAttributeInt("column", &ok);
1406     if (!ok) {
1407         reader->raiseError(tr("invalid or missing column index"));
1408         return false;
1409     }
1410 
1411     str = reader->readElementText();
1412     double value = str.toDouble(&ok);
1413     if (!ok) {
1414         reader->raiseError(tr("invalid cell value"));
1415         return false;
1416     }
1417     setCell(row, col, value);
1418 
1419     return true;
1420 }
1421 
setRowHeight(int row,int height)1422 void Matrix::setRowHeight(int row, int height)
1423 {
1424     d_matrix_private->setRowHeight(row, height);
1425 }
1426 
setColumnWidth(int col,int width)1427 void Matrix::setColumnWidth(int col, int width)
1428 {
1429     d_matrix_private->setColumnWidth(col, width);
1430 }
1431 
rowHeight(int row) const1432 int Matrix::rowHeight(int row) const
1433 {
1434     return d_matrix_private->rowHeight(row);
1435 }
1436 
columnWidth(int col) const1437 int Matrix::columnWidth(int col) const
1438 {
1439     return d_matrix_private->columnWidth(col);
1440 }
1441 
adjustTabBarAction(bool visible)1442 void Matrix::adjustTabBarAction(bool visible)
1443 {
1444     if (visible)
1445         action_toggle_tabbar->setText(tr("Hide Controls"));
1446     else
1447         action_toggle_tabbar->setText(tr("Show Controls"));
1448 }
1449 
columnCells(int col,int first_row,int last_row)1450 QVector<qreal> Matrix::columnCells(int col, int first_row, int last_row)
1451 {
1452     return d_matrix_private->columnCells(col, first_row, last_row);
1453 }
1454 
setColumnCells(int col,int first_row,int last_row,const QVector<qreal> & values)1455 void Matrix::setColumnCells(int col, int first_row, int last_row, const QVector<qreal> &values)
1456 {
1457     WAIT_CURSOR;
1458     exec(new MatrixSetColumnCellsCmd(d_matrix_private, col, first_row, last_row, values));
1459     RESET_CURSOR;
1460 }
1461 
rowCells(int row,int first_column,int last_column)1462 QVector<qreal> Matrix::rowCells(int row, int first_column, int last_column)
1463 {
1464     return d_matrix_private->rowCells(row, first_column, last_column);
1465 }
1466 
setRowCells(int row,int first_column,int last_column,const QVector<qreal> & values)1467 void Matrix::setRowCells(int row, int first_column, int last_column, const QVector<qreal> &values)
1468 {
1469     WAIT_CURSOR;
1470     exec(new MatrixSetRowCellsCmd(d_matrix_private, row, first_column, last_column, values));
1471     RESET_CURSOR;
1472 }
1473 
transpose()1474 void Matrix::transpose()
1475 {
1476     WAIT_CURSOR;
1477     exec(new MatrixTransposeCmd(d_matrix_private));
1478     RESET_CURSOR;
1479 }
1480 
1481 std::vector<std::vector<std::pair<double, bool>>>
getCells(const int startRow,const int endRow,const int startCol,const int endCol) const1482 Matrix::getCells(const int startRow, const int endRow, const int startCol, const int endCol) const
1483 {
1484     return d_matrix_private->getCells(startRow, endRow, startCol, endCol);
1485 }
1486 
setCells(const int startRow,const int startCol,const std::vector<std::vector<std::pair<double,bool>>> & values)1487 void Matrix::setCells(
1488         const int startRow, const int startCol,
1489                       const std::vector<std::vector<std::pair<double, bool>>> &values)
1490 {
1491     WAIT_CURSOR;
1492     auto lastRow = startRow + values.size() - 1;
1493     auto lastCol = startCol
1494             + std::max_element(values.cbegin(), values.cend(),
1495                                [](const std::vector<std::pair<double, bool>> &a,
1496                                   const std::vector<std::pair<double, bool>> &b) {
1497                                    return a.size() > b.size();
1498                                })
1499                       ->size() - 1;
1500     exec(new MatrixSetCellsCmd(d_matrix_private, startRow, lastRow, startCol, lastCol, values));
1501     RESET_CURSOR;
1502 
1503 }
1504 
mirrorHorizontally()1505 void Matrix::mirrorHorizontally()
1506 {
1507     WAIT_CURSOR;
1508     exec(new MatrixMirrorHorizontallyCmd(d_matrix_private));
1509     RESET_CURSOR;
1510 }
1511 
mirrorVertically()1512 void Matrix::mirrorVertically()
1513 {
1514     WAIT_CURSOR;
1515     exec(new MatrixMirrorVerticallyCmd(d_matrix_private));
1516     RESET_CURSOR;
1517 }
1518 
recalculateSelectedCells()1519 void Matrix::recalculateSelectedCells()
1520 {
1521     if (!d_view)
1522         return;
1523 #ifdef LEGACY_CODE_0_2_x
1524     WAIT_CURSOR;
1525     beginMacro(tr("%1: apply formula to selection").arg(name()));
1526     emit recalculate();
1527     endMacro();
1528     RESET_CURSOR;
1529 #endif
1530 }
1531 
1532 /* ========================= static methods ======================= */
1533 ActionManager *Matrix::action_manager = 0;
1534 
actionManager()1535 ActionManager *Matrix::actionManager()
1536 {
1537     if (!action_manager)
1538         initActionManager();
1539 
1540     return action_manager;
1541 }
1542 
initActionManager()1543 void Matrix::initActionManager()
1544 {
1545     if (!action_manager)
1546         action_manager = new ActionManager();
1547 
1548     action_manager->setTitle(tr("Matrix"));
1549     volatile Matrix *action_creator = new Matrix(); // initialize the action texts
1550     delete action_creator;
1551 }
1552 
fromImage(const QImage & image)1553 Matrix *Matrix::fromImage(const QImage &image)
1554 {
1555     int cols = image.width();
1556     int rows = image.height();
1557 
1558     QProgressDialog progress;
1559     progress.setRange(0, cols);
1560     progress.setWindowTitle(tr("SciDAVis") + " - " + tr("Import image..."));
1561     progress.raise();
1562 
1563     Matrix *matrix = new Matrix(0, rows, cols, tr("Matrix %1").arg(1));
1564 
1565     QVector<qreal> values;
1566     values.resize(rows);
1567 
1568     for (int i = 0; i < cols; i++) {
1569         for (int j = 0; j < rows; j++)
1570             values[j] = qGray(image.pixel(i, rows - 1 - j));
1571 
1572         matrix->setColumnCells(i, 0, rows - 1, values);
1573 
1574         if (i % 5 == 4) {
1575             progress.setValue(i);
1576             QApplication::processEvents();
1577         }
1578 
1579         if (progress.wasCanceled())
1580             break;
1581     }
1582 
1583     if (progress.wasCanceled()) {
1584         delete matrix;
1585         return NULL;
1586     }
1587     return matrix;
1588 }
1589 
1590 /* ========================== Matrix::Private ====================== */
1591 
Private(Matrix * owner)1592 Matrix::Private::Private(Matrix *owner) : d_owner(owner), d_column_count(0), d_row_count(0)
1593 {
1594     d_block_change_signals = false;
1595     d_numeric_format = 'f';
1596     d_displayed_digits = 6;
1597     d_x_start = 0.0;
1598     d_x_end = 1.0;
1599     d_y_start = 0.0;
1600     d_y_end = 1.0;
1601 }
1602 
insertColumns(int before,int count)1603 void Matrix::Private::insertColumns(int before, int count)
1604 {
1605     Q_ASSERT(before >= 0);
1606     Q_ASSERT(before <= d_column_count);
1607 
1608     emit d_owner->columnsAboutToBeInserted(before, count);
1609     for (int i = 0; i < count; i++) {
1610         d_data.insert(before + i, QVector<qreal>(d_row_count));
1611         d_column_widths.insert(before + i, Matrix::defaultColumnWidth());
1612     }
1613 
1614     d_column_count += count;
1615     emit d_owner->columnsInserted(before, count);
1616 }
1617 
removeColumns(int first,int count)1618 void Matrix::Private::removeColumns(int first, int count)
1619 {
1620     emit d_owner->columnsAboutToBeRemoved(first, count);
1621     Q_ASSERT(first >= 0);
1622     Q_ASSERT(first + count <= d_column_count);
1623     d_data.remove(first, count);
1624     for (int i = 0; i < count; i++)
1625         d_column_widths.removeAt(first);
1626     d_column_count -= count;
1627     emit d_owner->columnsRemoved(first, count);
1628 }
1629 
insertRows(int before,int count)1630 void Matrix::Private::insertRows(int before, int count)
1631 {
1632     emit d_owner->rowsAboutToBeInserted(before, count);
1633     Q_ASSERT(before >= 0);
1634     Q_ASSERT(before <= d_row_count);
1635     for (int col = 0; col < d_column_count; col++)
1636         for (int i = 0; i < count; i++)
1637             d_data[col].insert(before + i, 0.0);
1638     for (int i = 0; i < count; i++)
1639         d_row_heights.insert(before + i, Matrix::defaultRowHeight());
1640 
1641     d_row_count += count;
1642     emit d_owner->rowsInserted(before, count);
1643 }
1644 
removeRows(int first,int count)1645 void Matrix::Private::removeRows(int first, int count)
1646 {
1647     emit d_owner->rowsAboutToBeRemoved(first, count);
1648     Q_ASSERT(first >= 0);
1649     Q_ASSERT(first + count <= d_row_count);
1650     for (int col = 0; col < d_column_count; col++)
1651         d_data[col].remove(first, count);
1652     for (int i = 0; i < count; i++)
1653         d_row_heights.removeAt(first);
1654 
1655     d_row_count -= count;
1656     emit d_owner->rowsRemoved(first, count);
1657 }
1658 
cell(int row,int col) const1659 double Matrix::Private::cell(int row, int col) const
1660 {
1661     Q_ASSERT(row >= 0 && row < d_row_count);
1662     Q_ASSERT(col >= 0 && col < d_column_count);
1663     return d_data.at(col).at(row);
1664 }
1665 
setCell(int row,int col,double value)1666 void Matrix::Private::setCell(int row, int col, double value)
1667 {
1668     Q_ASSERT(row >= 0 && row < d_row_count);
1669     Q_ASSERT(col >= 0 && col < d_column_count);
1670     d_data[col][row] = value;
1671     if (!d_block_change_signals)
1672         emit d_owner->dataChanged(row, col, row, col);
1673 }
1674 
setCells(const QVector<qreal> & data)1675 void Matrix::Private::setCells(const QVector<qreal> &data)
1676 {
1677     if (rowCount() * columnCount() != data.size())
1678         return;
1679     int k = 0;
1680     for (int i = 0; i < columnCount(); i++) {
1681         for (int j = 0; j < rowCount(); j++) {
1682             d_data[i][j] = data[k++];
1683         }
1684     }
1685 }
1686 
columnCells(int col,int first_row,int last_row)1687 QVector<qreal> Matrix::Private::columnCells(int col, int first_row, int last_row)
1688 {
1689     Q_ASSERT(first_row >= 0 && first_row < d_row_count);
1690     Q_ASSERT(last_row >= 0 && last_row < d_row_count);
1691 
1692     if (first_row == 0 && last_row == d_row_count - 1)
1693         return d_data.at(col);
1694 
1695     QVector<qreal> result;
1696     for (int i = first_row; i <= last_row; i++)
1697         result.append(d_data.at(col).at(i));
1698     return result;
1699 }
1700 
setColumnCells(int col,int first_row,int last_row,const QVector<qreal> & values)1701 void Matrix::Private::setColumnCells(int col, int first_row, int last_row,
1702                                      const QVector<qreal> &values)
1703 {
1704     Q_ASSERT(first_row >= 0 && first_row < d_row_count);
1705     Q_ASSERT(last_row >= 0 && last_row < d_row_count);
1706     Q_ASSERT(values.count() > last_row - first_row);
1707 
1708     if (first_row == 0 && last_row == d_row_count - 1) {
1709         d_data[col] = values;
1710         d_data[col].resize(d_row_count); // values may be larger
1711         if (!d_block_change_signals)
1712             emit d_owner->dataChanged(first_row, col, last_row, col);
1713         return;
1714     }
1715 
1716     for (int i = first_row; i <= last_row; i++)
1717         d_data[col][i] = values.at(i - first_row);
1718     if (!d_block_change_signals)
1719         emit d_owner->dataChanged(first_row, col, last_row, col);
1720 }
1721 
rowCells(int row,int first_column,int last_column)1722 QVector<qreal> Matrix::Private::rowCells(int row, int first_column, int last_column)
1723 {
1724     Q_ASSERT(first_column >= 0 && first_column < d_column_count);
1725     Q_ASSERT(last_column >= 0 && last_column < d_column_count);
1726 
1727     QVector<qreal> result;
1728     for (int i = first_column; i <= last_column; i++)
1729         result.append(d_data.at(i).at(row));
1730     return result;
1731 }
1732 
setRowCells(int row,int first_column,int last_column,const QVector<qreal> & values)1733 void Matrix::Private::setRowCells(int row, int first_column, int last_column,
1734                                   const QVector<qreal> &values)
1735 {
1736     Q_ASSERT(first_column >= 0 && first_column < d_column_count);
1737     Q_ASSERT(last_column >= 0 && last_column < d_column_count);
1738     Q_ASSERT(values.count() > last_column - first_column);
1739 
1740     for (int i = first_column; i <= last_column; i++)
1741         d_data[i][row] = values.at(i - first_column);
1742     if (!d_block_change_signals)
1743         emit d_owner->dataChanged(row, first_column, row, last_column);
1744 }
1745 
clearColumn(int col)1746 void Matrix::Private::clearColumn(int col)
1747 {
1748     d_data[col].fill(0.0);
1749     if (!d_block_change_signals)
1750         emit d_owner->dataChanged(0, col, d_row_count - 1, col);
1751 }
1752 
xStart() const1753 double Matrix::Private::xStart() const
1754 {
1755     return d_x_start;
1756 }
1757 
yStart() const1758 double Matrix::Private::yStart() const
1759 {
1760     return d_y_start;
1761 }
1762 
xEnd() const1763 double Matrix::Private::xEnd() const
1764 {
1765     return d_x_end;
1766 }
1767 
yEnd() const1768 double Matrix::Private::yEnd() const
1769 {
1770     return d_y_end;
1771 }
1772 
setXStart(double x)1773 void Matrix::Private::setXStart(double x)
1774 {
1775     d_x_start = x;
1776     emit d_owner->coordinatesChanged();
1777 }
1778 
setXEnd(double x)1779 void Matrix::Private::setXEnd(double x)
1780 {
1781     d_x_end = x;
1782     emit d_owner->coordinatesChanged();
1783 }
1784 
setYStart(double y)1785 void Matrix::Private::setYStart(double y)
1786 {
1787     d_y_start = y;
1788     emit d_owner->coordinatesChanged();
1789 }
1790 
setYEnd(double y)1791 void Matrix::Private::setYEnd(double y)
1792 {
1793     d_y_end = y;
1794     emit d_owner->coordinatesChanged();
1795 }
1796 
formula() const1797 QString Matrix::Private::formula() const
1798 {
1799     return d_formula;
1800 }
1801 
setFormula(const QString & formula)1802 void Matrix::Private::setFormula(const QString &formula)
1803 {
1804     d_formula = formula;
1805     emit d_owner->formulaChanged();
1806 }
1807 
getCells(const int startRow,const int endRow,const int startCol,const int endCol) const1808 std::vector<std::vector<std::pair<double, bool>>> Matrix::Private::getCells(const int startRow,
1809                                                                             const int endRow,
1810                                                                             const int startCol,
1811                                                                             const int endCol) const
1812 {
1813     if (startRow > endRow || startCol > endCol || startCol < 0 || endCol > columnCount()
1814         || startRow < 0 || endRow > rowCount())
1815         return std::vector<std::vector<std::pair<double, bool>>>();
1816 
1817     std::pair<double, bool> invalidValue(std::numeric_limits<double>::quiet_NaN(), false);
1818     std::vector<std::vector<std::pair<double, bool>>> values(
1819             endRow - startRow + 1,
1820             std::vector<std::pair<double, bool>>(endCol - startCol + 1, invalidValue));
1821     for (int row = startRow; row <= endRow; row++)
1822         for (int col = startCol; col <= endCol; col++)
1823             values[row - startRow][col - startCol] = std::make_pair(cell(row, col), true);
1824     return values;
1825 }
1826 
setCells(const int startRow,const int startCol,const std::vector<std::vector<std::pair<double,bool>>> & values)1827 void Matrix::Private::setCells(const int startRow, const int startCol,
1828                                const std::vector<std::vector<std::pair<double, bool>>> &values)
1829 {
1830     blockChangeSignals(true);
1831     for (size_t ii = 0u; values.size() > ii; ++ii) {
1832         const auto &column = values[ii];
1833         for (size_t jj = 0u; column.size() > jj; ++jj) {
1834             const auto &pair = column[jj];
1835             if (!pair.second)
1836                 continue;
1837             setCell(startRow + ii, startCol + jj, pair.first);
1838         }
1839     }
1840     blockChangeSignals(false);
1841     auto endRow = startRow + values.size() - 1;
1842     auto endCol = startCol + std::max_element(
1843             values.cbegin(), values.cend(),
1844             [](const std::vector<std::pair<double, bool>> &a,
1845                const std::vector<std::pair<double, bool>> &b)
1846 			{ return a.size() > b.size();})->size() - 1;
1847     emit d_owner->dataChanged(startRow, startCol, endRow, endCol);
1848 }
1849 
1850 } // namespace
1851