~ubuntu-branches/ubuntu/quantal/cantor/quantal

« back to all changes in this revision

Viewing changes to src/backends/qalculate/qalculateexpression.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2011-12-15 14:17:54 UTC
  • mto: This revision was merged to the branch mainline in revision 7.
  • Revision ID: package-import@ubuntu.com-20111215141754-mpepwpmr42120lpf
Tags: upstream-4.7.90
ImportĀ upstreamĀ versionĀ 4.7.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*************************************************************************************
 
2
*  Copyright (C) 2009 by Milian Wolff <mail@milianw.de>                             *
 
3
*  Copyright (C) 2011 by Matteo Agostinelli <agostinelli@gmail.com>                 *
 
4
*                                                                                   *
 
5
*  This program is free software; you can redistribute it and/or                    *
 
6
*  modify it under the terms of the GNU General Public License                      *
 
7
*  as published by the Free Software Foundation; either version 2                   *
 
8
*  of the License, or (at your option) any later version.                           *
 
9
*                                                                                   *
 
10
*  This program is distributed in the hope that it will be useful,                  *
 
11
*  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
 
12
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
 
13
*  GNU General Public License for more details.                                     *
 
14
*                                                                                   *
 
15
*  You should have received a copy of the GNU General Public License                *
 
16
*  along with this program; if not, write to the Free Software                      *
 
17
*  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
 
18
*************************************************************************************/
 
19
 
 
20
#include <config-cantorlib.h>
 
21
 
 
22
#include "textresult.h"
 
23
#include "helpresult.h"
 
24
#include "imageresult.h"
 
25
#include "epsresult.h"
 
26
#include "settings.h"
 
27
#include "defaultvariablemodel.h"
 
28
 
 
29
#include "qalculateexpression.h"
 
30
#include "qalculatesession.h"
 
31
#include "qalculatesyntaxhelpobject.h"
 
32
 
 
33
#include <libqalculate/Calculator.h>
 
34
#include <libqalculate/ExpressionItem.h>
 
35
#include <libqalculate/Unit.h>
 
36
#include <libqalculate/Prefix.h>
 
37
#include <libqalculate/Variable.h>
 
38
#include <libqalculate/Function.h>
 
39
 
 
40
#include <string>
 
41
// required for the plotting interface of Qalculator
 
42
#include <vector>
 
43
 
 
44
#include <KGlobal>
 
45
#include <KMessageBox>
 
46
#include <KColorScheme>
 
47
#include <KLocale>
 
48
#include <KDebug>
 
49
 
 
50
#include <QApplication>
 
51
#include <QStack>
 
52
 
 
53
QalculateExpression::QalculateExpression( QalculateSession* session )
 
54
    : Cantor::Expression(session)
 
55
{
 
56
    m_tempFile = 0;
 
57
}
 
58
 
 
59
QalculateExpression::~QalculateExpression()
 
60
{
 
61
    if (m_tempFile)
 
62
        delete m_tempFile;
 
63
}
 
64
 
 
65
void QalculateExpression::evaluate()
 
66
{
 
67
    setStatus(Cantor::Expression::Computing);
 
68
    m_message = "";
 
69
 
 
70
    if (command().isEmpty()) {
 
71
        return;
 
72
    }
 
73
 
 
74
    if (command().contains("help")) {
 
75
        QalculateSyntaxHelpObject* helper = new QalculateSyntaxHelpObject(command(), (QalculateSession*) session());
 
76
        setResult(new Cantor::HelpResult(helper->answer()));
 
77
        setStatus(Cantor::Expression::Done);
 
78
        return;
 
79
    }
 
80
    else if (command().trimmed().startsWith("plot") &&
 
81
             (command().indexOf("plot")+4 == command().size() ||
 
82
              command()[command().indexOf("plot")+4].isSpace())) {
 
83
        evaluatePlotCommand();
 
84
        return;
 
85
    }
 
86
    else if (command().trimmed().startsWith("saveVariables") &&
 
87
             (command().indexOf("saveVariables")+13 == command().size() ||
 
88
              command()[command().indexOf("saveVariables")+13].isSpace())) {
 
89
        evaluateSaveVariablesCommand();
 
90
        return;
 
91
    }
 
92
    else if (command().trimmed().startsWith("loadVariables") &&
 
93
             (command().indexOf("loadVariables")+13 == command().size() ||
 
94
              command()[command().indexOf("loadVariables")+13].isSpace())) {
 
95
        evaluateLoadVariablesCommand();
 
96
        return;
 
97
    }
 
98
 
 
99
    string expression = unlocalizeExpression(command());
 
100
 
 
101
    kDebug() << "EXPR: " << QString(expression.c_str());
 
102
 
 
103
    EvaluationOptions eo = evaluationOptions();
 
104
 
 
105
    MathStructure result = CALCULATOR->calculate(expression, eo);
 
106
 
 
107
    // update the answer variables
 
108
    static_cast<QalculateSession*>(session())->setLastResult(result);
 
109
 
 
110
    // error handling
 
111
    if (checkForCalculatorMessages() & (MSG_WARN | MSG_WARN))
 
112
        return;
 
113
 
 
114
    updateVariables(CALCULATOR->parse(expression, eo.parse_options));
 
115
 
 
116
    QSharedPointer<PrintOptions> po = printOptions();
 
117
 
 
118
    result.format(*po);
 
119
 
 
120
    setResult(new Cantor::TextResult(result.print(*po).c_str()));
 
121
    setStatus(Done);
 
122
}
 
123
 
 
124
void QalculateExpression::evaluateSaveVariablesCommand()
 
125
{
 
126
    QString argString = command().mid(command().indexOf("saveVariables")+13);
 
127
    argString = argString.trimmed();
 
128
 
 
129
    QString usage = i18n("Usage: saveVariables file");
 
130
 
 
131
    QString fileName = parseForFilename(argString, usage);
 
132
    if (fileName.isNull())
 
133
        return;
 
134
    
 
135
    // We want to save Temporary variables, but Qalculate does not.
 
136
    std::vector<Variable*> variables = CALCULATOR->variables;
 
137
    // If somebody saves his variables in Cantor_Internal_Temporary
 
138
    // he deserves unexpected behavior.
 
139
    std::string tmpCategory = "Temporary";
 
140
    std::string newCategory = "Cantor_Internal_Temporary";
 
141
    for (int i = 0; i < variables.size(); ++i) {
 
142
        if (variables[i]->category() == tmpCategory)
 
143
            variables[i]->setCategory(newCategory);
 
144
    }
 
145
 
 
146
    int res = CALCULATOR->saveVariables(fileName.toLatin1().data());
 
147
 
 
148
    for (int i = 0; i < variables.size(); ++i) {
 
149
        if (variables[i]->category() == newCategory)
 
150
            variables[i]->setCategory(tmpCategory);
 
151
    }
 
152
 
 
153
 
 
154
    if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
 
155
        return;
 
156
    }
 
157
    if (res < 0) {
 
158
        showMessage(i18n("Saving failed."), MESSAGE_ERROR);
 
159
        return;
 
160
    }
 
161
 
 
162
    setStatus(Done);
 
163
}
 
164
 
 
165
void QalculateExpression::evaluateLoadVariablesCommand()
 
166
{
 
167
    QString argString = command().mid(command().indexOf("loadVariables")+13);
 
168
    argString = argString.trimmed();
 
169
 
 
170
    QString usage = i18n("Usage: loadVariables file");
 
171
 
 
172
    QString fileName = parseForFilename(argString, usage);
 
173
    if (fileName.isNull())
 
174
        return;
 
175
    
 
176
    int res = CALCULATOR->loadDefinitions(fileName.toLatin1().data());
 
177
    if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
 
178
        return;
 
179
    }
 
180
    if (res < 0) {
 
181
        showMessage(i18n("Loading failed."), MESSAGE_ERROR);
 
182
        return;
 
183
    }
 
184
 
 
185
    // We have to store temporary variables in a different category
 
186
    // (see parseSaveVariablesCommand())
 
187
    std::vector<Variable*> variables = CALCULATOR->variables;
 
188
    std::string tmpCategory = "Temporary";
 
189
    std::string newCategory = "Cantor_Internal_Temporary";
 
190
 
 
191
    for (int i = 0; i < variables.size(); ++i) {
 
192
        if (variables[i]->category() == newCategory)
 
193
            variables[i]->setCategory(tmpCategory);
 
194
    }
 
195
 
 
196
    setStatus(Done);
 
197
}
 
198
 
 
199
QString QalculateExpression::parseForFilename(QString argument, QString usage)
 
200
{
 
201
    if (argument.isEmpty()) {
 
202
        showMessage(usage, MESSAGE_ERROR);
 
203
        return QString();
 
204
    }
 
205
 
 
206
    QString fileName = "";
 
207
    QChar sep = '\0';
 
208
    int i = 0;
 
209
    if (argument[0] == '\'' || argument[0] == '"') {
 
210
        sep = argument[0];
 
211
        i = 1;
 
212
    }
 
213
    while (i < argument.size() && !argument[i].isSpace() && 
 
214
           argument[i] != sep) {
 
215
        if (argument[i] == '\\' && i < argument.size()-1)
 
216
            ++i;
 
217
        fileName += argument[i];
 
218
        ++i;
 
219
    }
 
220
 
 
221
    if (sep != '\0' && i == argument.size()) {
 
222
        showMessage(i18n("missing %1", sep), MESSAGE_ERROR);
 
223
        return QString();
 
224
    }
 
225
    
 
226
    if (i < argument.size() - 1) {
 
227
        showMessage(usage, MESSAGE_ERROR);
 
228
        return QString();
 
229
    }
 
230
 
 
231
    return fileName;
 
232
}
 
233
 
 
234
void QalculateExpression::evaluatePlotCommand()
 
235
{
 
236
    QString argString = command().mid(command().indexOf("plot")+4);
 
237
    argString = unlocalizeExpression(argString).c_str();
 
238
    argString = argString.trimmed();
 
239
 
 
240
    QList<QStringList> argumentsList;
 
241
    QStringList arguments;
 
242
 
 
243
    // For error handling
 
244
    KColorScheme scheme(QApplication::palette().currentColorGroup());
 
245
    const QString errorColor = scheme.foreground(KColorScheme::NegativeText).color().name();
 
246
    const QString warningColor = scheme.foreground(KColorScheme::NeutralText).color().name();
 
247
    const QString msgFormat("<font color=\"%1\">%2: %3</font><br>\n");
 
248
 
 
249
    if (!CALCULATOR->canPlot()) {
 
250
        showMessage(i18n("Qalculate reports it cannot print. Is gnuplot installed?"), MESSAGE_ERROR);
 
251
        return;
 
252
    }
 
253
 
 
254
    // Split argString into the arguments
 
255
    int i=0;
 
256
    int j=0;
 
257
    QString arg = "";
 
258
    while (i < argString.size()) {
 
259
        if (argString[i] == '"' || argString[i] == '\'') {
 
260
            ++j;
 
261
            while(j < argString.size() && argString[j] != argString[i]) {
 
262
                if (argString[j] == '\\') {
 
263
                    ++j;
 
264
                    if (j == argString.size())
 
265
                        continue; // just ignore trailing backslashes
 
266
                }
 
267
                arg += argString[j];
 
268
                ++j;
 
269
            }
 
270
            if (j == argString.size()) {
 
271
                showMessage(i18n("missing %1", argString[i]), MESSAGE_ERROR);
 
272
                return;
 
273
            }
 
274
            ++j;
 
275
        } else if (argString[i] == ',') {
 
276
            argumentsList.append(arguments);
 
277
            arguments.clear();
 
278
            ++j;
 
279
        } else {
 
280
            while(j < argString.size() && !argString[j].isSpace() && 
 
281
                  argString[j] != '=' && argString[j] != ',') {
 
282
                if (argString[j] == '\\') {
 
283
                    ++j;
 
284
                    if (j == argString.size())
 
285
                        continue; // just ignore trailing backslashes
 
286
                }
 
287
                arg += argString[j];
 
288
                ++j;
 
289
            }
 
290
        }
 
291
        if (argString[j] == '=') {
 
292
            // Parse things like title="..." as one argument
 
293
            arg += '=';
 
294
            i = ++j;
 
295
            continue;
 
296
        }
 
297
        if (!arg.isEmpty()) {
 
298
            arguments << arg;
 
299
            arg = "";
 
300
        }
 
301
        while (j < argString.size() && argString[j].isSpace())
 
302
            ++j;
 
303
        i = j;
 
304
    }
 
305
    argumentsList.append(arguments);
 
306
 
 
307
    // Parse the arguments and compute the points to be plotted 
 
308
    std::vector<MathStructure> y_vectors;
 
309
    std::vector<MathStructure> x_vectors;
 
310
    std::vector<PlotDataParameters*> plotDataParameterList;
 
311
    PlotParameters plotParameters;
 
312
    EvaluationOptions eo = evaluationOptions();
 
313
    /// temporary
 
314
    plotParameters.title = "";
 
315
    plotParameters.y_label = "";
 
316
    plotParameters.x_label = "";
 
317
    plotParameters.filename = "";
 
318
    plotParameters.filetype = PLOT_FILETYPE_AUTO;
 
319
    plotParameters.color = QalculateSettings::coloredPlot();
 
320
    plotParameters.auto_y_min = true;
 
321
    plotParameters.auto_x_min = true;
 
322
    plotParameters.auto_x_max = true;
 
323
    plotParameters.auto_y_max = true;
 
324
    plotParameters.y_log = false;
 
325
    plotParameters.x_log = false;
 
326
    plotParameters.grid = QalculateSettings::plotGrid();
 
327
    plotParameters.linewidth = QalculateSettings::plotLineWidth();
 
328
    plotParameters.show_all_borders = QalculateSettings::plotBorder();
 
329
    switch (QalculateSettings::plotLegend()) {
 
330
    case QalculateSettings::LEGEND_NONE:
 
331
        plotParameters.legend_placement = PLOT_LEGEND_NONE;
 
332
        break;
 
333
    case QalculateSettings::LEGEND_TOP_LEFT:
 
334
        plotParameters.legend_placement = PLOT_LEGEND_TOP_LEFT;
 
335
        break;
 
336
    case QalculateSettings::LEGEND_TOP_RIGHT:
 
337
        plotParameters.legend_placement = PLOT_LEGEND_TOP_RIGHT;
 
338
        break;
 
339
    case QalculateSettings::LEGEND_BOTTOM_LEFT:
 
340
        plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_LEFT;
 
341
        break;
 
342
    case QalculateSettings::LEGEND_BOTTOM_RIGHT:
 
343
        plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_RIGHT;
 
344
        break;
 
345
    case QalculateSettings::LEGEND_BELOW:
 
346
        plotParameters.legend_placement = PLOT_LEGEND_BELOW;
 
347
        break;
 
348
    case QalculateSettings::LEGEND_OUTSIDE:
 
349
        plotParameters.legend_placement = PLOT_LEGEND_OUTSIDE;
 
350
        break;
 
351
    }
 
352
    bool plotInline = QalculateSettings::inlinePlot();
 
353
    MathStructure xMin;
 
354
    MathStructure xMax;
 
355
    xMin.setUndefined();
 
356
    xMax.setUndefined();
 
357
    MathStructure stepLength;
 
358
    stepLength.setUndefined();
 
359
    int steps = QalculateSettings::plotSteps();
 
360
    
 
361
    QString mustBeNumber = i18n("%1 must be a number.");
 
362
    QString mustBeInteger = i18n("%1 must be a integer.");
 
363
    QString mustBeBoolean = i18n("%1 must be a boolean.");
 
364
    QString invalidOption = i18n("invalid option for %1: %2");
 
365
 
 
366
    for (int i = 0; i < argumentsList.size(); ++i) {
 
367
        std::string xVariable = "x";
 
368
        PlotDataParameters* plotDataParams = new PlotDataParameters;
 
369
        plotDataParameterList.push_back(plotDataParams);
 
370
        plotDataParams->title = "";
 
371
        switch(QalculateSettings::plotSmoothing()) {
 
372
        case QalculateSettings::SMOOTHING_NONE:
 
373
            plotDataParams->smoothing = PLOT_SMOOTHING_NONE;
 
374
            break;
 
375
        case QalculateSettings::SMOOTHING_UNIQUE:
 
376
            plotDataParams->smoothing = PLOT_SMOOTHING_UNIQUE;
 
377
            break;
 
378
        case QalculateSettings::SMOOTHING_CSPLINES:
 
379
            plotDataParams->smoothing = PLOT_SMOOTHING_CSPLINES;
 
380
            break;
 
381
        case QalculateSettings::SMOOTHING_BEZIER:
 
382
            plotDataParams->smoothing = PLOT_SMOOTHING_BEZIER;
 
383
            break;
 
384
        case QalculateSettings::SMOOTHING_SBEZIER:
 
385
            plotDataParams->smoothing = PLOT_SMOOTHING_SBEZIER;
 
386
            break;
 
387
        }
 
388
        switch(QalculateSettings::plotStyle()) {
 
389
        case QalculateSettings::STYLE_LINES:
 
390
            plotDataParams->style = PLOT_STYLE_LINES;
 
391
            break;
 
392
        case QalculateSettings::STYLE_POINTS:
 
393
            plotDataParams->style = PLOT_STYLE_POINTS;
 
394
            break;
 
395
        case QalculateSettings::STYLE_LINES_POINTS:
 
396
            plotDataParams->style = PLOT_STYLE_POINTS_LINES;
 
397
            break;
 
398
        case QalculateSettings::STYLE_BOXES:
 
399
            plotDataParams->style = PLOT_STYLE_BOXES;
 
400
            break;
 
401
        case QalculateSettings::STYLE_HISTOGRAM:
 
402
            plotDataParams->style = PLOT_STYLE_HISTOGRAM;
 
403
            break;
 
404
        case QalculateSettings::STYLE_STEPS:
 
405
            plotDataParams->style = PLOT_STYLE_STEPS;
 
406
            break;
 
407
        case QalculateSettings::STYLE_CANDLESTICKS:
 
408
            plotDataParams->style = PLOT_STYLE_CANDLESTICKS;
 
409
            break;
 
410
        case QalculateSettings::STYLE_DOTS:
 
411
            plotDataParams->style = PLOT_STYLE_DOTS;
 
412
            break;
 
413
        }
 
414
        plotDataParams->yaxis2 = false;
 
415
        plotDataParams->xaxis2 = false;
 
416
        arguments = argumentsList[i];
 
417
        std::string expression;
 
418
        int lastExpressionEntry = -1;
 
419
        for (int j = 0; j < arguments.size(); ++j) {
 
420
            QString argument = arguments[j];
 
421
            // PlotParameters
 
422
            if (argument.startsWith("plottitle="))
 
423
                plotParameters.title = argument.mid(10).toLatin1().data();
 
424
            else if (argument.startsWith("ylabel="))
 
425
                plotParameters.y_label = argument.mid(7).toLatin1().data();
 
426
            else if (argument.startsWith("xlabel="))
 
427
                plotParameters.x_label = argument.mid(7).toLatin1().data();
 
428
            else if (argument.startsWith("filename="))
 
429
                plotParameters.filename = argument.mid(9).toLatin1().data();
 
430
            else if (argument.startsWith("filetype=")) {
 
431
                QString option = argument.mid(9);
 
432
                if (option == "auto")
 
433
                    plotParameters.filetype = PLOT_FILETYPE_AUTO;
 
434
                else if (option == "png")
 
435
                    plotParameters.filetype = PLOT_FILETYPE_PNG;
 
436
                else if (option == "ps")
 
437
                    plotParameters.filetype = PLOT_FILETYPE_PS;
 
438
                else if (option == "eps")
 
439
                    plotParameters.filetype = PLOT_FILETYPE_EPS;
 
440
                else if (option == "latex")
 
441
                    plotParameters.filetype = PLOT_FILETYPE_LATEX;
 
442
                else if (option == "svg")
 
443
                    plotParameters.filetype = PLOT_FILETYPE_SVG;
 
444
                else if (option == "fig")
 
445
                    plotParameters.filetype = PLOT_FILETYPE_FIG;
 
446
                else {
 
447
                    QString msg = invalidOption.arg("filetype", option);
 
448
                    showMessage(msg, MESSAGE_ERROR);
 
449
                    deletePlotDataParameters(plotDataParameterList);
 
450
                    return;
 
451
                }
 
452
            }
 
453
            else if (argument.startsWith("font="))
 
454
                plotParameters.font = argument.mid(5).toLatin1().data();
 
455
            else if (argument.startsWith("color=")) {
 
456
                bool ok;
 
457
                plotParameters.color = stringToBool(argument.mid(6), &ok);
 
458
                if (!ok) {
 
459
                    showMessage(mustBeBoolean.arg("color"),
 
460
                                MESSAGE_ERROR);
 
461
                    deletePlotDataParameters(plotDataParameterList);
 
462
                    return;
 
463
                }
 
464
            }
 
465
            else if (argument.startsWith("ylog=")) {
 
466
                bool ok;
 
467
                plotParameters.y_log = stringToBool(argument.mid(5), &ok);
 
468
                if (!ok) {
 
469
                    showMessage(mustBeBoolean.arg("ylog"), MESSAGE_ERROR);
 
470
                    deletePlotDataParameters(plotDataParameterList);
 
471
                    return;
 
472
                }
 
473
            }
 
474
            else if (argument.startsWith("xlog=")) {
 
475
                bool ok;
 
476
                plotParameters.x_log = stringToBool(argument.mid(5), &ok);
 
477
                if (!ok) {
 
478
                    showMessage(mustBeBoolean.arg("xlog"), MESSAGE_ERROR);
 
479
                    return;
 
480
                }
 
481
            }
 
482
            else if (argument.startsWith("ylogbase=")) {
 
483
                MathStructure ylogStr = CALCULATOR->calculate(argument.mid(9).toLatin1().data(), eo);
 
484
                if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
 
485
                    deletePlotDataParameters(plotDataParameterList);
 
486
                    return;
 
487
                }
 
488
                if (ylogStr.isNumber()) {
 
489
                    Number ylogNum = ylogStr.number();
 
490
                    plotParameters.y_log_base = ylogNum.floatValue();
 
491
                } else {
 
492
                    showMessage(mustBeNumber.arg("ylogbase"), MESSAGE_ERROR);
 
493
                    deletePlotDataParameters(plotDataParameterList);
 
494
                    return;
 
495
                }
 
496
            }
 
497
            else if (argument.startsWith("xlogbase=")) {
 
498
                MathStructure xlogStr = CALCULATOR->calculate(argument.mid(9).toLatin1().data(), eo);
 
499
                if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
 
500
                    deletePlotDataParameters(plotDataParameterList);
 
501
                    return;
 
502
                }
 
503
                if (xlogStr.isNumber()) {
 
504
                    Number xlogNum = xlogStr.number();
 
505
                    plotParameters.x_log_base = xlogNum.floatValue();
 
506
                } else {
 
507
                    showMessage(mustBeNumber.arg("xlogbase"), MESSAGE_ERROR);
 
508
                    deletePlotDataParameters(plotDataParameterList);
 
509
                    return;
 
510
                }
 
511
            }
 
512
            else if (argument.startsWith("grid=")) {
 
513
                bool ok;
 
514
                plotParameters.grid = stringToBool(argument.mid(5), &ok);
 
515
                if (!ok) {
 
516
                    showMessage(mustBeBoolean.arg("grid"), MESSAGE_ERROR);
 
517
                    deletePlotDataParameters(plotDataParameterList);
 
518
                    return;
 
519
                }
 
520
            }
 
521
            else if (argument.startsWith("linewidth=")) {
 
522
                MathStructure lineWidthStr = CALCULATOR->calculate(argument.mid(10).toLatin1().data(), eo);
 
523
                Number lineWidthNum;
 
524
                if (lineWidthStr.isNumber() && lineWidthStr.number().isInteger()) {
 
525
                    lineWidthNum = lineWidthStr.number();
 
526
                    plotParameters.linewidth = lineWidthNum.intValue();
 
527
                } else {
 
528
                    showMessage(mustBeInteger.arg("linewidth"), MESSAGE_ERROR);
 
529
                    deletePlotDataParameters(plotDataParameterList);
 
530
                    return;
 
531
                }
 
532
            }
 
533
            else if (argument.startsWith("border=")) {
 
534
                bool ok;
 
535
                plotParameters.show_all_borders = stringToBool(argument.mid(7), &ok);
 
536
                if (!ok) {
 
537
                    showMessage(mustBeBoolean.arg("border"), MESSAGE_ERROR);
 
538
                    deletePlotDataParameters(plotDataParameterList);
 
539
                    return;
 
540
                }
 
541
            }
 
542
            else if (argument.startsWith("legend=")) {
 
543
                QString option = argument.mid(7);
 
544
                if (option == "none")
 
545
                    plotParameters.legend_placement = PLOT_LEGEND_NONE;
 
546
                else if (option == "top_left")
 
547
                    plotParameters.legend_placement = PLOT_LEGEND_TOP_LEFT;
 
548
                else if (option == "top_right")
 
549
                    plotParameters.legend_placement = PLOT_LEGEND_TOP_RIGHT;
 
550
                else if (option == "bottom_left")
 
551
                    plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_LEFT;
 
552
                else if (option == "bottom_right")
 
553
                    plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_RIGHT;
 
554
                else if (option == "below")
 
555
                    plotParameters.legend_placement = PLOT_LEGEND_BELOW;
 
556
                else if (option == "outside")
 
557
                    plotParameters.legend_placement = PLOT_LEGEND_OUTSIDE;
 
558
                else {
 
559
                    QString msg = invalidOption.arg("legend", option);
 
560
                    showMessage(msg, MESSAGE_ERROR);
 
561
                    deletePlotDataParameters(plotDataParameterList);
 
562
                    return;
 
563
                }
 
564
            }
 
565
            // PlotDataParameters
 
566
            else if (argument.startsWith("title=")) {
 
567
                plotDataParams->title = argument.mid(6).toLatin1().data();
 
568
            }
 
569
            else if (argument.startsWith("smoothing=")) {
 
570
                QString option = argument.mid(10);
 
571
                if (option == "none")
 
572
                    plotDataParams->smoothing = PLOT_SMOOTHING_NONE;
 
573
                else if (option == "monotonic")
 
574
                    plotDataParams->smoothing = PLOT_SMOOTHING_UNIQUE;
 
575
                else if (option == "csplines")
 
576
                    plotDataParams->smoothing = PLOT_SMOOTHING_CSPLINES;
 
577
                else if (option == "bezier")
 
578
                    plotDataParams->smoothing = PLOT_SMOOTHING_BEZIER;
 
579
                else if (option == "sbezier")
 
580
                    plotDataParams->smoothing = PLOT_SMOOTHING_SBEZIER;
 
581
                else {
 
582
                    QString msg = invalidOption.arg("smoothing", option);
 
583
                    showMessage(msg, MESSAGE_ERROR);
 
584
                    deletePlotDataParameters(plotDataParameterList);
 
585
                    return;
 
586
                }
 
587
            } else if (argument.startsWith("style=")) {
 
588
                QString option = argument.mid(6);
 
589
                if (option == "lines")
 
590
                    plotDataParams->style = PLOT_STYLE_LINES;
 
591
                else if (option == "points")
 
592
                    plotDataParams->style = PLOT_STYLE_POINTS;
 
593
                else if (option == "points_lines")
 
594
                    plotDataParams->style = PLOT_STYLE_POINTS_LINES;
 
595
                else if (option == "boxes")
 
596
                    plotDataParams->style = PLOT_STYLE_BOXES;
 
597
                else if (option == "histogram")
 
598
                    plotDataParams->style = PLOT_STYLE_HISTOGRAM;
 
599
                else if (option == "steps")
 
600
                    plotDataParams->style = PLOT_STYLE_STEPS;
 
601
                else if (option == "candlesticks")
 
602
                    plotDataParams->style = PLOT_STYLE_CANDLESTICKS;
 
603
                else if (option == "dots")
 
604
                    plotDataParams->style = PLOT_STYLE_DOTS;
 
605
                else {
 
606
                    QString msg = invalidOption.arg("style", option);
 
607
                    showMessage(msg, MESSAGE_ERROR);
 
608
                    deletePlotDataParameters(plotDataParameterList);
 
609
                    return;
 
610
                }
 
611
            } else if (argument.startsWith("xaxis2=")) {
 
612
                bool ok;
 
613
                plotDataParams->xaxis2 = stringToBool(argument.mid(7), &ok);
 
614
                if (!ok) {
 
615
                    showMessage(mustBeBoolean.arg("xaxis2"), MESSAGE_ERROR);
 
616
                    deletePlotDataParameters(plotDataParameterList);
 
617
                    return;
 
618
                }
 
619
            } else if (argument.startsWith("yaxis2=")) {
 
620
                bool ok;
 
621
                plotDataParams->yaxis2 = stringToBool(argument.mid(7), &ok);
 
622
                if (!ok) {
 
623
                    showMessage(mustBeBoolean.arg("yaxis2"), MESSAGE_ERROR);
 
624
                    deletePlotDataParameters(plotDataParameterList);
 
625
                    return;
 
626
                }
 
627
            }
 
628
            // inline, xmin, xmax, step, steps, xvar
 
629
            // Custom options
 
630
            else if (argument.startsWith("inline=")) {
 
631
                bool ok;
 
632
                plotInline = stringToBool(argument.mid(7), &ok);
 
633
                if (!ok) {
 
634
                    showMessage(mustBeBoolean.arg("inline"), MESSAGE_ERROR);
 
635
                    deletePlotDataParameters(plotDataParameterList);
 
636
                    return;
 
637
                }
 
638
            }
 
639
            else if (argument.startsWith("xmin=")) {
 
640
                xMin = CALCULATOR->calculate(argument.mid(5).toLatin1().data(), eo);
 
641
                if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
 
642
                    deletePlotDataParameters(plotDataParameterList);
 
643
                    return;
 
644
                }
 
645
            }
 
646
            else if (argument.startsWith("xmax=")) {
 
647
                xMax = CALCULATOR->calculate(argument.mid(5).toLatin1().data(), eo);
 
648
                if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
 
649
                    deletePlotDataParameters(plotDataParameterList);
 
650
                    return;
 
651
                }
 
652
            }
 
653
            else if (argument.startsWith("step=")) {
 
654
                stepLength = CALCULATOR->calculate(argument.mid(5).toLatin1().data(), eo);
 
655
                if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
 
656
                    deletePlotDataParameters(plotDataParameterList);
 
657
                    return;
 
658
                }
 
659
                steps = -1;
 
660
            }
 
661
            else if (argument.startsWith("steps=")) {
 
662
                MathStructure stepsStr = CALCULATOR->calculate(argument.mid(6).toLatin1().data(), eo);
 
663
                if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
 
664
                    deletePlotDataParameters(plotDataParameterList);
 
665
                    return;
 
666
                }
 
667
                Number stepsNum;
 
668
                if (stepsStr.isNumber() && stepsStr.number().isInteger()) {
 
669
                    stepsNum = stepsStr.number();
 
670
                    steps = stepsNum.intValue();
 
671
                    stepLength.setUndefined();
 
672
                } else {
 
673
                    showMessage(mustBeInteger.arg("steps"), MESSAGE_ERROR);
 
674
                    deletePlotDataParameters(plotDataParameterList);
 
675
                    return;
 
676
                }
 
677
            }
 
678
            else if (argument.startsWith("xvar=")) {
 
679
                xVariable = argument.mid(5).toLatin1().data();
 
680
            }
 
681
            else if (expression.empty()) {
 
682
                expression = argument.toLatin1().data();
 
683
                lastExpressionEntry = j;
 
684
            }
 
685
            else if (lastExpressionEntry == j-1) {
 
686
                expression += " ";
 
687
                expression += argument.toLatin1().data();
 
688
                lastExpressionEntry = j;
 
689
            }
 
690
            else {
 
691
                QString msg = i18n("found multiple expressions in one plot command (%1 and %2).", QString(expression.c_str()), argument);
 
692
                showMessage(msg, MESSAGE_ERROR);
 
693
                deletePlotDataParameters(plotDataParameterList);
 
694
                return;
 
695
            }
 
696
        }
 
697
        if (expression.empty())
 
698
            continue;
 
699
        if (xMin.isUndefined()) {
 
700
            if (!plotParameters.auto_x_min)
 
701
                xMin = plotParameters.x_min;
 
702
            else
 
703
                xMin = 0.0;
 
704
        }
 
705
        if (xMax.isUndefined()) {
 
706
            if (!plotParameters.auto_x_max)
 
707
                xMax = plotParameters.x_max;
 
708
            else
 
709
                xMax = 10.0;
 
710
        }
 
711
        if (plotDataParams->title.empty())
 
712
            plotDataParams->title = expression;
 
713
        MathStructure x_vec, y_vec;
 
714
        x_vec.clearVector();
 
715
        if (!stepLength.isUndefined())
 
716
            y_vec = CALCULATOR->expressionToPlotVector(expression, xMin, xMax, stepLength, &x_vec, xVariable, eo.parse_options);
 
717
        else
 
718
            y_vec = CALCULATOR->expressionToPlotVector(expression, xMin, xMax, steps, &x_vec, xVariable, eo.parse_options);
 
719
        if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
 
720
            deletePlotDataParameters(plotDataParameterList);
 
721
            return;
 
722
        }
 
723
 
 
724
        x_vectors.push_back(x_vec);
 
725
        y_vectors.push_back(y_vec);
 
726
 
 
727
        //PrintOptions po;
 
728
        //y_vec.format(po);
 
729
        
 
730
        //setResult(new Cantor::TextResult(y_vec.print(po).c_str()));
 
731
        //setStatus(Done);
 
732
        //deletePlotDataParameters(plotDataParameterList);
 
733
        //return;
 
734
    }
 
735
 
 
736
    if (plotInline && plotParameters.filename.empty()) {
 
737
        // TODO: get a temporary file name here
 
738
        if (!m_tempFile) {
 
739
            m_tempFile = new KTemporaryFile();
 
740
#ifdef WITH_EPS
 
741
            m_tempFile->setSuffix(".eps");
 
742
#else
 
743
            m_tempFile->setSuffix(".png");
 
744
#endif
 
745
            m_tempFile->open();
 
746
        }
 
747
        plotParameters.filename = m_tempFile->fileName().toLatin1().data();
 
748
        plotParameters.filetype = PLOT_FILETYPE_AUTO;
 
749
    }
 
750
 
 
751
    CALCULATOR->plotVectors(&plotParameters, y_vectors, x_vectors, 
 
752
                            plotDataParameterList);
 
753
    if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
 
754
        deletePlotDataParameters(plotDataParameterList);
 
755
        return;
 
756
    }
 
757
 
 
758
    deletePlotDataParameters(plotDataParameterList);
 
759
 
 
760
    if (plotInline) {
 
761
#ifdef WITH_EPS
 
762
        size_t p = plotParameters.filename.size();
 
763
        if (plotParameters.filetype == PLOT_FILETYPE_EPS ||
 
764
            plotParameters.filetype == PLOT_FILETYPE_PS  ||
 
765
            (plotParameters.filetype == PLOT_FILETYPE_AUTO && p >= 4 &&
 
766
             plotParameters.filename.substr(p-4,4) == ".eps") ||
 
767
            (plotParameters.filetype == PLOT_FILETYPE_AUTO && p >= 3 &&
 
768
             plotParameters.filename.substr(p-3,3) == ".ps")) 
 
769
            setResult(new Cantor::EpsResult(KUrl(plotParameters.filename.c_str())));
 
770
        else
 
771
            setResult(new Cantor::ImageResult(KUrl(plotParameters.filename.c_str())));
 
772
#else
 
773
        setResult(new Cantor::ImageResult(KUrl(plotParameters.filename.c_str())));
 
774
#endif
 
775
        setStatus(Cantor::Expression::Done);
 
776
    }
 
777
 
 
778
}
 
779
 
 
780
void QalculateExpression::showMessage(QString msg, MessageType mtype)
 
781
{
 
782
    KColorScheme scheme(QApplication::palette().currentColorGroup());
 
783
    const QString errorColor = scheme.foreground(KColorScheme::NegativeText).color().name();
 
784
    const QString warningColor = scheme.foreground(KColorScheme::NeutralText).color().name();
 
785
    const QString msgFormat("<font color=\"%1\">%2: %3</font><br>\n");
 
786
    if(mtype == MESSAGE_ERROR || mtype == MESSAGE_WARNING) {
 
787
        msg.replace("&", "&amp;");
 
788
        msg.replace(">", "&gt;");
 
789
        msg.replace("<", "&lt;");
 
790
 
 
791
        if (mtype == MESSAGE_ERROR) {
 
792
            msg = msgFormat.arg(errorColor, i18n("ERROR"), msg.toLatin1().data());
 
793
        } else {
 
794
            msg = msgFormat.arg(errorColor, i18n("WARNING"), msg.toLatin1().data());
 
795
        }
 
796
        setErrorMessage(msg);
 
797
        setStatus(Error);
 
798
    } else {
 
799
        KMessageBox::information(QApplication::activeWindow(), msg);
 
800
    }
 
801
}
 
802
 
 
803
EvaluationOptions QalculateExpression::evaluationOptions()
 
804
{
 
805
    EvaluationOptions eo;
 
806
 
 
807
    eo.auto_post_conversion = QalculateSettings::postConversion() ? POST_CONVERSION_BEST : POST_CONVERSION_NONE;
 
808
    eo.keep_zero_units = false;
 
809
 
 
810
    eo.parse_options = parseOptions();
 
811
 
 
812
    switch (QalculateSettings::structuring()) {
 
813
        case 0:
 
814
            eo.structuring = STRUCTURING_NONE;
 
815
            break;
 
816
        case 1:
 
817
            eo.structuring = STRUCTURING_SIMPLIFY;
 
818
            break;
 
819
        case 2:
 
820
            eo.structuring = STRUCTURING_FACTORIZE;
 
821
            break;
 
822
    }
 
823
    
 
824
    return eo;
 
825
}
 
826
 
 
827
ParseOptions QalculateExpression::parseOptions()
 
828
{
 
829
    ParseOptions po;
 
830
    switch (QalculateSettings::angleUnit()) {
 
831
        case 0:
 
832
            po.angle_unit = ANGLE_UNIT_NONE;
 
833
            break;
 
834
        case 1:
 
835
            po.angle_unit = ANGLE_UNIT_RADIANS;
 
836
            break;
 
837
        case 2:
 
838
            po.angle_unit = ANGLE_UNIT_DEGREES;
 
839
            break;
 
840
        case 3:
 
841
            po.angle_unit = ANGLE_UNIT_GRADIANS;
 
842
            break;
 
843
    }
 
844
 
 
845
    po.base = QalculateSettings::base();
 
846
 
 
847
    return po;
 
848
}
 
849
 
 
850
void QalculateExpression::deletePlotDataParameters
 
851
    (const std::vector<PlotDataParameters*>& plotDataParameterList)
 
852
{
 
853
    for(int i = 0; i < plotDataParameterList.size(); ++i)
 
854
        delete plotDataParameterList[i];
 
855
}
 
856
 
 
857
bool QalculateExpression::stringToBool(const QString &string, bool *ok)
 
858
{
 
859
    if (string == "true" || string == "1") {
 
860
        *ok = true;
 
861
        return true;
 
862
    } else if (string == "false" || string == "0") {
 
863
        *ok = true;
 
864
        return false;
 
865
    } else {
 
866
        *ok = false;
 
867
        return false;
 
868
    }
 
869
}
 
870
 
 
871
int QalculateExpression::checkForCalculatorMessages()
 
872
{
 
873
    // error handling, most of it copied from qalculate-kde
 
874
    int msgType = MSG_NONE;
 
875
    if ( CALCULATOR->message() ) {
 
876
        QString msg;
 
877
        KColorScheme scheme(QApplication::palette().currentColorGroup());
 
878
        const QString errorColor = scheme.foreground(KColorScheme::NegativeText).color().name();
 
879
        const QString warningColor = scheme.foreground(KColorScheme::NeutralText).color().name();
 
880
        const QString msgFormat("<font color=\"%1\">%2: %3</font><br>\n");
 
881
        MessageType mtype;
 
882
        while(true) {
 
883
            mtype = CALCULATOR->message()->type();
 
884
            switch(mtype) {
 
885
            case MESSAGE_INFORMATION:
 
886
                msgType |= MSG_INFO; break;
 
887
            case MESSAGE_WARNING:
 
888
                msgType |= MSG_WARN; break;
 
889
            case MESSAGE_ERROR:
 
890
                msgType |= MSG_ERR;  break;
 
891
            }
 
892
            if(mtype == MESSAGE_ERROR || mtype == MESSAGE_WARNING) {
 
893
                QString text = CALCULATOR->message()->message().c_str();
 
894
                text.replace("&", "&amp;");
 
895
                text.replace(">", "&gt;");
 
896
                text.replace("<", "&lt;");
 
897
 
 
898
                if (mtype == MESSAGE_ERROR) {
 
899
                    msg.append(msgFormat.arg(errorColor, i18n("ERROR"), text));
 
900
                } else {
 
901
                    msg.append(msgFormat.arg(errorColor, i18n("WARNING"), text));
 
902
                }
 
903
            } else {
 
904
                KMessageBox::information(QApplication::activeWindow(), CALCULATOR->message()->message().c_str());
 
905
            }
 
906
            if(!CALCULATOR->nextMessage()) break;
 
907
        }
 
908
        if ( !msg.isEmpty() ) {
 
909
            m_message += msg;
 
910
            setErrorMessage(m_message);
 
911
            setStatus(Error);
 
912
        }
 
913
    }
 
914
    return msgType;
 
915
}
 
916
 
 
917
std::string QalculateExpression::unlocalizeExpression(QString expr)
 
918
{
 
919
    // copy'n'pasted from qalculate plasma applet
 
920
 
 
921
    return CALCULATOR->unlocalizeExpression(
 
922
             expr.replace(QChar(0xA3), "GBP")
 
923
                 .replace(QChar(0xA5), "JPY")
 
924
                 .replace("$", "USD")
 
925
                 .replace(QChar(0x20AC), "EUR")
 
926
                 .toLatin1().data()
 
927
           );
 
928
}
 
929
 
 
930
void QalculateExpression::updateVariables(MathStructure command)
 
931
{
 
932
    Cantor::DefaultVariableModel* model = 
 
933
        static_cast<Cantor::DefaultVariableModel*>(session()->variableModel());
 
934
    QStack<MathStructure*> stack;
 
935
    stack.push(&command);
 
936
    QSharedPointer<PrintOptions> po = printOptions();
 
937
    while (!stack.isEmpty()) {
 
938
        MathStructure* current = stack.pop();
 
939
        if (current->isFunction() && current->function()->name() == "save" &&
 
940
            current->countChildren() >= 2 && current->getChild(2)->isSymbolic())
 
941
        {
 
942
            // I'd like to use CALCULATOR->getVariable and KnownVariable::get,
 
943
            // but that doesn't work for built in variables, as it keeps
 
944
            // returning the old value
 
945
            std::string name = current->getChild(2)->symbol();
 
946
            MathStructure m = CALCULATOR->calculate(name, evaluationOptions());
 
947
            m.format(*po);
 
948
            model->addVariable(name.c_str(), m.print(*po).c_str());
 
949
        }
 
950
        for (int i = 0; i < current->countChildren(); ++i) {
 
951
            stack.push(current->getChild(i+1));
 
952
        }
 
953
    }
 
954
}
 
955
 
 
956
QSharedPointer<PrintOptions> QalculateExpression::printOptions()
 
957
{
 
958
    QSharedPointer<PrintOptions> po(new PrintOptions);
 
959
 
 
960
    switch (QalculateSettings::fractionFormat()) {
 
961
    case 0:
 
962
        po->number_fraction_format = FRACTION_DECIMAL;
 
963
        break;
 
964
    case 1:
 
965
        po->number_fraction_format = FRACTION_DECIMAL_EXACT;
 
966
        break;
 
967
    case 2:
 
968
        po->number_fraction_format = FRACTION_FRACTIONAL;
 
969
        break;
 
970
    case 3:
 
971
        po->number_fraction_format = FRACTION_COMBINED;
 
972
        break;
 
973
    }
 
974
    po->indicate_infinite_series = QalculateSettings::indicateInfiniteSeries();
 
975
    po->use_all_prefixes = QalculateSettings::useAllPrefixes();
 
976
    po->negative_exponents = QalculateSettings::negativeExponents();
 
977
 
 
978
    po->lower_case_e = true;
 
979
    po->base = QalculateSettings::base();
 
980
    po->decimalpoint_sign = KGlobal::locale()->decimalSymbol().toLocal8Bit().data();
 
981
 
 
982
    switch (QalculateSettings::minExp()) {
 
983
    case 0:
 
984
        po->min_exp = EXP_NONE;
 
985
        break;
 
986
    case 1:
 
987
        po->min_exp = EXP_PURE;
 
988
        break;
 
989
    case 2:
 
990
        po->min_exp = EXP_SCIENTIFIC;
 
991
        break;
 
992
    case 3:
 
993
        po->min_exp = EXP_PRECISION;
 
994
        break;
 
995
    case 4:
 
996
        po->min_exp = EXP_BASE_3;
 
997
        break;
 
998
    }
 
999
    return po;
 
1000
}