diff --git a/src/backends/qalculate/qalculateexpression.cpp b/src/backends/qalculate/qalculateexpression.cpp index 5b8c5494..931d6c82 100644 --- a/src/backends/qalculate/qalculateexpression.cpp +++ b/src/backends/qalculate/qalculateexpression.cpp @@ -1,1010 +1,884 @@ /************************************************************************************* * Copyright (C) 2009 by Milian Wolff * * Copyright (C) 2011 by Matteo Agostinelli * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include #include "textresult.h" #include "helpresult.h" #include "imageresult.h" #include "epsresult.h" #include "settings.h" #include "defaultvariablemodel.h" #include "qalculateexpression.h" #include "qalculatesession.h" #include "qalculatesyntaxhelpobject.h" #include #include #include #include #include #include #include // required for the plotting interface of Qalculator #include #include #include #include #include #include #include #include #include QalculateExpression::QalculateExpression( QalculateSession* session ) : Cantor::Expression(session) { m_tempFile = 0; } QalculateExpression::~QalculateExpression() { if (m_tempFile) delete m_tempFile; } void QalculateExpression::evaluate() { + /* + Use Api for: + * help + * plot + Use qalc for any other command + */ setStatus(Cantor::Expression::Computing); - m_message = QLatin1String(""); - if (command().isEmpty()) { + setStatus(Cantor::Expression::Done); return; } - QStringList commands=command().split(QLatin1Char('\n')); - QString resultString; + QStringList commands = command().split(QLatin1Char('\n')); foreach(const QString& command, commands) { if (command.contains(QLatin1String("help"))) { QalculateSyntaxHelpObject* helper = new QalculateSyntaxHelpObject(command, (QalculateSession*) session()); setResult(new Cantor::HelpResult(helper->answer())); setStatus(Cantor::Expression::Done); return; } else if (command.trimmed().startsWith(QLatin1String("plot")) && (command.indexOf(QLatin1String("plot"))+4 == command.size() || command[command.indexOf(QLatin1String("plot"))+4].isSpace())) { evaluatePlotCommand(); return; } - else if (command.trimmed().startsWith(QLatin1String("saveVariables")) && - (command.indexOf(QLatin1String("saveVariables"))+13 == command.size() || - command[command.indexOf(QLatin1String("saveVariables"))+13].isSpace())) { - evaluateSaveVariablesCommand(); - return; - } - else if (command.trimmed().startsWith(QLatin1String("loadVariables")) && - (command.indexOf(QLatin1String("loadVariables"))+13 == command.size() || - command[command.indexOf(QLatin1String("loadVariables"))+13].isSpace())) { - evaluateLoadVariablesCommand(); - return; - } - - string expression = unlocalizeExpression(command); - - qDebug() << "EXPR: " << QLatin1String(expression.c_str()); - - EvaluationOptions eo = evaluationOptions(); - - MathStructure result = CALCULATOR->calculate(expression, eo); - - // update the answer variables - static_cast(session())->setLastResult(result); - - // error handling - if (checkForCalculatorMessages() & (MSG_WARN | MSG_WARN)) - return; - - updateVariables(CALCULATOR->parse(expression, eo.parse_options)); - - QSharedPointer po = printOptions(); - - result.format(*po); - - resultString+=QLatin1String(result.print(*po).c_str()) + QLatin1Char('\n'); - } + // we are here because the commands entered by user are regular commands. We would have returned by now otherwise + QalculateSession* currentSession = dynamic_cast(session()); + currentSession->runExpression(); - setResult(new Cantor::TextResult(resultString)); - setStatus(Done); } -void QalculateExpression::evaluateSaveVariablesCommand() +void QalculateExpression::parseOutput(QString& output) { - QString argString = command().mid(command().indexOf(QLatin1String("saveVariables"))+13); - argString = argString.trimmed(); - - QString usage = i18n("Usage: saveVariables file"); - - QString fileName = parseForFilename(argString, usage); - if (fileName.isNull()) - return; - - // We want to save Temporary variables, but Qalculate does not. - std::vector variables = CALCULATOR->variables; - // If somebody saves his variables in Cantor_Internal_Temporary - // he deserves unexpected behavior. - std::string tmpCategory = "Temporary"; - std::string newCategory = "Cantor_Internal_Temporary"; - for (size_t i = 0; i < variables.size(); ++i) { - if (variables[i]->category() == tmpCategory) - variables[i]->setCategory(newCategory); - } - - int res = CALCULATOR->saveVariables(fileName.toLatin1().data()); - - for (size_t i = 0; i < variables.size(); ++i) { - if (variables[i]->category() == newCategory) - variables[i]->setCategory(tmpCategory); - } + output.remove(QLatin1String(">")); + output = output.trimmed(); + + qDebug() << "output from qalc for command: " << command() << " " << output << endl; + setResult(new Cantor::TextResult(output)); + // update the variable model + updateVariables(); + setStatus(Cantor::Expression::Done); +} - if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { - return; - } - if (res < 0) { - showMessage(i18n("Saving failed."), MESSAGE_ERROR); - return; +void QalculateExpression::updateVariables() +{ + QalculateSession* currentSession = dynamic_cast(session()); + QMap &variables = currentSession->variables; + Cantor::DefaultVariableModel* model = static_cast(currentSession->variableModel()); + QMap::const_iterator it = variables.constBegin(); + while (it != variables.constEnd()) { + model->addVariable(it.key(), it.value()); + ++it; } - - setStatus(Done); } -void QalculateExpression::evaluateLoadVariablesCommand() +void QalculateExpression::parseError(QString& error) { - QString argString = command().mid(command().indexOf(QLatin1String("loadVariables"))+13); - argString = argString.trimmed(); - - QString usage = i18n("Usage: loadVariables file"); - - QString fileName = parseForFilename(argString, usage); - if (fileName.isNull()) - return; - - int res = CALCULATOR->loadDefinitions(fileName.toLatin1().data()); - if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { - return; - } - if (res < 0) { - showMessage(i18n("Loading failed."), MESSAGE_ERROR); - return; - } - - // We have to store temporary variables in a different category - // (see parseSaveVariablesCommand()) - std::vector variables = CALCULATOR->variables; - std::string tmpCategory = "Temporary"; - std::string newCategory = "Cantor_Internal_Temporary"; - - for (size_t i = 0; i < variables.size(); ++i) { - if (variables[i]->category() == newCategory) - variables[i]->setCategory(tmpCategory); - } - - setStatus(Done); + error.remove(QLatin1String(">")); + error = error.trimmed(); + qDebug() << "Error from qalc for command: " << command() << " " << error << endl; + setErrorMessage(error); + setStatus(Cantor::Expression::Error); } -QString QalculateExpression::parseForFilename(QString argument, QString usage) +void QalculateExpression::interrupt() { - if (argument.isEmpty()) { - showMessage(usage, MESSAGE_ERROR); - return QString(); - } - - QString fileName = QLatin1String(""); - QChar sep = QLatin1Char('\0'); - int i = 0; - if (argument[0] == QLatin1Char('\'') || argument[0] == QLatin1Char('"')) { - sep = argument[0]; - i = 1; - } - while (i < argument.size() && !argument[i].isSpace() && - argument[i] != sep) { - if (argument[i] == QLatin1Char('\\') && i < argument.size()-1) - ++i; - fileName += argument[i]; - ++i; - } - - if (sep != QLatin1Char('\0') && i == argument.size()) { - showMessage(i18n("missing %1", sep), MESSAGE_ERROR); - return QString(); - } - - if (i < argument.size() - 1) { - showMessage(usage, MESSAGE_ERROR); - return QString(); - } - - return fileName; + setStatus(Cantor::Expression::Interrupted); } void QalculateExpression::evaluatePlotCommand() { QString argString = command().mid(command().indexOf(QLatin1String("plot"))+4); argString = QLatin1String(unlocalizeExpression(argString).c_str()); argString = argString.trimmed(); QList argumentsList; QStringList arguments; // For error handling KColorScheme scheme(QApplication::palette().currentColorGroup()); const QString errorColor = scheme.foreground(KColorScheme::NegativeText).color().name(); const QString warningColor = scheme.foreground(KColorScheme::NeutralText).color().name(); const QString msgFormat(QLatin1String("%2: %3
\n")); if (!CALCULATOR->canPlot()) { showMessage(i18n("Qalculate reports it cannot print. Is gnuplot installed?"), MESSAGE_ERROR); return; } // Split argString into the arguments int i=0; int j=0; QString arg = QLatin1String(""); while (i < argString.size()) { if (argString[i] == QLatin1Char('"') || argString[i] == QLatin1Char('\'')) { ++j; while(j < argString.size() && argString[j] != argString[i]) { if (argString[j] == QLatin1Char('\\')) { ++j; if (j == argString.size()) continue; // just ignore trailing backslashes } arg += argString[j]; ++j; } if (j == argString.size()) { showMessage(i18n("missing %1", argString[i]), MESSAGE_ERROR); return; } ++j; } else if (argString[i] == QLatin1Char(',')) { argumentsList.append(arguments); arguments.clear(); ++j; } else { while(j < argString.size() && !argString[j].isSpace() && argString[j] != QLatin1Char('=') && argString[j] != QLatin1Char(',')) { if (argString[j] == QLatin1Char('\\')) { ++j; if (j == argString.size()) continue; // just ignore trailing backslashes } arg += argString[j]; ++j; } } if (argString[j] == QLatin1Char('=')) { // Parse things like title="..." as one argument arg += QLatin1Char('='); i = ++j; continue; } if (!arg.isEmpty()) { arguments << arg; arg = QLatin1String(""); } while (j < argString.size() && argString[j].isSpace()) ++j; i = j; } argumentsList.append(arguments); // Parse the arguments and compute the points to be plotted std::vector y_vectors; std::vector x_vectors; std::vector plotDataParameterList; PlotParameters plotParameters; EvaluationOptions eo = evaluationOptions(); /// temporary plotParameters.title = ""; plotParameters.y_label = ""; plotParameters.x_label = ""; plotParameters.filename = ""; plotParameters.filetype = PLOT_FILETYPE_AUTO; plotParameters.color = QalculateSettings::coloredPlot(); plotParameters.auto_y_min = true; plotParameters.auto_x_min = true; plotParameters.auto_x_max = true; plotParameters.auto_y_max = true; plotParameters.y_log = false; plotParameters.x_log = false; plotParameters.grid = QalculateSettings::plotGrid(); plotParameters.linewidth = QalculateSettings::plotLineWidth(); plotParameters.show_all_borders = QalculateSettings::plotBorder(); switch (QalculateSettings::plotLegend()) { case QalculateSettings::LEGEND_NONE: plotParameters.legend_placement = PLOT_LEGEND_NONE; break; case QalculateSettings::LEGEND_TOP_LEFT: plotParameters.legend_placement = PLOT_LEGEND_TOP_LEFT; break; case QalculateSettings::LEGEND_TOP_RIGHT: plotParameters.legend_placement = PLOT_LEGEND_TOP_RIGHT; break; case QalculateSettings::LEGEND_BOTTOM_LEFT: plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_LEFT; break; case QalculateSettings::LEGEND_BOTTOM_RIGHT: plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_RIGHT; break; case QalculateSettings::LEGEND_BELOW: plotParameters.legend_placement = PLOT_LEGEND_BELOW; break; case QalculateSettings::LEGEND_OUTSIDE: plotParameters.legend_placement = PLOT_LEGEND_OUTSIDE; break; } bool plotInline = QalculateSettings::inlinePlot(); MathStructure xMin; MathStructure xMax; xMin.setUndefined(); xMax.setUndefined(); MathStructure stepLength; stepLength.setUndefined(); int steps = QalculateSettings::plotSteps(); QString mustBeNumber = i18n("%1 must be a number."); QString mustBeInteger = i18n("%1 must be a integer."); QString mustBeBoolean = i18n("%1 must be a boolean."); QString invalidOption = i18n("invalid option for %1: %2"); for (int i = 0; i < argumentsList.size(); ++i) { std::string xVariable = "x"; PlotDataParameters* plotDataParams = new PlotDataParameters; plotDataParameterList.push_back(plotDataParams); plotDataParams->title = ""; switch(QalculateSettings::plotSmoothing()) { case QalculateSettings::SMOOTHING_NONE: plotDataParams->smoothing = PLOT_SMOOTHING_NONE; break; case QalculateSettings::SMOOTHING_UNIQUE: plotDataParams->smoothing = PLOT_SMOOTHING_UNIQUE; break; case QalculateSettings::SMOOTHING_CSPLINES: plotDataParams->smoothing = PLOT_SMOOTHING_CSPLINES; break; case QalculateSettings::SMOOTHING_BEZIER: plotDataParams->smoothing = PLOT_SMOOTHING_BEZIER; break; case QalculateSettings::SMOOTHING_SBEZIER: plotDataParams->smoothing = PLOT_SMOOTHING_SBEZIER; break; } switch(QalculateSettings::plotStyle()) { case QalculateSettings::STYLE_LINES: plotDataParams->style = PLOT_STYLE_LINES; break; case QalculateSettings::STYLE_POINTS: plotDataParams->style = PLOT_STYLE_POINTS; break; case QalculateSettings::STYLE_LINES_POINTS: plotDataParams->style = PLOT_STYLE_POINTS_LINES; break; case QalculateSettings::STYLE_BOXES: plotDataParams->style = PLOT_STYLE_BOXES; break; case QalculateSettings::STYLE_HISTOGRAM: plotDataParams->style = PLOT_STYLE_HISTOGRAM; break; case QalculateSettings::STYLE_STEPS: plotDataParams->style = PLOT_STYLE_STEPS; break; case QalculateSettings::STYLE_CANDLESTICKS: plotDataParams->style = PLOT_STYLE_CANDLESTICKS; break; case QalculateSettings::STYLE_DOTS: plotDataParams->style = PLOT_STYLE_DOTS; break; } plotDataParams->yaxis2 = false; plotDataParams->xaxis2 = false; arguments = argumentsList[i]; std::string expression; int lastExpressionEntry = -1; for (int j = 0; j < arguments.size(); ++j) { QString argument = arguments[j]; // PlotParameters if (argument.startsWith(QLatin1String("plottitle="))) plotParameters.title = argument.mid(10).toLatin1().data(); else if (argument.startsWith(QLatin1String("ylabel="))) plotParameters.y_label = argument.mid(7).toLatin1().data(); else if (argument.startsWith(QLatin1String("xlabel="))) plotParameters.x_label = argument.mid(7).toLatin1().data(); else if (argument.startsWith(QLatin1String("filename="))) plotParameters.filename = argument.mid(9).toLatin1().data(); else if (argument.startsWith(QLatin1String("filetype="))) { QString option = argument.mid(9); if (option == QLatin1String("auto")) plotParameters.filetype = PLOT_FILETYPE_AUTO; else if (option == QLatin1String("png")) plotParameters.filetype = PLOT_FILETYPE_PNG; else if (option == QLatin1String("ps")) plotParameters.filetype = PLOT_FILETYPE_PS; else if (option == QLatin1String("eps")) plotParameters.filetype = PLOT_FILETYPE_EPS; else if (option == QLatin1String("latex")) plotParameters.filetype = PLOT_FILETYPE_LATEX; else if (option == QLatin1String("svg")) plotParameters.filetype = PLOT_FILETYPE_SVG; else if (option == QLatin1String("fig")) plotParameters.filetype = PLOT_FILETYPE_FIG; else { QString msg = invalidOption.arg(QLatin1String("filetype"), option); showMessage(msg, MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("font="))) plotParameters.font = argument.mid(5).toLatin1().data(); else if (argument.startsWith(QLatin1String("color="))) { bool ok; plotParameters.color = stringToBool(argument.mid(6), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("color")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("ylog="))) { bool ok; plotParameters.y_log = stringToBool(argument.mid(5), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("ylog")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("xlog="))) { bool ok; plotParameters.x_log = stringToBool(argument.mid(5), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("xlog")), MESSAGE_ERROR); return; } } else if (argument.startsWith(QLatin1String("ylogbase="))) { MathStructure ylogStr = CALCULATOR->calculate(argument.mid(9).toLatin1().data(), eo); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } if (ylogStr.isNumber()) { Number ylogNum = ylogStr.number(); plotParameters.y_log_base = ylogNum.floatValue(); } else { showMessage(mustBeNumber.arg(QLatin1String("ylogbase")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("xlogbase="))) { MathStructure xlogStr = CALCULATOR->calculate(argument.mid(9).toLatin1().data(), eo); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } if (xlogStr.isNumber()) { Number xlogNum = xlogStr.number(); plotParameters.x_log_base = xlogNum.floatValue(); } else { showMessage(mustBeNumber.arg(QLatin1String("xlogbase")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("grid="))) { bool ok; plotParameters.grid = stringToBool(argument.mid(5), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("grid")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("linewidth="))) { MathStructure lineWidthStr = CALCULATOR->calculate(argument.mid(10).toLatin1().data(), eo); Number lineWidthNum; if (lineWidthStr.isNumber() && lineWidthStr.number().isInteger()) { lineWidthNum = lineWidthStr.number(); plotParameters.linewidth = lineWidthNum.intValue(); } else { showMessage(mustBeInteger.arg(QLatin1String("linewidth")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("border="))) { bool ok; plotParameters.show_all_borders = stringToBool(argument.mid(7), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("border")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("legend="))) { QString option = argument.mid(7); if (option == QLatin1String("none")) plotParameters.legend_placement = PLOT_LEGEND_NONE; else if (option == QLatin1String("top_left")) plotParameters.legend_placement = PLOT_LEGEND_TOP_LEFT; else if (option == QLatin1String("top_right")) plotParameters.legend_placement = PLOT_LEGEND_TOP_RIGHT; else if (option == QLatin1String("bottom_left")) plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_LEFT; else if (option == QLatin1String("bottom_right")) plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_RIGHT; else if (option == QLatin1String("below")) plotParameters.legend_placement = PLOT_LEGEND_BELOW; else if (option == QLatin1String("outside")) plotParameters.legend_placement = PLOT_LEGEND_OUTSIDE; else { QString msg = invalidOption.arg(QLatin1String("legend"), option); showMessage(msg, MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } // PlotDataParameters else if (argument.startsWith(QLatin1String("title="))) { plotDataParams->title = argument.mid(6).toLatin1().data(); } else if (argument.startsWith(QLatin1String("smoothing="))) { QString option = argument.mid(10); if (option == QLatin1String("none")) plotDataParams->smoothing = PLOT_SMOOTHING_NONE; else if (option == QLatin1String("monotonic")) plotDataParams->smoothing = PLOT_SMOOTHING_UNIQUE; else if (option == QLatin1String("csplines")) plotDataParams->smoothing = PLOT_SMOOTHING_CSPLINES; else if (option == QLatin1String("bezier")) plotDataParams->smoothing = PLOT_SMOOTHING_BEZIER; else if (option == QLatin1String("sbezier")) plotDataParams->smoothing = PLOT_SMOOTHING_SBEZIER; else { QString msg = invalidOption.arg(QLatin1String("smoothing"), option); showMessage(msg, MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("style="))) { QString option = argument.mid(6); if (option == QLatin1String("lines")) plotDataParams->style = PLOT_STYLE_LINES; else if (option == QLatin1String("points")) plotDataParams->style = PLOT_STYLE_POINTS; else if (option == QLatin1String("points_lines")) plotDataParams->style = PLOT_STYLE_POINTS_LINES; else if (option == QLatin1String("boxes")) plotDataParams->style = PLOT_STYLE_BOXES; else if (option == QLatin1String("histogram")) plotDataParams->style = PLOT_STYLE_HISTOGRAM; else if (option == QLatin1String("steps")) plotDataParams->style = PLOT_STYLE_STEPS; else if (option == QLatin1String("candlesticks")) plotDataParams->style = PLOT_STYLE_CANDLESTICKS; else if (option == QLatin1String("dots")) plotDataParams->style = PLOT_STYLE_DOTS; else { QString msg = invalidOption.arg(QLatin1String("style"), option); showMessage(msg, MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("xaxis2="))) { bool ok; plotDataParams->xaxis2 = stringToBool(argument.mid(7), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("xaxis2")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("yaxis2="))) { bool ok; plotDataParams->yaxis2 = stringToBool(argument.mid(7), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("yaxis2")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } // inline, xmin, xmax, step, steps, xvar // Custom options else if (argument.startsWith(QLatin1String("inline="))) { bool ok; plotInline = stringToBool(argument.mid(7), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("inline")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("xmin="))) { xMin = CALCULATOR->calculate(argument.mid(5).toLatin1().data(), eo); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("xmax="))) { xMax = CALCULATOR->calculate(argument.mid(5).toLatin1().data(), eo); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("step="))) { stepLength = CALCULATOR->calculate(argument.mid(5).toLatin1().data(), eo); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } steps = -1; } else if (argument.startsWith(QLatin1String("steps="))) { MathStructure stepsStr = CALCULATOR->calculate(argument.mid(6).toLatin1().data(), eo); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } Number stepsNum; if (stepsStr.isNumber() && stepsStr.number().isInteger()) { stepsNum = stepsStr.number(); steps = stepsNum.intValue(); stepLength.setUndefined(); } else { showMessage(mustBeInteger.arg(QLatin1String("steps")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("xvar="))) { xVariable = argument.mid(5).toLatin1().data(); } else if (expression.empty()) { expression = argument.toLatin1().data(); lastExpressionEntry = j; } else if (lastExpressionEntry == j-1) { expression += " "; expression += argument.toLatin1().data(); lastExpressionEntry = j; } else { QString msg = i18n("found multiple expressions in one plot command (%1 and %2).", QLatin1String(expression.c_str()), argument); showMessage(msg, MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } if (expression.empty()) continue; if (xMin.isUndefined()) { if (!plotParameters.auto_x_min) xMin = plotParameters.x_min; else xMin = 0.0; } if (xMax.isUndefined()) { if (!plotParameters.auto_x_max) xMax = plotParameters.x_max; else xMax = 10.0; } if (plotDataParams->title.empty()) plotDataParams->title = expression; MathStructure x_vec, y_vec; x_vec.clearVector(); if (!stepLength.isUndefined()) y_vec = CALCULATOR->expressionToPlotVector(expression, xMin, xMax, stepLength, &x_vec, xVariable, eo.parse_options); else y_vec = CALCULATOR->expressionToPlotVector(expression, xMin, xMax, steps, &x_vec, xVariable, eo.parse_options); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } x_vectors.push_back(x_vec); y_vectors.push_back(y_vec); //PrintOptions po; //y_vec.format(po); //setResult(new Cantor::TextResult(y_vec.print(po).c_str())); //setStatus(Done); //deletePlotDataParameters(plotDataParameterList); //return; } if (plotInline && plotParameters.filename.empty()) { // TODO: get a temporary file name here if (!m_tempFile) { #ifdef WITH_EPS m_tempFile=new QTemporaryFile(QDir::tempPath() + QLatin1String("/cantor_qalculate-XXXXXX.eps" )); #else m_tempFile=new QTemporaryFile(QDir::tempPath() + QLatin1String("/cantor_qalculate-XXXXXX.png")); #endif m_tempFile->open(); } plotParameters.filename = m_tempFile->fileName().toLatin1().data(); plotParameters.filetype = PLOT_FILETYPE_AUTO; } CALCULATOR->plotVectors(&plotParameters, y_vectors, x_vectors, plotDataParameterList); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } deletePlotDataParameters(plotDataParameterList); if (plotInline) { #ifdef WITH_EPS size_t p = plotParameters.filename.size(); if (plotParameters.filetype == PLOT_FILETYPE_EPS || plotParameters.filetype == PLOT_FILETYPE_PS || (plotParameters.filetype == PLOT_FILETYPE_AUTO && p >= 4 && plotParameters.filename.substr(p-4,4) == ".eps") || (plotParameters.filetype == PLOT_FILETYPE_AUTO && p >= 3 && plotParameters.filename.substr(p-3,3) == ".ps")) setResult(new Cantor::EpsResult(QUrl(QString::fromStdString(plotParameters.filename)))); else setResult(new Cantor::ImageResult(QUrl(QString::fromStdString(plotParameters.filename)))); #else setResult(new Cantor::ImageResult(QUrl::fromLocalFile(QString::fromStdString(plotParameters.filename)))); #endif setStatus(Cantor::Expression::Done); } } void QalculateExpression::showMessage(QString msg, MessageType mtype) { KColorScheme scheme(QApplication::palette().currentColorGroup()); const QString errorColor = scheme.foreground(KColorScheme::NegativeText).color().name(); const QString warningColor = scheme.foreground(KColorScheme::NeutralText).color().name(); const QString msgFormat(QLatin1String("%2: %3
\n")); if(mtype == MESSAGE_ERROR || mtype == MESSAGE_WARNING) { msg.replace(QLatin1String("&"), QLatin1String("&")); msg.replace(QLatin1String(">"), QLatin1String(">")); msg.replace(QLatin1String("<"), QLatin1String("<")); if (mtype == MESSAGE_ERROR) { msg = msgFormat.arg(errorColor, i18n("ERROR"), QLatin1String(msg.toLatin1().data())); } else { msg = msgFormat.arg(errorColor, i18n("WARNING"), QLatin1String(msg.toLatin1().data())); } setErrorMessage(msg); setStatus(Error); } else { KMessageBox::information(QApplication::activeWindow(), msg); } } EvaluationOptions QalculateExpression::evaluationOptions() { EvaluationOptions eo; eo.auto_post_conversion = QalculateSettings::postConversion() ? POST_CONVERSION_BEST : POST_CONVERSION_NONE; eo.keep_zero_units = false; eo.parse_options = parseOptions(); switch (QalculateSettings::structuring()) { case 0: eo.structuring = STRUCTURING_NONE; break; case 1: eo.structuring = STRUCTURING_SIMPLIFY; break; case 2: eo.structuring = STRUCTURING_FACTORIZE; break; } return eo; } ParseOptions QalculateExpression::parseOptions() { ParseOptions po; switch (QalculateSettings::angleUnit()) { case 0: po.angle_unit = ANGLE_UNIT_NONE; break; case 1: po.angle_unit = ANGLE_UNIT_RADIANS; break; case 2: po.angle_unit = ANGLE_UNIT_DEGREES; break; case 3: po.angle_unit = ANGLE_UNIT_GRADIANS; break; } po.base = QalculateSettings::base(); return po; } void QalculateExpression::deletePlotDataParameters (const std::vector& plotDataParameterList) { for(size_t i = 0; i < plotDataParameterList.size(); ++i) delete plotDataParameterList[i]; } bool QalculateExpression::stringToBool(const QString &string, bool *ok) { if (string == QLatin1String("true") || string == QLatin1String("1")) { *ok = true; return true; } else if (string == QLatin1String("false") || string == QLatin1String("0")) { *ok = true; return false; } else { *ok = false; return false; } } int QalculateExpression::checkForCalculatorMessages() { // error handling, most of it copied from qalculate-kde int msgType = MSG_NONE; if ( CALCULATOR->message() ) { QString msg; KColorScheme scheme(QApplication::palette().currentColorGroup()); const QString errorColor = scheme.foreground(KColorScheme::NegativeText).color().name(); const QString warningColor = scheme.foreground(KColorScheme::NeutralText).color().name(); const QString msgFormat(QLatin1String("%2: %3
\n")); MessageType mtype; while(true) { mtype = CALCULATOR->message()->type(); switch(mtype) { case MESSAGE_INFORMATION: msgType |= MSG_INFO; break; case MESSAGE_WARNING: msgType |= MSG_WARN; break; case MESSAGE_ERROR: msgType |= MSG_ERR; break; } if(mtype == MESSAGE_ERROR || mtype == MESSAGE_WARNING) { QString text = QLatin1String(CALCULATOR->message()->message().c_str()); text.replace(QLatin1String("&"), QLatin1String("&")); text.replace(QLatin1String(">"), QLatin1String(">")); text.replace(QLatin1String("<"), QLatin1String("<")); if (mtype == MESSAGE_ERROR) { msg.append(msgFormat.arg(errorColor, i18n("ERROR"), text)); } else { msg.append(msgFormat.arg(errorColor, i18n("WARNING"), text)); } } else { KMessageBox::information(QApplication::activeWindow(), QLatin1String(CALCULATOR->message()->message().c_str())); } if(!CALCULATOR->nextMessage()) break; } if ( !msg.isEmpty() ) { m_message += msg; setErrorMessage(m_message); setStatus(Error); } } return msgType; } std::string QalculateExpression::unlocalizeExpression(QString expr) { // copy'n'pasted from qalculate plasma applet return CALCULATOR->unlocalizeExpression( expr.replace(QChar(0xA3), QLatin1String("GBP")) .replace(QChar(0xA5), QLatin1String("JPY")) .replace(QLatin1String("$"), QLatin1String("USD")) .replace(QChar(0x20AC), QLatin1String("EUR")) .toLatin1().data() ); } -void QalculateExpression::updateVariables(MathStructure command) -{ - Cantor::DefaultVariableModel* model = - static_cast(session()->variableModel()); - QStack stack; - stack.push(&command); - QSharedPointer po = printOptions(); - while (!stack.isEmpty()) { - MathStructure* current = stack.pop(); - if (current->isFunction() && current->function()->name() == "save" && - current->countChildren() >= 2 && current->getChild(2)->isSymbolic()) - { - // I'd like to use CALCULATOR->getVariable and KnownVariable::get, - // but that doesn't work for built in variables, as it keeps - // returning the old value - std::string name = current->getChild(2)->symbol(); - MathStructure m = CALCULATOR->calculate(name, evaluationOptions()); - m.format(*po); - model->addVariable(QLatin1String(name.c_str()), QLatin1String(m.print(*po).c_str())); - } - for (size_t i = 0; i < current->countChildren(); ++i) { - stack.push(current->getChild(i+1)); - } - } -} + QSharedPointer QalculateExpression::printOptions() { QSharedPointer po(new PrintOptions); switch (QalculateSettings::fractionFormat()) { case 0: po->number_fraction_format = FRACTION_DECIMAL; break; case 1: po->number_fraction_format = FRACTION_DECIMAL_EXACT; break; case 2: po->number_fraction_format = FRACTION_FRACTIONAL; break; case 3: po->number_fraction_format = FRACTION_COMBINED; break; } po->indicate_infinite_series = QalculateSettings::indicateInfiniteSeries(); po->use_all_prefixes = QalculateSettings::useAllPrefixes(); po->negative_exponents = QalculateSettings::negativeExponents(); po->lower_case_e = true; po->base = QalculateSettings::base(); po->decimalpoint_sign = QLocale().decimalPoint().toLatin1(); switch (QalculateSettings::minExp()) { case 0: po->min_exp = EXP_NONE; break; case 1: po->min_exp = EXP_PURE; break; case 2: po->min_exp = EXP_SCIENTIFIC; break; case 3: po->min_exp = EXP_PRECISION; break; case 4: po->min_exp = EXP_BASE_3; break; } return po; } diff --git a/src/backends/qalculate/qalculateexpression.h b/src/backends/qalculate/qalculateexpression.h index 2c13eb24..60196f8a 100644 --- a/src/backends/qalculate/qalculateexpression.h +++ b/src/backends/qalculate/qalculateexpression.h @@ -1,65 +1,64 @@ /************************************************************************************* * Copyright (C) 2009 by Milian Wolff * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #ifndef QALCULATE_EXPRESSION_H #define QALCULATE_EXPRESSION_H #include "expression.h" #include #include #include #include class QalculateSession; class QTemporaryFile; class QalculateExpression : public Cantor::Expression { Q_OBJECT private: QTemporaryFile *m_tempFile; QString m_message; - enum MsgType { MSG_NONE=0, MSG_INFO=1, MSG_WARN=2, MSG_ERR=4 }; - + enum MsgType { MSG_NONE=0, MSG_INFO=1, MSG_WARN=2, MSG_ERR=4 }; + void evaluatePlotCommand(); - void evaluateLoadVariablesCommand(); - void evaluateSaveVariablesCommand(); - QString parseForFilename(QString argument, QString usage); bool stringToBool(const QString&, bool*); void deletePlotDataParameters(const std::vector&); void showMessage(QString msg, MessageType mtype); int checkForCalculatorMessages(); - void updateVariables(MathStructure); + void updateVariables(); QSharedPointer printOptions(); EvaluationOptions evaluationOptions(); ParseOptions parseOptions(); std::string unlocalizeExpression(QString expr); public: QalculateExpression( QalculateSession* session); ~QalculateExpression(); void evaluate(); - void interrupt() {} + void interrupt(); + void parseOutput(QString& output); + void parseError(QString& error); }; #endif diff --git a/src/backends/qalculate/qalculatesession.cpp b/src/backends/qalculate/qalculatesession.cpp index 1746aaa8..64a7532e 100644 --- a/src/backends/qalculate/qalculatesession.cpp +++ b/src/backends/qalculate/qalculatesession.cpp @@ -1,127 +1,426 @@ /************************************************************************************ * Copyright (C) 2009 by Milian Wolff * * Copyright (C) 2011 by Matteo Agostinelli * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "settings.h" #include "qalculatesession.h" -#include "qalculateexpression.h" #include "qalculatecompletionobject.h" #include "qalculatehighlighter.h" #include "defaultvariablemodel.h" #include +#include +#include +#include #include #include #include #include #include #include #include "qalculatesyntaxhelpobject.h" QalculateSession::QalculateSession( Cantor::Backend* backend) : Session(backend), - m_variableModel(new Cantor::DefaultVariableModel(this)) + m_variableModel(new Cantor::DefaultVariableModel(this)), + m_process(0), + m_currentExpression(0), + m_isSaveCommand(false) { + /* + qalc does all of this by default but we still need the CALCULATOR instance for plotting + graphs + */ + if ( !CALCULATOR ) { new Calculator(); CALCULATOR->loadGlobalDefinitions(); CALCULATOR->loadLocalDefinitions(); CALCULATOR->loadExchangeRates(); } - // from qalc.cc in libqalculate - std::string ansName = "ans"; - // m_undefined is not a variable in this class, but is defined in - // libqalculate/includes.h - m_ansVariables.append(static_cast(CALCULATOR->addVariable(new KnownVariable("Temporary", ansName, m_undefined, "Last Answer", false)))); - m_ansVariables[0]->addName("answer"); - m_ansVariables[0]->addName(ansName + "1"); - m_ansVariables.append(static_cast(CALCULATOR->addVariable(new KnownVariable("Temporary", ansName+"2", m_undefined, "Answer 2", false)))); - m_ansVariables.append(static_cast(CALCULATOR->addVariable(new KnownVariable("Temporary", ansName+"3", m_undefined, "Answer 3", false)))); - m_ansVariables.append(static_cast(CALCULATOR->addVariable(new KnownVariable("Temporary", ansName+"4", m_undefined, "Answer 4", false)))); - m_ansVariables.append(static_cast(CALCULATOR->addVariable(new KnownVariable("Temporary", ansName+"5", m_undefined, "Answer 5", false)))); } QalculateSession::~QalculateSession() { CALCULATOR->abort(); + if(m_process) + m_process->kill(); } void QalculateSession::login() { - if(!QalculateSettings::autorunScripts().isEmpty()){ - QString autorunScripts = QalculateSettings::self()->autorunScripts().join(QLatin1String("\n")); - evaluateExpression(autorunScripts, QalculateExpression::DeleteOnFinish); + /* we will , most probably, use autoscripts for setting the mode , evaulate options, print options etc */ + + // if(!QalculateSettings::autorunScripts().isEmpty()){ + // QString autorunScripts = QalculateSettings::self()->autorunScripts().join(QLatin1String("\n")); + // + // evaluateExpression(autorunScripts, QalculateExpression::DeleteOnFinish); + // } + + /* + set up the process here. The program path , arguments(if any),channel modes , and connections should all be set up here. + once the setup is complete, start the process and inform the worksheet that we are ready + */ + m_process = new QProcess(this); + + m_process->setProgram(QStandardPaths::findExecutable(QLatin1String("qalc"))); + m_process->setProcessChannelMode(QProcess::SeparateChannels); + + connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutput())); + connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(readError())); + connect(m_process, SIGNAL(started()), this, SLOT(processStarted())); + + m_process->start(); +} + +void QalculateSession::readOutput() +{ + while(m_process->bytesAvailable()) { + m_output.append(QString::fromLocal8Bit(m_process->readLine())); + qDebug() << m_output << endl; + } + + if(m_currentExpression && !m_output.isEmpty() && m_output.trimmed().endsWith(QLatin1String(">"))) { + + // check if the commandQueue is empty or not . if it's not empty run the "runCommandQueue" function. + // store the output in finalOutput and clear m_output + + if(m_currentCommand.trimmed().isEmpty()) + m_output.clear(); + + if(!m_output.toLower().contains(QLatin1String("error")) && m_isSaveCommand) { + storeVariables(m_currentCommand, m_output); + m_isSaveCommand = false; + } + + m_output = m_output.trimmed(); + m_output.remove(m_currentCommand); + if (!m_output.isEmpty()) + m_finalOutput.append(m_output); + + // we tried to perform a save operation but failed(see parseSaveCommand()).In such a case + // m_output will be empty but m_saveError will contain the error message. + if(!m_saveError.isEmpty()) { + m_finalOutput.append(m_saveError); + m_saveError.clear(); + } + + m_finalOutput.append(QLatin1String("\n")); + m_output.clear(); + + + + if (!m_commandQueue.isEmpty()) + runCommandQueue(); + else { + qDebug () << "parsing output: " << m_finalOutput << endl; + m_currentExpression->parseOutput(m_finalOutput); + m_finalOutput.clear(); + } + + + } +} + +void QalculateSession::storeVariables(QString& currentCmd, QString output) +{ + + // internally we pass save(value,variable) command to qlac to save the variables. see parseSaveCommand() + // TODO: if the user if trying to override a default variable(constansts etc) or an existing variable, ask the user if he/she wants to override it or not. + + qDebug() << "save command " << currentCmd << endl; + + /** + if we have reached here, we expect our variable model to be updated with new variables. + In case the variable model is not updated, it most probably because we were not able to successfully parse the + current command and ouput to extract variable and value + + This is probably not the best way to get the variable and value. + But since qalc does not provide a way to get the list of variables, we will have to stick to parsing + **/ + QString value; + QString var; + QRegExp regex; + // find the value + regex.setPattern(QLatin1String("[\\s\\w\\W]+=\\s*([\\w\\W]+)")); + if(regex.exactMatch(output)) { + int pos = regex.indexIn(output); + if (pos > -1) { + value = regex.cap(1); + value = value.trimmed(); + value.replace(QLatin1String("\n"), QLatin1String("")); + value.remove(QLatin1String(">")); + } } - changeStatus(Cantor::Session::Done); + //find the varaiable. + // ex1: currentCmd = save(10, var_1,category, title): var_1 = variable + // ex2: currentCmd = save(planet(jupter,mass), jupiter_mass, category, title): jupiter_mass = variable + // Not the best regex. Cab be improved + regex.setPattern(QLatin1String("\\s*save\\s*\\(\\s*[\\s\\w]+\\s*,([\\s\\w]+),*[\\w\\W]*\\)\\s*;*$|\\s*save\\s*\\(\\s*[\\s\\w\\W]+\\)\\s*,([\\s\\w]+),*[\\w\\W]*\\)\\s*;*$")); + if(regex.exactMatch(currentCmd)) { + int pos = regex.indexIn(currentCmd); + if (pos > -1) { + if(!regex.cap(1).trimmed().isEmpty()) + var = regex.cap(1).trimmed(); + else + var = regex.cap(2).trimmed(); + + var = var.trimmed(); + var.replace(QLatin1String("\n"), QLatin1String("")); + var.remove(QLatin1String(">")); + } + } + if(!value.isEmpty() && !var.isEmpty()) + variables.insert(var, value); + +} + +void QalculateSession::readError() +{ + + QString error = QLatin1String(m_process->readAllStandardError()); + if(m_currentExpression) { + m_currentExpression->parseError(error); + } +} + +void QalculateSession::processStarted() +{ + qDebug() << "process started " << m_process->program() << m_process->processId() << endl; emit ready(); } void QalculateSession::logout() { + qDebug () << "logging out " << endl; + if(m_process) { + m_process->write("quit\n"); + if(!m_process->waitForFinished(1000)) + m_process->kill(); + } } void QalculateSession::interrupt() { - changeStatus(Cantor::Session::Done); + qDebug () << "interrupting .... " << endl; + if(m_currentExpression) + m_currentExpression->interrupt(); + + m_commandQueue.clear(); + m_expressionQueue.clear(); + m_output.clear(); + m_finalOutput.clear(); + m_currentCommand.clear(); + m_currentExpression = 0; + +} + +void QalculateSession::runExpression() +{ + + const QString& command = m_currentExpression->command(); + foreach(const QString& cmd, command.split(QLatin1Char('\n'))) { + m_commandQueue.enqueue(cmd); + } + runCommandQueue(); + +} + + +void QalculateSession::runCommandQueue() +{ + if (!m_commandQueue.isEmpty()) { + m_currentCommand = m_commandQueue.dequeue(); + // parse the current command if it's a save/load/store command + if( m_currentCommand.toLower().trimmed().startsWith(QLatin1String("save")) || + m_currentCommand.toLower().trimmed().startsWith(QLatin1String("store")) || + m_currentCommand.trimmed().startsWith(QLatin1String("saveVariables"))) { + + m_currentCommand = parseSaveCommand(m_currentCommand); + } + + + m_currentCommand = m_currentCommand.trimmed(); + m_currentCommand += QLatin1String("\n"); + m_process->write(m_currentCommand.toLocal8Bit()); + + } +} + +QString QalculateSession::parseSaveCommand(QString& currentCmd) +{ + /* + make sure the command is: + * fomatted correctly. e.g if the command is save(value,variable), we have to make sure that there is no space between save and '(', otherwise qalc + waits for user input which is not supported by us as of now + * supported save commands: save(value,variable,[category],[title]), save definitions, save mode, save var, store var, + saveVariables filename + */ + + QRegExp regex; + regex.setCaseSensitivity(Qt::CaseInsensitive); + + regex.setPattern(QLatin1String("\\s*save\\s*definitions\\s*")); + if(regex.exactMatch(currentCmd)) { + // save the variables in ~/.cantor/backends/qalculate/definitions + currentCmd.clear(); + return currentCmd; + } + + regex.setPattern(QLatin1String("\\s*save\\s*mode\\s*")); + if(regex.exactMatch(currentCmd)) { + // save the mode in ~/.cantor/backends/qalculate/cantor_qalc.cfg + currentCmd.clear(); + return currentCmd; + } + + regex.setPattern(QLatin1String("\\s*saveVariables\\s*[\\w\\W]+")); + if(regex.exactMatch(currentCmd)) { + // save the variables in a file + currentCmd.clear(); + return currentCmd; + } + + + regex.setPattern(QLatin1String("\\s*store\\s*([a-zA-Z_]+[\\w]*)|\\s*save\\s*([a-zA-Z_]+[\\w]*)")); + if(regex.exactMatch(currentCmd)) { + m_isSaveCommand = true; + int pos = regex.indexIn(currentCmd); + if(pos > -1) { + if(!regex.cap(1).trimmed().isEmpty()) + currentCmd = QStringLiteral("save(%1, %2)").arg(QStringLiteral("ans")).arg(regex.cap(1).trimmed()); + else + currentCmd = QStringLiteral("save(%1, %2)").arg(QStringLiteral("ans")).arg(regex.cap(2).trimmed()); + + return currentCmd; + } + } + + regex.setPattern(QLatin1String("\\s*save\\s*(\\([\\w\\W]+\\))\\s*;*$")); + if(regex.exactMatch(currentCmd)) { + m_isSaveCommand = true; + int pos = regex.indexIn(currentCmd); + if (pos > -1) { + currentCmd = QStringLiteral("save%1").arg(regex.cap(1).trimmed()); + return currentCmd; + } + } + + /* + If we have not returned by this point, it's because: + * we did not parse the save command properly. This might be due to malformed regular expressions. + * or the commnad given by the user is malformed. More likely to happen + In both these cases we will simply return an empty string because we don't want qalc to run malformed queries, + else it would wait for user input and hence Qprocess would never return a complete output and the expression will remain in + 'calculating' state + */ + m_saveError = currentCmd + QLatin1String("\nError: Could not save.\n"); + return QLatin1String(""); + +} + +void QalculateSession::currentExpressionStatusChanged(Cantor::Expression::Status status) +{ + // depending on the status of the expression change the status of the session; + switch (status) { + + case Cantor::Expression::Computing: + break; + case Cantor::Expression::Interrupted: + changeStatus(Cantor::Session::Done); + break; + case Cantor::Expression::Done: + case Cantor::Expression::Error: + qDebug() << " ****** STATUS " << status; + changeStatus(Cantor::Session::Done); + if(m_expressionQueue.size() > 0) + m_expressionQueue.dequeue(); + if(!m_expressionQueue.isEmpty()) + runExpressionQueue(); + } } Cantor::Expression* QalculateSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave) { - QalculateExpression* expr = new QalculateExpression(this); - expr->setFinishingBehavior(behave); + + qDebug() << " ** evaluating expression: " << cmd << endl; + qDebug() << " size of expression queue: " << m_expressionQueue.size() << endl; changeStatus(Cantor::Session::Running); + + QalculateExpression* expr = new QalculateExpression(this); + expr->setFinishingBehavior(behave); expr->setCommand(cmd); - expr->evaluate(); - changeStatus(Cantor::Session::Done); + + m_expressionQueue.enqueue(expr); + runExpressionQueue(); return expr; + } +void QalculateSession::runExpressionQueue() +{ + if(!m_expressionQueue.isEmpty()) { + + if(!m_currentExpression) + m_currentExpression = m_expressionQueue.head(); + + else { + /* there was some expression that was being executed by cantor. We run the new expression only + if the current expression's status is 'Done' or 'Error', if not , we simply return + */ + Cantor::Expression::Status expr_status = m_currentExpression->status(); + if(expr_status != Cantor::Expression::Done && expr_status != Cantor::Expression::Error) + return; + } + + m_currentExpression = m_expressionQueue.head(); + connect(m_currentExpression, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(currentExpressionStatusChanged(Cantor::Expression::Status))); + // start processing the expression + m_currentExpression->evaluate(); + + } +} + + Cantor::CompletionObject* QalculateSession::completionFor(const QString& command, int index) { return new QalculateCompletionObject(command, index, this); } Cantor::SyntaxHelpObject* QalculateSession::syntaxHelpFor(const QString& cmd) { return new QalculateSyntaxHelpObject(cmd, this); } QSyntaxHighlighter* QalculateSession::syntaxHighlighter(QObject* parent) { return new QalculateHighlighter(parent); } -void QalculateSession::setLastResult(MathStructure result) -{ - for (int i = m_ansVariables.size()-1; i >0 ; --i) { - m_ansVariables[i]->set(m_ansVariables[i-1]->get()); - } - m_ansVariables[0]->set(result); -} QAbstractItemModel* QalculateSession::variableModel() { return m_variableModel; } diff --git a/src/backends/qalculate/qalculatesession.h b/src/backends/qalculate/qalculatesession.h index b911a0f2..388200e5 100644 --- a/src/backends/qalculate/qalculatesession.h +++ b/src/backends/qalculate/qalculatesession.h @@ -1,61 +1,90 @@ /************************************************************************************* * Copyright (C) 2009 by Milian Wolff * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #ifndef QALCULATE_SESSION_H #define QALCULATE_SESSION_H #include "session.h" +#include "qalculateexpression.h" #include +#include +#include #include #include namespace Cantor { class DefaultVariableModel; } class QalculateEngine; +class QProcess; + class QalculateSession : public Cantor::Session { Q_OBJECT private: - QList m_ansVariables; Cantor::DefaultVariableModel* m_variableModel; + QProcess* m_process; + QalculateExpression* m_currentExpression; + QString m_output; + QString m_finalOutput; + QString m_currentCommand; + QString m_saveError; + QQueue m_expressionQueue; + QQueue m_commandQueue; + bool m_isSaveCommand; + + +private: + void runExpressionQueue(); + void runCommandQueue(); + QString parseSaveCommand(QString& currentCmd); + void storeVariables(QString& currentCmd, QString output); public: QalculateSession( Cantor::Backend* backend); ~QalculateSession(); virtual void login(); virtual void logout(); virtual void interrupt(); virtual Cantor::Expression* evaluateExpression(const QString& command, Cantor::Expression::FinishingBehavior behave); virtual Cantor::CompletionObject* completionFor(const QString& cmd, int index=-1); virtual Cantor::SyntaxHelpObject* syntaxHelpFor(const QString& cmd); virtual QSyntaxHighlighter* syntaxHighlighter(QObject* parent); - void setLastResult(MathStructure); + void runExpression(); QAbstractItemModel* variableModel(); + +public: + QMap variables; + +public Q_SLOTS: + void readOutput(); + void readError(); + void processStarted(); + void currentExpressionStatusChanged(Cantor::Expression::Status status); }; #endif