1 /***************************************************************************
2     File                 : Matrix.cpp
3     Project              : SciDAVis
4     Description          : Matrix worksheet class
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.h"
32 #include "future/matrix/MatrixView.h"
33 #include "ScriptEdit.h"
34 
35 #include <QtGlobal>
36 #include <QTextStream>
37 #include <QList>
38 #include <QEvent>
39 #include <QContextMenuEvent>
40 #include <QVBoxLayout>
41 #include <QMouseEvent>
42 #include <QHeaderView>
43 #include <QDateTime>
44 #include <QApplication>
45 #include <QMessageBox>
46 #include <QVarLengthArray>
47 #include <QClipboard>
48 #include <QShortcut>
49 #include <QPrinter>
50 #include <QPrintDialog>
51 #include <QPainter>
52 #include <QLocale>
53 #include <QXmlStreamWriter>
54 #include <QtDebug>
55 
56 #include <stdlib.h>
57 #include <math.h>
58 #include <stdio.h>
59 
60 #include <gsl/gsl_linalg.h>
61 #include <gsl/gsl_math.h>
62 
Matrix(ScriptingEnv * env,int r,int c,const QString & label,QWidget * parent,const char * name,Qt::WindowFlags f)63 Matrix::Matrix(ScriptingEnv *env, int r, int c, const QString &label, QWidget *parent,
64                const char *name, Qt::WindowFlags f)
65     : MatrixView(label, parent, name, f), scripted(env)
66 {
67     d_future_matrix = new future::Matrix(0, r, c, label);
68     init(r, c);
69 }
70 
Matrix(future::Matrix * future_matrix,ScriptingEnv * env,int r,int c,const QString & label,QWidget * parent,const char * name,Qt::WindowFlags f)71 Matrix::Matrix(future::Matrix *future_matrix, ScriptingEnv *env, int r, int c, const QString &label,
72                QWidget *parent, const char *name, Qt::WindowFlags f)
73     : MatrixView(label, parent, name, f), scripted(env)
74 {
75     d_future_matrix = future_matrix;
76     init(r, c);
77 }
78 
init(int,int)79 void Matrix::init(int, int)
80 {
81     MatrixView::setMatrix(d_future_matrix);
82     d_future_matrix->setView(this);
83     d_future_matrix->setNumericFormat('f');
84     d_future_matrix->setDisplayedDigits(6);
85     d_future_matrix->setCoordinates(1.0, 10.0, 1.0, 10.0);
86     dMatrix = 0;
87 
88     birthdate = QLocale().toString(d_future_matrix->creationTime());
89 
90     // this is not very nice but works for the moment
91     ui.gridLayout2->removeWidget(ui.formula_box);
92     delete ui.formula_box;
93     ui.formula_box = new ScriptEdit(scriptEnv, ui.formula_tab);
94     ui.formula_box->setObjectName(QString::fromUtf8("formula_box"));
95     ui.formula_box->setMinimumSize(QSize(60, 10));
96     ui.formula_box->setAcceptRichText(false);
97     ui.formula_box->setLineWrapMode(QTextEdit::WidgetWidth);
98     ui.gridLayout2->addWidget(ui.formula_box, 1, 0, 1, 3);
99 
100     ui.add_cell_combobox->addItem("cell(i, j)");
101     ui.add_function_combobox->addItems(scriptEnv->mathFunctions());
102     updateFunctionDoc();
103 
104     connect(ui.add_function_combobox, SIGNAL(currentIndexChanged(int)), this,
105             SLOT(updateFunctionDoc()));
106     connect(ui.button_set_formula, SIGNAL(pressed()), this, SLOT(applyFormula()));
107     connect(ui.add_function_button, SIGNAL(pressed()), this, SLOT(addFunction()));
108     connect(ui.add_cell_button, SIGNAL(pressed()), this, SLOT(addCell()));
109 
110     connect(d_future_matrix, SIGNAL(columnsInserted(int, int)), this, SLOT(handleChange()));
111     connect(d_future_matrix, SIGNAL(columnsRemoved(int, int)), this, SLOT(handleChange()));
112     connect(d_future_matrix, SIGNAL(rowsInserted(int, int)), this, SLOT(handleChange()));
113     connect(d_future_matrix, SIGNAL(rowsRemoved(int, int)), this, SLOT(handleChange()));
114     connect(d_future_matrix, SIGNAL(dataChanged(int, int, int, int)), this, SLOT(handleChange()));
115     connect(d_future_matrix, SIGNAL(coordinatesChanged()), this, SLOT(handleChange()));
116     connect(d_future_matrix, SIGNAL(formulaChanged()), this, SLOT(handleChange()));
117     connect(d_future_matrix, SIGNAL(formatChanged()), this, SLOT(handleChange()));
118     connect(d_future_matrix, SIGNAL(recalculate()), this, SLOT(recalculate()));
119 
120     connect(d_future_matrix, SIGNAL(aspectDescriptionChanged(const AbstractAspect *)), this,
121             SLOT(handleAspectDescriptionChange(const AbstractAspect *)));
122 }
123 
~Matrix()124 Matrix::~Matrix() { }
125 
handleChange()126 void Matrix::handleChange()
127 {
128     emit modifiedWindow(this);
129 }
130 
cell(int row,int col)131 double Matrix::cell(int row, int col)
132 {
133     if (dMatrix)
134         return dMatrix[row][col];
135     else
136         return d_future_matrix->cell(row, col);
137 }
138 
setCell(int row,int col,double value)139 void Matrix::setCell(int row, int col, double value)
140 {
141     d_future_matrix->setCell(row, col, value);
142 }
143 
setCells(const QVector<qreal> & data)144 void Matrix::setCells(const QVector<qreal> &data)
145 {
146     d_future_matrix->setCells(data);
147 }
148 
text(int row,int col)149 QString Matrix::text(int row, int col)
150 {
151     return d_future_matrix->text(row, col);
152 }
153 
setText(int row,int col,const QString & new_text)154 void Matrix::setText(int row, int col, const QString &new_text)
155 {
156     bool ok = true;
157     QLocale locale;
158     double res = locale.toDouble(new_text, &ok);
159     if (ok)
160         d_future_matrix->setCell(row, col, res);
161     else {
162         Script *script = scriptEnv->newScript(new_text, this,
163                                               QString("<%1_%2_%3>").arg(name()).arg(row).arg(col));
164         connect(script, SIGNAL(error(const QString &, const QString &, int)), scriptEnv,
165                 SIGNAL(error(const QString &, const QString &, int)));
166 
167         script->setInt(row + 1, "row");
168         script->setInt(row + 1, "i");
169         script->setInt(col + 1, "col");
170         script->setInt(col + 1, "j");
171 
172         QVariant ret = script->eval();
173         setCell(row, col, ret.toDouble());
174     }
175 }
176 
setCoordinates(double xs,double xe,double ys,double ye)177 void Matrix::setCoordinates(double xs, double xe, double ys, double ye)
178 {
179     d_future_matrix->setCoordinates(xs, xe, ys, ye);
180 }
181 
saveToString(const QString & geometry)182 QString Matrix::saveToString(const QString &geometry)
183 {
184     QString s = "<matrix>\n";
185     QString xml;
186     QXmlStreamWriter writer(&xml);
187     d_future_matrix->save(&writer);
188     s += QString::number(xml.length()) + "\n"; // this is need in case there are newlines in the XML
189     s += xml + "\n";
190     s += geometry + "\n";
191     s += "</matrix>\n";
192     return s;
193 
194 #if 0
195 	QString s = "<matrix>\n";
196 	s += QString(name()) + "\t";
197 	s += QString::number(numRows())+"\t";
198 	s += QString::number(numCols())+"\t";
199 	s += birthDate() + "\n";
200 	s += info;
201 	s += "ColWidth\t" + QString::number(columnWidth(0))+"\n";
202 	s += "<formula>\n" + formula() + "\n</formula>\n";
203 	s += "TextFormat\t" + QString(d_future_matrix->numericFormat()) + "\t" + QString::number(d_future_matrix->displayedDigits()) + "\n";
204 	s += "WindowLabel\t" + windowLabel() + "\t" + QString::number(captionPolicy()) + "\n";
205 	s += "Coordinates\t" + QString::number(xStart(),'g',15) + "\t" +QString::number(xEnd(),'g',15) + "\t";
206 	s += QString::number(yStart(),'g',15) + "\t" + QString::number(yEnd(),'g',15) + "\n";
207 	s += saveText();
208 	s +="</matrix>\n";
209 	return s;
210 #endif
211 }
212 
saveAsTemplate(const QString & info)213 QString Matrix::saveAsTemplate(const QString &info)
214 {
215     QString s = "<matrix>\t";
216     s += QString::number(numRows()) + "\t";
217     s += QString::number(numCols()) + "\n";
218     s += info;
219     s += "ColWidth\t" + QString::number(columnWidth(0)) + "\n";
220     s += "<formula>\n" + formula() + "\n</formula>\n";
221     s += "TextFormat\t" + QString(d_future_matrix->numericFormat()) + "\t"
222             + QString::number(d_future_matrix->displayedDigits()) + "\n";
223     s += "Coordinates\t" + QString::number(xStart(), 'g', 15) + "\t"
224             + QString::number(xEnd(), 'g', 15) + "\t";
225     s += QString::number(yStart(), 'g', 15) + "\t" + QString::number(yEnd(), 'g', 15) + "\n";
226     s += "</matrix>\n";
227     return s;
228 }
229 
saveText()230 QString Matrix::saveText()
231 {
232     QString out_text = "<data>\n";
233     int cols = d_future_matrix->columnCount();
234     for (int i = 0; i < d_future_matrix->rowCount(); i++) {
235         out_text += QString::number(i) + "\t";
236         for (int j = 0; j < cols - 1; j++)
237             out_text += QString::number(cell(i, j), 'e', 16) + "\t";
238 
239         out_text += QString::number(cell(i, cols - 1), 'e', 16) + "\n";
240     }
241     return out_text + "</data>\n";
242 }
243 
setFormula(const QString & s)244 void Matrix::setFormula(const QString &s)
245 {
246     d_future_matrix->setFormula(s);
247 }
248 
formula()249 QString Matrix::formula()
250 {
251     return d_future_matrix->formula();
252 }
253 
setNumericFormat(const QChar & f,int prec)254 void Matrix::setNumericFormat(const QChar &f, int prec)
255 {
256     if (d_future_matrix->numericFormat() == f && d_future_matrix->displayedDigits() == prec)
257         return;
258 
259     d_future_matrix->setNumericFormat(f.toLatin1());
260     d_future_matrix->setDisplayedDigits(prec);
261 
262     emit modifiedWindow(this);
263 }
264 
setTextFormat(const QChar & format,int precision)265 void Matrix::setTextFormat(const QChar &format, int precision)
266 {
267     d_future_matrix->setNumericFormat(format.toLatin1());
268     d_future_matrix->setDisplayedDigits(precision);
269 }
270 
setColumnsWidth(int width)271 void Matrix::setColumnsWidth(int width)
272 {
273     for (int i = 0; i < d_future_matrix->columnCount(); i++)
274         setColumnWidth(i, width);
275 }
276 
setDimensions(int rows,int cols)277 void Matrix::setDimensions(int rows, int cols)
278 {
279     d_future_matrix->setDimensions(rows, cols);
280 }
281 
numRows()282 int Matrix::numRows()
283 {
284     return d_future_matrix->rowCount();
285 }
286 
setNumRows(int rows)287 void Matrix::setNumRows(int rows)
288 {
289     d_future_matrix->setDimensions(rows, d_future_matrix->columnCount());
290 }
291 
numCols()292 int Matrix::numCols()
293 {
294     return d_future_matrix->columnCount();
295 }
296 
setNumCols(int cols)297 void Matrix::setNumCols(int cols)
298 {
299     d_future_matrix->setDimensions(d_future_matrix->rowCount(), cols);
300 }
301 
determinant()302 double Matrix::determinant()
303 {
304     int rows = numRows();
305     int cols = numCols();
306 
307     if (rows != cols) {
308         QMessageBox::critical(0, tr("Error"), tr("Calculation failed, the matrix is not square!"));
309         return GSL_POSINF;
310     }
311 
312     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
313 
314     gsl_matrix *A = gsl_matrix_alloc(rows, cols);
315     int i;
316     for (i = 0; i < rows; i++)
317         for (int j = 0; j < cols; j++)
318             gsl_matrix_set(A, i, j, cell(i, j));
319 
320     gsl_permutation *p = gsl_permutation_alloc(rows);
321     gsl_linalg_LU_decomp(A, p, &i);
322 
323     double det = gsl_linalg_LU_det(A, i);
324 
325     gsl_matrix_free(A);
326     gsl_permutation_free(p);
327 
328     QApplication::restoreOverrideCursor();
329     return det;
330 }
331 
invert()332 void Matrix::invert()
333 {
334     int rows = numRows();
335     int cols = numCols();
336 
337     if (rows != cols) {
338         QMessageBox::critical(0, tr("Error"), tr("Inversion failed, the matrix is not square!"));
339         return;
340     }
341 
342     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
343 
344     gsl_matrix *A = gsl_matrix_alloc(rows, cols);
345     int i;
346     for (i = 0; i < rows; i++) {
347         for (int j = 0; j < cols; j++)
348             gsl_matrix_set(A, i, j, cell(i, j));
349     }
350 
351     gsl_permutation *p = gsl_permutation_alloc(cols);
352     gsl_linalg_LU_decomp(A, p, &i);
353 
354     gsl_matrix *inverse = gsl_matrix_alloc(rows, cols);
355     gsl_linalg_LU_invert(A, p, inverse);
356 
357     gsl_matrix_free(A);
358     gsl_permutation_free(p);
359 
360     this->blockSignals(true);
361     for (i = 0; i < rows; i++) {
362         for (int j = 0; j < cols; j++)
363             setCell(i, j, gsl_matrix_get(inverse, i, j));
364     }
365     this->blockSignals(false);
366 
367     gsl_matrix_free(inverse);
368     QApplication::restoreOverrideCursor();
369     emit modifiedWindow(this);
370 }
371 
transpose()372 void Matrix::transpose()
373 {
374     d_future_matrix->transpose();
375 }
376 
saveCellsToMemory()377 void Matrix::saveCellsToMemory()
378 {
379     int rows = numRows();
380     int cols = numCols();
381     dMatrix = allocateMatrixData(rows, cols);
382     for (int i = 0; i < rows; i++)
383         for (int j = 0; j < cols; j++)
384             dMatrix[i][j] = d_future_matrix->cell(i, j);
385 }
386 
forgetSavedCells()387 void Matrix::forgetSavedCells()
388 {
389     freeMatrixData(dMatrix, numRows());
390     dMatrix = 0;
391 }
392 
recalculate()393 bool Matrix::recalculate()
394 {
395     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
396     Script *script = scriptEnv->newScript(formula(), this, QString("<%1>").arg(name()));
397     connect(script, SIGNAL(error(const QString &, const QString &, int)), scriptEnv,
398             SIGNAL(error(const QString &, const QString &, int)));
399     connect(script, SIGNAL(print(const QString &)), scriptEnv, SIGNAL(print(const QString &)));
400     if (!script->compile()) {
401         QApplication::restoreOverrideCursor();
402         return false;
403     }
404 
405     this->blockSignals(true);
406 
407     int startRow = firstSelectedRow(false);
408     int endRow = lastSelectedRow(false);
409     int startCol = firstSelectedColumn(false);
410     int endCol = lastSelectedColumn(false);
411 
412     QVariant ret;
413     saveCellsToMemory();
414     double dx = fabs(xEnd() - xStart()) / (double)(numRows() - 1);
415     double dy = fabs(yEnd() - yStart()) / (double)(numCols() - 1);
416     std::pair<double, bool> unsetValue(std::numeric_limits<double>::quiet_NaN(), false);
417     std::vector<std::vector<std::pair<double, bool>>> calculatedValues(
418             endRow - startRow + 1,
419             std::vector<std::pair<double, bool>>(endCol - startCol + 1, unsetValue));
420     for (int row = startRow; row <= endRow; row++)
421         for (int col = startCol; col <= endCol; col++) {
422             if (!isCellSelected(row, col))
423                 continue;
424             script->setInt(row + 1, "i");
425             script->setInt(row + 1, "row");
426             script->setDouble(yStart() + row * dy, "y");
427             script->setInt(col + 1, "j");
428             script->setInt(col + 1, "col");
429             script->setDouble(xStart() + col * dx, "x");
430             ret = script->eval();
431             if (!ret.isValid()) {
432                 forgetSavedCells();
433                 blockSignals(false);
434                 emit modifiedWindow(this);
435                 QApplication::restoreOverrideCursor();
436                 return false;
437             }
438             calculatedValues[row - startRow][col - startCol] = std::make_pair(ret.toDouble(), true);
439         }
440     d_future_matrix->setCells(startRow, startCol, calculatedValues);
441     forgetSavedCells();
442 
443     blockSignals(false);
444     emit modifiedWindow(this);
445     QApplication::restoreOverrideCursor();
446     return true;
447 }
448 
clearSelection()449 void Matrix::clearSelection()
450 {
451     d_future_matrix->clearSelectedCells();
452 }
453 
copySelection()454 void Matrix::copySelection()
455 {
456     d_future_matrix->copySelection();
457 }
458 
cutSelection()459 void Matrix::cutSelection()
460 {
461     copySelection();
462     clearSelection();
463 }
464 
rowsSelected()465 bool Matrix::rowsSelected()
466 {
467     for (int i = 0; i < numRows(); i++) {
468         if (this->isRowSelected(i, true))
469             return true;
470     }
471     return false;
472 }
473 
deleteSelectedRows()474 void Matrix::deleteSelectedRows()
475 {
476     d_future_matrix->removeSelectedRows();
477 }
478 
insertColumn()479 void Matrix::insertColumn()
480 {
481     d_future_matrix->insertEmptyColumns();
482 }
483 
columnsSelected()484 bool Matrix::columnsSelected()
485 {
486     for (int i = 0; i < numCols(); i++) {
487         if (this->isColumnSelected(i, true))
488             return true;
489     }
490     return false;
491 }
492 
deleteSelectedColumns()493 void Matrix::deleteSelectedColumns()
494 {
495     d_future_matrix->removeSelectedColumns();
496 }
497 
numSelectedRows()498 int Matrix::numSelectedRows()
499 {
500     int r = 0;
501     for (int i = 0; i < numRows(); i++)
502         if (this->isRowSelected(i, true))
503             r++;
504     return r;
505 }
506 
numSelectedColumns()507 int Matrix::numSelectedColumns()
508 {
509     int c = 0;
510     for (int i = 0; i < numCols(); i++)
511         if (this->isColumnSelected(i, true))
512             c++;
513     return c;
514 }
515 
insertRow()516 void Matrix::insertRow()
517 {
518     d_future_matrix->insertEmptyRows();
519 }
520 
pasteSelection()521 void Matrix::pasteSelection()
522 {
523     d_future_matrix->pasteIntoSelection();
524 }
525 
customEvent(QEvent * e)526 void Matrix::customEvent(QEvent *e)
527 {
528     if (e->type() == SCRIPTING_CHANGE_EVENT)
529         scriptingChangeEvent((ScriptingChangeEvent *)e);
530 }
531 
closeEvent(QCloseEvent * e)532 void Matrix::closeEvent(QCloseEvent *e)
533 {
534     if (askOnClose) {
535         switch (QMessageBox::information(this, tr("SciDAVis"),
536                                          tr("Do you want to hide or delete") + "<p><b>'"
537                                                  + objectName() + "'</b> ?",
538                                          tr("Delete"), tr("Hide"), tr("Cancel"), 0, 2)) {
539         case 0:
540             e->accept();
541             d_future_matrix->remove();
542             return;
543 
544         case 1:
545             e->ignore();
546             emit hiddenWindow(this);
547             break;
548 
549         case 2:
550             e->ignore();
551             break;
552         }
553     } else {
554         e->accept();
555         d_future_matrix->remove();
556         return;
557     }
558 }
559 
exportPDF(const QString & fileName)560 void Matrix::exportPDF(const QString &fileName)
561 {
562     print(fileName);
563 }
564 
print()565 void Matrix::print()
566 {
567     print(QString());
568 }
569 
print(const QString & fileName)570 void Matrix::print(const QString &fileName)
571 {
572     QPrinter printer;
573     printer.setColorMode(QPrinter::GrayScale);
574 
575     if (!fileName.isEmpty()) {
576         printer.setCreator("SciDAVis");
577         printer.setOutputFormat(QPrinter::PdfFormat);
578         printer.setOutputFileName(fileName);
579     } else {
580         QPrintDialog printDialog(&printer);
581         if (printDialog.exec() != QDialog::Accepted)
582             return;
583     }
584 
585     printer.setFullPage(true);
586     QPainter p;
587     if (!p.begin(&printer))
588         return; // paint on printer
589     int dpiy = printer.logicalDpiY();
590     const int margin = (int)((1 / 2.54) * dpiy); // 1 cm margins
591 
592     QHeaderView *vHeader = d_view_widget->verticalHeader();
593 
594     int rows = numRows();
595     int cols = numCols();
596     int height = margin;
597     int i, vertHeaderWidth = vHeader->width();
598     int right = margin + vertHeaderWidth;
599 
600     // print header
601     p.setFont(QFont());
602     QString header_label = d_view_widget->model()->headerData(0, Qt::Horizontal).toString();
603     QRect br = p.boundingRect(br, Qt::AlignCenter, header_label);
604     p.drawLine(right, height, right, height + br.height());
605     QRect tr(br);
606 
607     for (i = 0; i < cols; i++) {
608         int w = columnWidth(i);
609         tr.setTopLeft(QPoint(right, height));
610         tr.setWidth(w);
611         tr.setHeight(br.height());
612         header_label = d_view_widget->model()->headerData(i, Qt::Horizontal).toString();
613         p.drawText(tr, Qt::AlignCenter, header_label);
614         right += w;
615         p.drawLine(right, height, right, height + tr.height());
616 
617         if (right >= printer.width() - 2 * margin)
618             break;
619     }
620 
621     p.drawLine(margin + vertHeaderWidth, height, right - 1, height); // first horizontal line
622     height += tr.height();
623     p.drawLine(margin, height, right - 1, height);
624 
625     // print table values
626     for (i = 0; i < rows; i++) {
627         right = margin;
628         QString cell_text = d_view_widget->model()->headerData(i, Qt::Vertical).toString() + "\t";
629         tr = p.boundingRect(tr, Qt::AlignCenter, cell_text);
630         p.drawLine(right, height, right, height + tr.height());
631 
632         br.setTopLeft(QPoint(right, height));
633         br.setWidth(vertHeaderWidth);
634         br.setHeight(tr.height());
635         p.drawText(br, Qt::AlignCenter, cell_text);
636         right += vertHeaderWidth;
637         p.drawLine(right, height, right, height + tr.height());
638 
639         for (int j = 0; j < cols; j++) {
640             int w = columnWidth(j);
641             cell_text = text(i, j) + "\t";
642             tr = p.boundingRect(tr, Qt::AlignCenter, cell_text);
643             br.setTopLeft(QPoint(right, height));
644             br.setWidth(w);
645             br.setHeight(tr.height());
646             p.drawText(br, Qt::AlignCenter, cell_text);
647             right += w;
648             p.drawLine(right, height, right, height + tr.height());
649 
650             if (right >= printer.width() - 2 * margin)
651                 break;
652         }
653         height += br.height();
654         p.drawLine(margin, height, right - 1, height);
655 
656         if (height >= printer.height() - margin) {
657             printer.newPage();
658             height = margin;
659             p.drawLine(margin, height, right, height);
660         }
661     }
662 }
663 
range(double * min,double * max)664 void Matrix::range(double *min, double *max)
665 {
666     double d_min = cell(0, 0);
667     double d_max = d_min;
668 
669     for (int i = 0; i < numRows(); i++) {
670         for (int j = 0; j < numCols(); j++) {
671             double aux = cell(i, j);
672             if (aux <= d_min)
673                 d_min = aux;
674 
675             if (aux >= d_max)
676                 d_max = aux;
677         }
678     }
679 
680     *min = d_min;
681     *max = d_max;
682 }
683 
allocateMatrixData(int rows,int columns)684 double **Matrix::allocateMatrixData(int rows, int columns)
685 {
686     double **data = new double *[rows];
687     for (int i = 0; i < rows; ++i)
688         data[i] = new double[columns];
689 
690     return data;
691 }
692 
freeMatrixData(double ** data,int rows)693 void Matrix::freeMatrixData(double **data, int rows)
694 {
695     for (int i = 0; i < rows; i++)
696         delete[] data[i];
697 
698     delete[] data;
699 }
700 
updateDecimalSeparators()701 void Matrix::updateDecimalSeparators()
702 {
703     this->update();
704 }
705 
copy(Matrix * m)706 void Matrix::copy(Matrix *m)
707 {
708     if (!m)
709         return;
710 
711     d_future_matrix->copy(m->d_future_matrix);
712 }
713 
fromImage(const QImage & image,ScriptingEnv * env)714 Matrix *Matrix::fromImage(const QImage &image, ScriptingEnv *env)
715 {
716     future::Matrix *fm = future::Matrix::fromImage(image);
717     if (!fm)
718         return NULL;
719     return new Matrix(fm, env, image.height(), image.width(), tr("Matrix %1").arg(1));
720 }
721 
applyFormula()722 void Matrix::applyFormula()
723 {
724     QApplication::setOverrideCursor(Qt::WaitCursor);
725     d_future_matrix->beginMacro(tr("%1: apply formula to selection").arg(name()));
726 
727     setFormula(ui.formula_box->toPlainText());
728     recalculate();
729 
730     d_future_matrix->endMacro();
731     QApplication::restoreOverrideCursor();
732 }
733 
addFunction()734 void Matrix::addFunction()
735 {
736     static_cast<ScriptEdit *>(ui.formula_box)
737             ->insertFunction(ui.add_function_combobox->currentText());
738 }
739 
addCell()740 void Matrix::addCell()
741 {
742     ui.formula_box->insertPlainText(ui.add_cell_combobox->currentText());
743 }
744 
updateFunctionDoc()745 void Matrix::updateFunctionDoc()
746 {
747     ui.add_function_combobox->setToolTip(
748             scriptEnv->mathFunctionDoc(ui.add_function_combobox->currentText()));
749 }
750 
handleAspectDescriptionChange(const AbstractAspect * aspect)751 void Matrix::handleAspectDescriptionChange(const AbstractAspect *aspect)
752 {
753     if (aspect != d_future_matrix)
754         return;
755     setObjectName(d_future_matrix->name());
756     updateCaption();
757 }
758