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(¤t_row, ¤t_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