File indexing completed on 2024-04-28 11:20:38

0001 /*
0002     SPDX-FileCopyrightText: 2009 Milian Wolff <mail@milianw.de>
0003     SPDX-FileCopyrightText: 2011 Matteo Agostinelli <agostinelli@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include <config-cantorlib.h>
0009 
0010 #include "textresult.h"
0011 #include "helpresult.h"
0012 #include "imageresult.h"
0013 #include "epsresult.h"
0014 #include "settings.h"
0015 #include "defaultvariablemodel.h"
0016 
0017 #include "qalculateexpression.h"
0018 #include "qalculatesession.h"
0019 #include "qalculatesyntaxhelpobject.h"
0020 
0021 #include <libqalculate/ExpressionItem.h>
0022 #include <libqalculate/Unit.h>
0023 #include <libqalculate/Prefix.h>
0024 #include <libqalculate/Variable.h>
0025 #include <libqalculate/Function.h>
0026 
0027 // required for the plotting interface of Qalculator
0028 
0029 #include <QDir>
0030 #include <QTemporaryFile>
0031 
0032 #include <KMessageBox>
0033 #include <KColorScheme>
0034 #include <KLocalizedString>
0035 #include <QLocale>
0036 
0037 #include <QApplication>
0038 #include <QStack>
0039 
0040 QalculateExpression::QalculateExpression( QalculateSession* session, bool internal)
0041     : Cantor::Expression(session, internal)
0042 {
0043 }
0044 
0045 QalculateExpression::~QalculateExpression()
0046 {
0047     if (m_tempFile)
0048         delete m_tempFile;
0049 }
0050 
0051 void QalculateExpression::evaluate()
0052 {
0053     /*
0054         Use Api for:
0055         * help
0056         * plot
0057         Use qalc for any other command
0058     */
0059     setStatus(Cantor::Expression::Computing);
0060     if (command().isEmpty()) {
0061         setStatus(Cantor::Expression::Done);
0062         return;
0063     }
0064 
0065 
0066     QStringList commands = command().split(QLatin1Char('\n'));
0067     foreach(const QString& command, commands)
0068     {
0069         if (command.contains(QLatin1String("help"))) {
0070             QalculateSyntaxHelpObject* helper = new QalculateSyntaxHelpObject(command, (QalculateSession*) session());
0071             setResult(new Cantor::HelpResult(helper->answer()));
0072             setStatus(Cantor::Expression::Done);
0073             return;
0074         }
0075         else if (command.trimmed().startsWith(QLatin1String("plot")) &&
0076                  (command.indexOf(QLatin1String("plot"))+4 == command.size() ||
0077                   command[command.indexOf(QLatin1String("plot"))+4].isSpace())) {
0078             evaluatePlotCommand();
0079             return;
0080         }
0081     }
0082     // we are here because the commands entered by user are regular commands. We would have returned by now otherwise
0083     QalculateSession* currentSession = dynamic_cast<QalculateSession*>(session());
0084     currentSession->runExpression();
0085 }
0086 
0087 void QalculateExpression::parseOutput(const QString& output)
0088 {
0089     QString resultStr = output;
0090     resultStr.remove(QLatin1String(">"));
0091     resultStr = resultStr.trimmed();
0092 
0093     qDebug() << "output from qalc for command: " << command() << " " << resultStr;
0094     setResult(new Cantor::TextResult(resultStr));
0095     // update the variable model
0096     updateVariables();
0097     setStatus(Cantor::Expression::Done);
0098 }
0099 
0100 void QalculateExpression::updateVariables()
0101 {
0102     auto* currentSession = dynamic_cast<QalculateSession*>(session());
0103     auto& variables = currentSession->variables;
0104     auto it = variables.constBegin();
0105     while (it != variables.constEnd()) {
0106         currentSession->variableModel()->addVariable(it.key(), it.value());
0107         ++it;
0108     }
0109 }
0110 
0111 void QalculateExpression::parseError(const QString& error)
0112 {
0113     QString errorStr = error;
0114     errorStr.remove(QLatin1String(">"));
0115     errorStr  = errorStr.trimmed();
0116     qDebug() << "Error from qalc for command: " << command() <<  " " << error;
0117     setErrorMessage(errorStr);
0118     setStatus(Cantor::Expression::Error);
0119 }
0120 
0121 void QalculateExpression::evaluatePlotCommand()
0122 {
0123     QString argString = command().mid(command().indexOf(QLatin1String("plot"))+4);
0124     argString = QLatin1String(unlocalizeExpression(argString).c_str());
0125     argString = argString.trimmed();
0126 
0127     QList<QStringList> argumentsList;
0128     QStringList arguments;
0129 
0130     // For error handling
0131     KColorScheme scheme(QApplication::palette().currentColorGroup());
0132     const QString errorColor = scheme.foreground(KColorScheme::NegativeText).color().name();
0133     const QString warningColor = scheme.foreground(KColorScheme::NeutralText).color().name();
0134     const QString msgFormat(QLatin1String("<font color=\"%1\">%2: %3</font><br>\n"));
0135 
0136     if (!CALCULATOR->canPlot()) {
0137         showMessage(i18n("Qalculate reports it cannot print. Is gnuplot installed?"), MESSAGE_ERROR);
0138         return;
0139     }
0140 
0141     // Split argString into the arguments
0142     int i=0;
0143     int j=0;
0144     QString arg = QLatin1String("");
0145     while (i < argString.size()) {
0146         if (argString[i] == QLatin1Char('"') || argString[i] == QLatin1Char('\'')) {
0147         ++j;
0148         while(j < argString.size() && argString[j] != argString[i]) {
0149             if (argString[j] == QLatin1Char('\\')) {
0150             ++j;
0151             if (j == argString.size())
0152             continue; // just ignore trailing backslashes
0153         }
0154         arg += argString[j];
0155             ++j;
0156         }
0157         if (j == argString.size()) {
0158         showMessage(i18n("missing %1", argString[i]), MESSAGE_ERROR);
0159         return;
0160         }
0161         ++j;
0162         } else if (argString[i] == QLatin1Char(',')) {
0163         argumentsList.append(arguments);
0164         arguments.clear();
0165         ++j;
0166     } else {
0167         while(j < argString.size() && !argString[j].isSpace() &&
0168           argString[j] != QLatin1Char('=') && argString[j] != QLatin1Char(',')) {
0169             if (argString[j] == QLatin1Char('\\')) {
0170             ++j;
0171             if (j == argString.size())
0172             continue; // just ignore trailing backslashes
0173         }
0174         arg += argString[j];
0175         ++j;
0176         }
0177     }
0178     if (argString[j] == QLatin1Char('=')) {
0179         // Parse things like title="..." as one argument
0180         arg += QLatin1Char('=');
0181         i = ++j;
0182         continue;
0183     }
0184     if (!arg.isEmpty()) {
0185         arguments << arg;
0186         arg = QLatin1String("");
0187     }
0188     while (j < argString.size() && argString[j].isSpace())
0189         ++j;
0190     i = j;
0191     }
0192     argumentsList.append(arguments);
0193 
0194     // Parse the arguments and compute the points to be plotted
0195     std::vector<MathStructure> y_vectors;
0196     std::vector<MathStructure> x_vectors;
0197     std::vector<PlotDataParameters*> plotDataParameterList;
0198     PlotParameters plotParameters;
0199     EvaluationOptions eo = evaluationOptions();
0200     /// temporary
0201     plotParameters.title = "";
0202     plotParameters.y_label = "";
0203     plotParameters.x_label = "";
0204     plotParameters.filename = "";
0205     plotParameters.filetype = PLOT_FILETYPE_AUTO;
0206     plotParameters.color = QalculateSettings::coloredPlot();
0207     plotParameters.auto_y_min = true;
0208     plotParameters.auto_x_min = true;
0209     plotParameters.auto_x_max = true;
0210     plotParameters.auto_y_max = true;
0211     plotParameters.y_log = false;
0212     plotParameters.x_log = false;
0213     plotParameters.grid = QalculateSettings::plotGrid();
0214     plotParameters.linewidth = QalculateSettings::plotLineWidth();
0215     plotParameters.show_all_borders = QalculateSettings::plotBorder();
0216     switch (QalculateSettings::plotLegend()) {
0217     case QalculateSettings::LEGEND_NONE:
0218     plotParameters.legend_placement = PLOT_LEGEND_NONE;
0219     break;
0220     case QalculateSettings::LEGEND_TOP_LEFT:
0221     plotParameters.legend_placement = PLOT_LEGEND_TOP_LEFT;
0222     break;
0223     case QalculateSettings::LEGEND_TOP_RIGHT:
0224     plotParameters.legend_placement = PLOT_LEGEND_TOP_RIGHT;
0225     break;
0226     case QalculateSettings::LEGEND_BOTTOM_LEFT:
0227     plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_LEFT;
0228     break;
0229     case QalculateSettings::LEGEND_BOTTOM_RIGHT:
0230     plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_RIGHT;
0231     break;
0232     case QalculateSettings::LEGEND_BELOW:
0233     plotParameters.legend_placement = PLOT_LEGEND_BELOW;
0234     break;
0235     case QalculateSettings::LEGEND_OUTSIDE:
0236     plotParameters.legend_placement = PLOT_LEGEND_OUTSIDE;
0237     break;
0238     }
0239     bool plotInline = QalculateSettings::inlinePlot();
0240     MathStructure xMin;
0241     MathStructure xMax;
0242     xMin.setUndefined();
0243     xMax.setUndefined();
0244     MathStructure stepLength;
0245     stepLength.setUndefined();
0246     int steps = QalculateSettings::plotSteps();
0247 
0248     QString mustBeNumber = i18n("%1 must be a number.");
0249     QString mustBeInteger = i18n("%1 must be a integer.");
0250     QString mustBeBoolean = i18n("%1 must be a boolean.");
0251     QString invalidOption = i18n("invalid option for %1: %2");
0252 
0253     for (int i = 0; i < argumentsList.size(); ++i) {
0254     std::string xVariable = "x";
0255     PlotDataParameters* plotDataParams = new PlotDataParameters;
0256     plotDataParameterList.push_back(plotDataParams);
0257     plotDataParams->title = "";
0258     switch(QalculateSettings::plotSmoothing()) {
0259     case QalculateSettings::SMOOTHING_NONE:
0260         plotDataParams->smoothing = PLOT_SMOOTHING_NONE;
0261         break;
0262     case QalculateSettings::SMOOTHING_UNIQUE:
0263         plotDataParams->smoothing = PLOT_SMOOTHING_UNIQUE;
0264         break;
0265     case QalculateSettings::SMOOTHING_CSPLINES:
0266         plotDataParams->smoothing = PLOT_SMOOTHING_CSPLINES;
0267         break;
0268     case QalculateSettings::SMOOTHING_BEZIER:
0269         plotDataParams->smoothing = PLOT_SMOOTHING_BEZIER;
0270         break;
0271     case QalculateSettings::SMOOTHING_SBEZIER:
0272         plotDataParams->smoothing = PLOT_SMOOTHING_SBEZIER;
0273         break;
0274     }
0275     switch(QalculateSettings::plotStyle()) {
0276     case QalculateSettings::STYLE_LINES:
0277         plotDataParams->style = PLOT_STYLE_LINES;
0278         break;
0279     case QalculateSettings::STYLE_POINTS:
0280         plotDataParams->style = PLOT_STYLE_POINTS;
0281         break;
0282     case QalculateSettings::STYLE_LINES_POINTS:
0283         plotDataParams->style = PLOT_STYLE_POINTS_LINES;
0284         break;
0285     case QalculateSettings::STYLE_BOXES:
0286         plotDataParams->style = PLOT_STYLE_BOXES;
0287         break;
0288     case QalculateSettings::STYLE_HISTOGRAM:
0289         plotDataParams->style = PLOT_STYLE_HISTOGRAM;
0290         break;
0291     case QalculateSettings::STYLE_STEPS:
0292         plotDataParams->style = PLOT_STYLE_STEPS;
0293         break;
0294     case QalculateSettings::STYLE_CANDLESTICKS:
0295         plotDataParams->style = PLOT_STYLE_CANDLESTICKS;
0296         break;
0297     case QalculateSettings::STYLE_DOTS:
0298         plotDataParams->style = PLOT_STYLE_DOTS;
0299         break;
0300     }
0301     plotDataParams->yaxis2 = false;
0302     plotDataParams->xaxis2 = false;
0303     arguments = argumentsList[i];
0304     std::string expression;
0305     int lastExpressionEntry = -1;
0306     for (int j = 0; j < arguments.size(); ++j) {
0307         QString argument = arguments[j];
0308         // PlotParameters
0309         if (argument.startsWith(QLatin1String("plottitle=")))
0310         plotParameters.title = argument.mid(10).toLatin1().data();
0311         else if (argument.startsWith(QLatin1String("ylabel=")))
0312         plotParameters.y_label = argument.mid(7).toLatin1().data();
0313         else if (argument.startsWith(QLatin1String("xlabel=")))
0314         plotParameters.x_label = argument.mid(7).toLatin1().data();
0315         else if (argument.startsWith(QLatin1String("filename=")))
0316         plotParameters.filename = argument.mid(9).toLatin1().data();
0317         else if (argument.startsWith(QLatin1String("filetype="))) {
0318         QString option = argument.mid(9);
0319         if (option == QLatin1String("auto"))
0320             plotParameters.filetype = PLOT_FILETYPE_AUTO;
0321         else if (option == QLatin1String("png"))
0322             plotParameters.filetype = PLOT_FILETYPE_PNG;
0323         else if (option == QLatin1String("ps"))
0324             plotParameters.filetype = PLOT_FILETYPE_PS;
0325         else if (option == QLatin1String("eps"))
0326             plotParameters.filetype = PLOT_FILETYPE_EPS;
0327         else if (option == QLatin1String("latex"))
0328             plotParameters.filetype = PLOT_FILETYPE_LATEX;
0329         else if (option == QLatin1String("svg"))
0330             plotParameters.filetype = PLOT_FILETYPE_SVG;
0331         else if (option == QLatin1String("fig"))
0332             plotParameters.filetype = PLOT_FILETYPE_FIG;
0333         else {
0334             QString msg = invalidOption.arg(QLatin1String("filetype"), option);
0335             showMessage(msg, MESSAGE_ERROR);
0336             deletePlotDataParameters(plotDataParameterList);
0337             return;
0338         }
0339         }
0340         else if (argument.startsWith(QLatin1String("font=")))
0341         plotParameters.font = argument.mid(5).toLatin1().data();
0342         else if (argument.startsWith(QLatin1String("color="))) {
0343         bool ok;
0344         plotParameters.color = stringToBool(argument.mid(6), &ok);
0345         if (!ok) {
0346             showMessage(mustBeBoolean.arg(QLatin1String("color")),
0347                 MESSAGE_ERROR);
0348             deletePlotDataParameters(plotDataParameterList);
0349             return;
0350         }
0351         }
0352         else if (argument.startsWith(QLatin1String("ylog="))) {
0353         bool ok;
0354         plotParameters.y_log = stringToBool(argument.mid(5), &ok);
0355         if (!ok) {
0356             showMessage(mustBeBoolean.arg(QLatin1String("ylog")), MESSAGE_ERROR);
0357             deletePlotDataParameters(plotDataParameterList);
0358             return;
0359         }
0360         }
0361         else if (argument.startsWith(QLatin1String("xlog="))) {
0362         bool ok;
0363         plotParameters.x_log = stringToBool(argument.mid(5), &ok);
0364         if (!ok) {
0365             showMessage(mustBeBoolean.arg(QLatin1String("xlog")), MESSAGE_ERROR);
0366             return;
0367         }
0368         }
0369         else if (argument.startsWith(QLatin1String("ylogbase="))) {
0370         MathStructure ylogStr = CALCULATOR->calculate(argument.mid(9).toLatin1().data(), eo);
0371         if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
0372             deletePlotDataParameters(plotDataParameterList);
0373             return;
0374         }
0375         if (ylogStr.isNumber()) {
0376             Number ylogNum = ylogStr.number();
0377             plotParameters.y_log_base = ylogNum.floatValue();
0378         } else {
0379             showMessage(mustBeNumber.arg(QLatin1String("ylogbase")), MESSAGE_ERROR);
0380             deletePlotDataParameters(plotDataParameterList);
0381             return;
0382         }
0383         }
0384         else if (argument.startsWith(QLatin1String("xlogbase="))) {
0385         MathStructure xlogStr = CALCULATOR->calculate(argument.mid(9).toLatin1().data(), eo);
0386         if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
0387             deletePlotDataParameters(plotDataParameterList);
0388             return;
0389         }
0390         if (xlogStr.isNumber()) {
0391             Number xlogNum = xlogStr.number();
0392             plotParameters.x_log_base = xlogNum.floatValue();
0393         } else {
0394             showMessage(mustBeNumber.arg(QLatin1String("xlogbase")), MESSAGE_ERROR);
0395             deletePlotDataParameters(plotDataParameterList);
0396             return;
0397         }
0398         }
0399         else if (argument.startsWith(QLatin1String("grid="))) {
0400         bool ok;
0401         plotParameters.grid = stringToBool(argument.mid(5), &ok);
0402         if (!ok) {
0403             showMessage(mustBeBoolean.arg(QLatin1String("grid")), MESSAGE_ERROR);
0404             deletePlotDataParameters(plotDataParameterList);
0405             return;
0406         }
0407         }
0408         else if (argument.startsWith(QLatin1String("linewidth="))) {
0409         MathStructure lineWidthStr = CALCULATOR->calculate(argument.mid(10).toLatin1().data(), eo);
0410         Number lineWidthNum;
0411         if (lineWidthStr.isNumber() && lineWidthStr.number().isInteger()) {
0412             lineWidthNum = lineWidthStr.number();
0413             plotParameters.linewidth = lineWidthNum.intValue();
0414         } else {
0415             showMessage(mustBeInteger.arg(QLatin1String("linewidth")), MESSAGE_ERROR);
0416             deletePlotDataParameters(plotDataParameterList);
0417             return;
0418         }
0419         }
0420         else if (argument.startsWith(QLatin1String("border="))) {
0421         bool ok;
0422         plotParameters.show_all_borders = stringToBool(argument.mid(7), &ok);
0423         if (!ok) {
0424             showMessage(mustBeBoolean.arg(QLatin1String("border")), MESSAGE_ERROR);
0425             deletePlotDataParameters(plotDataParameterList);
0426             return;
0427         }
0428         }
0429         else if (argument.startsWith(QLatin1String("legend="))) {
0430         QString option = argument.mid(7);
0431         if (option == QLatin1String("none"))
0432             plotParameters.legend_placement = PLOT_LEGEND_NONE;
0433         else if (option == QLatin1String("top_left"))
0434             plotParameters.legend_placement = PLOT_LEGEND_TOP_LEFT;
0435         else if (option == QLatin1String("top_right"))
0436             plotParameters.legend_placement = PLOT_LEGEND_TOP_RIGHT;
0437         else if (option == QLatin1String("bottom_left"))
0438             plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_LEFT;
0439         else if (option == QLatin1String("bottom_right"))
0440             plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_RIGHT;
0441         else if (option == QLatin1String("below"))
0442             plotParameters.legend_placement = PLOT_LEGEND_BELOW;
0443         else if (option == QLatin1String("outside"))
0444             plotParameters.legend_placement = PLOT_LEGEND_OUTSIDE;
0445         else {
0446             QString msg = invalidOption.arg(QLatin1String("legend"), option);
0447             showMessage(msg, MESSAGE_ERROR);
0448             deletePlotDataParameters(plotDataParameterList);
0449             return;
0450         }
0451         }
0452         // PlotDataParameters
0453         else if (argument.startsWith(QLatin1String("title="))) {
0454         plotDataParams->title = argument.mid(6).toLatin1().data();
0455         }
0456         else if (argument.startsWith(QLatin1String("smoothing="))) {
0457         QString option = argument.mid(10);
0458         if (option == QLatin1String("none"))
0459             plotDataParams->smoothing = PLOT_SMOOTHING_NONE;
0460         else if (option == QLatin1String("monotonic"))
0461             plotDataParams->smoothing = PLOT_SMOOTHING_UNIQUE;
0462         else if (option == QLatin1String("csplines"))
0463             plotDataParams->smoothing = PLOT_SMOOTHING_CSPLINES;
0464         else if (option == QLatin1String("bezier"))
0465             plotDataParams->smoothing = PLOT_SMOOTHING_BEZIER;
0466         else if (option == QLatin1String("sbezier"))
0467             plotDataParams->smoothing = PLOT_SMOOTHING_SBEZIER;
0468         else {
0469             QString msg = invalidOption.arg(QLatin1String("smoothing"), option);
0470             showMessage(msg, MESSAGE_ERROR);
0471             deletePlotDataParameters(plotDataParameterList);
0472             return;
0473         }
0474         } else if (argument.startsWith(QLatin1String("style="))) {
0475         QString option = argument.mid(6);
0476         if (option == QLatin1String("lines"))
0477             plotDataParams->style = PLOT_STYLE_LINES;
0478         else if (option == QLatin1String("points"))
0479             plotDataParams->style = PLOT_STYLE_POINTS;
0480         else if (option == QLatin1String("points_lines"))
0481             plotDataParams->style = PLOT_STYLE_POINTS_LINES;
0482         else if (option == QLatin1String("boxes"))
0483             plotDataParams->style = PLOT_STYLE_BOXES;
0484         else if (option == QLatin1String("histogram"))
0485             plotDataParams->style = PLOT_STYLE_HISTOGRAM;
0486         else if (option == QLatin1String("steps"))
0487             plotDataParams->style = PLOT_STYLE_STEPS;
0488         else if (option == QLatin1String("candlesticks"))
0489             plotDataParams->style = PLOT_STYLE_CANDLESTICKS;
0490         else if (option == QLatin1String("dots"))
0491             plotDataParams->style = PLOT_STYLE_DOTS;
0492         else {
0493             QString msg = invalidOption.arg(QLatin1String("style"), option);
0494             showMessage(msg, MESSAGE_ERROR);
0495             deletePlotDataParameters(plotDataParameterList);
0496             return;
0497         }
0498         } else if (argument.startsWith(QLatin1String("xaxis2="))) {
0499         bool ok;
0500         plotDataParams->xaxis2 = stringToBool(argument.mid(7), &ok);
0501         if (!ok) {
0502             showMessage(mustBeBoolean.arg(QLatin1String("xaxis2")), MESSAGE_ERROR);
0503             deletePlotDataParameters(plotDataParameterList);
0504             return;
0505         }
0506         } else if (argument.startsWith(QLatin1String("yaxis2="))) {
0507         bool ok;
0508         plotDataParams->yaxis2 = stringToBool(argument.mid(7), &ok);
0509         if (!ok) {
0510             showMessage(mustBeBoolean.arg(QLatin1String("yaxis2")), MESSAGE_ERROR);
0511             deletePlotDataParameters(plotDataParameterList);
0512             return;
0513         }
0514         }
0515         // inline, xmin, xmax, step, steps, xvar
0516         // Custom options
0517         else if (argument.startsWith(QLatin1String("inline="))) {
0518         bool ok;
0519         plotInline = stringToBool(argument.mid(7), &ok);
0520         if (!ok) {
0521             showMessage(mustBeBoolean.arg(QLatin1String("inline")), MESSAGE_ERROR);
0522             deletePlotDataParameters(plotDataParameterList);
0523             return;
0524         }
0525         }
0526         else if (argument.startsWith(QLatin1String("xmin="))) {
0527         xMin = CALCULATOR->calculate(argument.mid(5).toLatin1().data(), eo);
0528         if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
0529             deletePlotDataParameters(plotDataParameterList);
0530             return;
0531         }
0532         }
0533         else if (argument.startsWith(QLatin1String("xmax="))) {
0534         xMax = CALCULATOR->calculate(argument.mid(5).toLatin1().data(), eo);
0535         if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
0536             deletePlotDataParameters(plotDataParameterList);
0537             return;
0538         }
0539         }
0540         else if (argument.startsWith(QLatin1String("step="))) {
0541         stepLength = CALCULATOR->calculate(argument.mid(5).toLatin1().data(), eo);
0542         if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
0543             deletePlotDataParameters(plotDataParameterList);
0544             return;
0545         }
0546         steps = -1;
0547         }
0548         else if (argument.startsWith(QLatin1String("steps="))) {
0549         MathStructure stepsStr = CALCULATOR->calculate(argument.mid(6).toLatin1().data(), eo);
0550         if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
0551             deletePlotDataParameters(plotDataParameterList);
0552             return;
0553         }
0554         Number stepsNum;
0555         if (stepsStr.isNumber() && stepsStr.number().isInteger()) {
0556             stepsNum = stepsStr.number();
0557             steps = stepsNum.intValue();
0558             stepLength.setUndefined();
0559         } else {
0560             showMessage(mustBeInteger.arg(QLatin1String("steps")), MESSAGE_ERROR);
0561             deletePlotDataParameters(plotDataParameterList);
0562             return;
0563         }
0564         }
0565         else if (argument.startsWith(QLatin1String("xvar="))) {
0566         xVariable = argument.mid(5).toLatin1().data();
0567         }
0568         else if (expression.empty()) {
0569         expression = argument.toLatin1().data();
0570         lastExpressionEntry = j;
0571         }
0572         else if (lastExpressionEntry == j-1) {
0573         expression += " ";
0574         expression += argument.toLatin1().data();
0575         lastExpressionEntry = j;
0576         }
0577         else {
0578         QString msg = i18n("found multiple expressions in one plot command (%1 and %2).", QLatin1String(expression.c_str()), argument);
0579         showMessage(msg, MESSAGE_ERROR);
0580         deletePlotDataParameters(plotDataParameterList);
0581         return;
0582         }
0583     }
0584     if (expression.empty())
0585         continue;
0586     if (xMin.isUndefined()) {
0587         if (!plotParameters.auto_x_min)
0588         xMin = plotParameters.x_min;
0589         else
0590         xMin = 0.0;
0591     }
0592     if (xMax.isUndefined()) {
0593         if (!plotParameters.auto_x_max)
0594         xMax = plotParameters.x_max;
0595         else
0596         xMax = 10.0;
0597     }
0598     if (plotDataParams->title.empty())
0599         plotDataParams->title = expression;
0600     MathStructure x_vec, y_vec;
0601     x_vec.clearVector();
0602     if (!stepLength.isUndefined())
0603         y_vec = CALCULATOR->expressionToPlotVector(expression, xMin, xMax, stepLength, &x_vec, xVariable, eo.parse_options);
0604     else
0605         y_vec = CALCULATOR->expressionToPlotVector(expression, xMin, xMax, steps, &x_vec, xVariable, eo.parse_options);
0606     if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
0607         deletePlotDataParameters(plotDataParameterList);
0608         return;
0609     }
0610 
0611     x_vectors.push_back(x_vec);
0612     y_vectors.push_back(y_vec);
0613 
0614     //PrintOptions po;
0615     //y_vec.format(po);
0616 
0617     //setResult(new Cantor::TextResult(y_vec.print(po).c_str()));
0618     //setStatus(Done);
0619     //deletePlotDataParameters(plotDataParameterList);
0620     //return;
0621     }
0622 
0623     if (plotInline && plotParameters.filename.empty()) {
0624     // TODO: get a temporary file name here
0625     if (!m_tempFile) {
0626 #ifdef WITH_EPS
0627         m_tempFile=new QTemporaryFile(QDir::tempPath() + QLatin1String("/cantor_qalculate-XXXXXX.eps" ));
0628 #else
0629         m_tempFile=new QTemporaryFile(QDir::tempPath() + QLatin1String("/cantor_qalculate-XXXXXX.png"));
0630 #endif
0631         m_tempFile->open();
0632     }
0633     plotParameters.filename = m_tempFile->fileName().toLatin1().data();
0634     plotParameters.filetype = PLOT_FILETYPE_AUTO;
0635     }
0636 
0637     CALCULATOR->plotVectors(&plotParameters, y_vectors, x_vectors,
0638                 plotDataParameterList);
0639     if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) {
0640     deletePlotDataParameters(plotDataParameterList);
0641     return;
0642     }
0643 
0644     deletePlotDataParameters(plotDataParameterList);
0645 
0646     if (plotInline) {
0647 #ifdef WITH_EPS
0648     size_t p = plotParameters.filename.size();
0649     if (plotParameters.filetype == PLOT_FILETYPE_EPS ||
0650         plotParameters.filetype == PLOT_FILETYPE_PS  ||
0651         (plotParameters.filetype == PLOT_FILETYPE_AUTO && p >= 4 &&
0652          plotParameters.filename.substr(p-4,4) == ".eps") ||
0653         (plotParameters.filetype == PLOT_FILETYPE_AUTO && p >= 3 &&
0654          plotParameters.filename.substr(p-3,3) == ".ps"))
0655         setResult(new Cantor::EpsResult(QUrl(QString::fromStdString(plotParameters.filename))));
0656     else
0657         setResult(new Cantor::ImageResult(QUrl(QString::fromStdString(plotParameters.filename))));
0658 #else
0659     setResult(new Cantor::ImageResult(QUrl::fromLocalFile(QString::fromStdString(plotParameters.filename))));
0660 #endif
0661     setStatus(Cantor::Expression::Done);
0662     }
0663 }
0664 
0665 void QalculateExpression::showMessage(QString msg, MessageType mtype)
0666 {
0667     KColorScheme scheme(QApplication::palette().currentColorGroup());
0668     const QString errorColor = scheme.foreground(KColorScheme::NegativeText).color().name();
0669     const QString warningColor = scheme.foreground(KColorScheme::NeutralText).color().name();
0670     const QString msgFormat(QLatin1String("<font color=\"%1\">%2: %3</font><br>\n"));
0671     if(mtype == MESSAGE_ERROR || mtype == MESSAGE_WARNING) {
0672     msg.replace(QLatin1String("&"), QLatin1String("&amp;"));
0673     msg.replace(QLatin1String(">"), QLatin1String("&gt;"));
0674     msg.replace(QLatin1String("<"), QLatin1String("&lt;"));
0675 
0676     if (mtype == MESSAGE_ERROR) {
0677         msg = msgFormat.arg(errorColor, i18n("ERROR"), QLatin1String(msg.toLatin1().data()));
0678     } else {
0679         msg = msgFormat.arg(errorColor, i18n("WARNING"), QLatin1String(msg.toLatin1().data()));
0680     }
0681     setErrorMessage(msg);
0682     setStatus(Error);
0683     } else {
0684     KMessageBox::information(QApplication::activeWindow(), msg);
0685     }
0686 }
0687 
0688 EvaluationOptions QalculateExpression::evaluationOptions()
0689 {
0690     EvaluationOptions eo;
0691 
0692     eo.auto_post_conversion = QalculateSettings::postConversion() ? POST_CONVERSION_BEST : POST_CONVERSION_NONE;
0693     eo.keep_zero_units = false;
0694 
0695     eo.parse_options = parseOptions();
0696 
0697     switch (QalculateSettings::structuring()) {
0698         case 0:
0699             eo.structuring = STRUCTURING_NONE;
0700             break;
0701         case 1:
0702             eo.structuring = STRUCTURING_SIMPLIFY;
0703             break;
0704         case 2:
0705             eo.structuring = STRUCTURING_FACTORIZE;
0706             break;
0707     }
0708 
0709     return eo;
0710 }
0711 
0712 ParseOptions QalculateExpression::parseOptions()
0713 {
0714     ParseOptions po;
0715     switch (QalculateSettings::angleUnit()) {
0716         case 0:
0717             po.angle_unit = ANGLE_UNIT_NONE;
0718             break;
0719         case 1:
0720             po.angle_unit = ANGLE_UNIT_RADIANS;
0721             break;
0722         case 2:
0723             po.angle_unit = ANGLE_UNIT_DEGREES;
0724             break;
0725         case 3:
0726             po.angle_unit = ANGLE_UNIT_GRADIANS;
0727             break;
0728     }
0729 
0730     po.base = QalculateSettings::base();
0731     po.comma_as_separator = false;
0732 
0733     return po;
0734 }
0735 
0736 void QalculateExpression::deletePlotDataParameters
0737     (const std::vector<PlotDataParameters*>& plotDataParameterList)
0738 {
0739     for(size_t i = 0; i < plotDataParameterList.size(); ++i)
0740         delete plotDataParameterList[i];
0741 }
0742 
0743 bool QalculateExpression::stringToBool(const QString &string, bool *ok)
0744 {
0745     if (string == QLatin1String("true") || string == QLatin1String("1")) {
0746     *ok = true;
0747     return true;
0748     } else if (string == QLatin1String("false") || string == QLatin1String("0")) {
0749     *ok = true;
0750     return false;
0751     } else {
0752     *ok = false;
0753     return false;
0754     }
0755 }
0756 
0757 int QalculateExpression::checkForCalculatorMessages()
0758 {
0759     // error handling, most of it copied from qalculate-kde
0760     int msgType = MSG_NONE;
0761     if ( CALCULATOR->message() ) {
0762         QString msg;
0763         KColorScheme scheme(QApplication::palette().currentColorGroup());
0764         const QString errorColor = scheme.foreground(KColorScheme::NegativeText).color().name();
0765         const QString warningColor = scheme.foreground(KColorScheme::NeutralText).color().name();
0766         const QString msgFormat(QLatin1String("<font color=\"%1\">%2: %3</font><br>\n"));
0767         MessageType mtype;
0768         while(true) {
0769             mtype = CALCULATOR->message()->type();
0770         switch(mtype) {
0771         case MESSAGE_INFORMATION:
0772         msgType |= MSG_INFO; break;
0773         case MESSAGE_WARNING:
0774         msgType |= MSG_WARN; break;
0775         case MESSAGE_ERROR:
0776         msgType |= MSG_ERR;  break;
0777         }
0778             if(mtype == MESSAGE_ERROR || mtype == MESSAGE_WARNING) {
0779                 QString text = QLatin1String(CALCULATOR->message()->message().c_str());
0780                 text.replace(QLatin1String("&"), QLatin1String("&amp;"));
0781                 text.replace(QLatin1String(">"), QLatin1String("&gt;"));
0782                 text.replace(QLatin1String("<"), QLatin1String("&lt;"));
0783 
0784                 if (mtype == MESSAGE_ERROR) {
0785                     msg.append(msgFormat.arg(errorColor, i18n("ERROR"), text));
0786                 } else {
0787                     msg.append(msgFormat.arg(errorColor, i18n("WARNING"), text));
0788                 }
0789             } else {
0790                 KMessageBox::information(QApplication::activeWindow(), QLatin1String(CALCULATOR->message()->message().c_str()));
0791             }
0792             if(!CALCULATOR->nextMessage()) break;
0793         }
0794         if ( !msg.isEmpty() ) {
0795         m_message += msg;
0796             setErrorMessage(m_message);
0797             setStatus(Error);
0798         }
0799     }
0800     return msgType;
0801 }
0802 
0803 std::string QalculateExpression::unlocalizeExpression(QString expr)
0804 {
0805     // copy'n'pasted from qalculate plasma applet
0806 
0807     return CALCULATOR->unlocalizeExpression(
0808              expr.replace(QChar(0xA3), QLatin1String("GBP"))
0809                  .replace(QChar(0xA5), QLatin1String("JPY"))
0810                  .replace(QLatin1String("$"), QLatin1String("USD"))
0811                  .replace(QChar(0x20AC), QLatin1String("EUR"))
0812                  .toLatin1().data()
0813            );
0814 }
0815 
0816 QSharedPointer<PrintOptions> QalculateExpression::printOptions()
0817 {
0818     QSharedPointer<PrintOptions> po(new PrintOptions);
0819 
0820     switch (QalculateSettings::fractionFormat()) {
0821     case 0:
0822         po->number_fraction_format = FRACTION_DECIMAL;
0823         break;
0824     case 1:
0825         po->number_fraction_format = FRACTION_DECIMAL_EXACT;
0826         break;
0827     case 2:
0828         po->number_fraction_format = FRACTION_FRACTIONAL;
0829         break;
0830     case 3:
0831         po->number_fraction_format = FRACTION_COMBINED;
0832         break;
0833     }
0834     po->indicate_infinite_series = QalculateSettings::indicateInfiniteSeries();
0835     po->use_all_prefixes = QalculateSettings::useAllPrefixes();
0836     po->negative_exponents = QalculateSettings::negativeExponents();
0837 
0838     po->lower_case_e = true;
0839     po->base = QalculateSettings::base();
0840     po->decimalpoint_sign = QLocale().decimalPoint().toLatin1();
0841 
0842     switch (QalculateSettings::minExp()) {
0843     case 0:
0844         po->min_exp = EXP_NONE;
0845         break;
0846     case 1:
0847         po->min_exp = EXP_PURE;
0848         break;
0849     case 2:
0850         po->min_exp = EXP_SCIENTIFIC;
0851         break;
0852     case 3:
0853         po->min_exp = EXP_PRECISION;
0854         break;
0855     case 4:
0856         po->min_exp = EXP_BASE_3;
0857         break;
0858     }
0859     return po;
0860 }