diff --git a/src/backends/qalculate/qalculateexpression.cpp b/src/backends/qalculate/qalculateexpression.cpp index e17a11b3..339478fa 100644 --- a/src/backends/qalculate/qalculateexpression.cpp +++ b/src/backends/qalculate/qalculateexpression.cpp @@ -1,883 +1,883 @@ /************************************************************************************* * 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, bool internal) : Cantor::Expression(session, internal), m_tempFile(nullptr) { } QalculateExpression::~QalculateExpression() { if (m_tempFile) - delete m_tempFile; + delete m_tempFile; } void QalculateExpression::evaluate() { /* Use Api for: * help * plot Use qalc for any other command */ setStatus(Cantor::Expression::Computing); if (command().isEmpty()) { setStatus(Cantor::Expression::Done); return; } 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; } } // 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(); } void QalculateExpression::parseOutput(QString& output) { 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); } void QalculateExpression::updateVariables() { QalculateSession* currentSession = dynamic_cast(session()); QMap &variables = currentSession->variables; QMap::const_iterator it = variables.constBegin(); while (it != variables.constEnd()) { currentSession->variableModel()->addVariable(it.key(), it.value()); ++it; } } void QalculateExpression::parseError(QString& error) { error.remove(QLatin1String(">")); error = error.trimmed(); qDebug() << "Error from qalc for command: " << command() << " " << error << endl; setErrorMessage(error); setStatus(Cantor::Expression::Error); } void QalculateExpression::interrupt() { 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(); po.comma_as_separator = false; 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() ); } 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/lib/backend.cpp b/src/lib/backend.cpp index 88cfbd83..261d9f7b 100644 --- a/src/lib/backend.cpp +++ b/src/lib/backend.cpp @@ -1,207 +1,206 @@ /* 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. --- Copyright (C) 2009 Alexander Rieder */ #include "backend.h" using namespace Cantor; #include #include #include #include #include #include #include #include #include "extension.h" class Cantor::BackendPrivate { public: QString name; QString comment; QString icon; QString url; bool enabled; }; Backend::Backend(QObject* parent, const QList& args) : QObject(parent), d(new BackendPrivate) { Q_UNUSED(args) d->enabled=true; } Backend::~Backend() { delete d; } QString Backend::name() const { return d->name; } QString Backend::comment() const { return d->comment; } QString Backend::description() const { return comment(); } QString Backend::icon() const { return d->icon; } QString Backend::version() const { return QLatin1String(); } QString Backend::url() const { return d->url; } QUrl Backend::helpUrl() const { return QUrl(); } bool Backend::isEnabled() const { return d->enabled&&requirementsFullfilled(); } void Backend::setEnabled(bool enabled) { d->enabled=enabled; } -static QList backendCache; - QStringList Backend::listAvailableBackends() { QList backends=availableBackends(); QStringList l; foreach(Backend* b, backends) { if(b->isEnabled()) l<name(); } return l; } QList Backend::availableBackends() { + static QList backendCache; //if we already have all backends Cached, just return the cache. //otherwise create the available backends if(!backendCache.isEmpty()) { return backendCache; } QStringList pluginDirs; foreach(const QString &dir, QCoreApplication::libraryPaths()){ pluginDirs << dir + QDir::separator() + QLatin1String("cantor/backends"); } QPluginLoader loader; foreach(const QString &dir, pluginDirs){ qDebug() << "dir: " << dir; QStringList plugins; QDir pluginDir = QDir(dir); plugins = pluginDir.entryList(); foreach (const QString &plugin, plugins){ if (plugin==QLatin1String(".") || plugin==QLatin1String("..")) continue; loader.setFileName(dir + QDir::separator() + plugin); if (!loader.load()){ qDebug() << "Error while loading plugin: " << plugin; continue; } KPluginFactory* factory = KPluginLoader(loader.fileName()).factory(); Backend* backend = factory->create(); KPluginMetaData info(loader); backend->d->name=info.name(); backend->d->comment=info.description(); backend->d->icon=info.iconName(); backend->d->url=info.website(); backendCache< backends=availableBackends(); foreach(Backend* b, backends) { if(b->name().toLower()==name.toLower() || b->id().toLower()==name.toLower()) return b; } return nullptr; } QWidget* Backend::settingsWidget(QWidget* parent) const { Q_UNUSED(parent) return nullptr; } KConfigSkeleton* Backend::config() const { return nullptr; } QStringList Backend::extensions() const { QList extensions=findChildren(QRegExp(QLatin1String(".*Extension"))); QStringList names; foreach(Extension* e, extensions) names<objectName(); return names; } Extension* Backend::extension(const QString& name) const { return findChild(name); } bool Backend::requirementsFullfilled() const { return true; } diff --git a/src/lib/latexrenderer.cpp b/src/lib/latexrenderer.cpp index f7caf52a..00726714 100644 --- a/src/lib/latexrenderer.cpp +++ b/src/lib/latexrenderer.cpp @@ -1,257 +1,267 @@ /* 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. --- Copyright (C) 2011 Alexander Rieder */ #include "latexrenderer.h" using namespace Cantor; #include #include #include #include #include #include #include #include #include "settings.h" class Cantor::LatexRendererPrivate { public: QString latexCode; QString header; LatexRenderer::Method method; bool isEquationOnly; LatexRenderer::EquationType equationType; QString errorMessage; bool success; QString latexFilename; + QTemporaryFile* texFile; }; static const QLatin1String tex("\\documentclass[12pt,fleqn]{article}"\ "\\usepackage{latexsym,amsfonts,amssymb,ulem}"\ "\\usepackage{amsmath}"\ "\\usepackage[dvips]{graphicx}"\ "\\usepackage[utf8]{inputenc}"\ "\\usepackage{xcolor}"\ "\\setlength\\textwidth{5in}"\ "\\setlength{\\parindent}{0pt}"\ "%1"\ "\\pagecolor[rgb]{%2,%3,%4}"\ "\\pagestyle{empty}"\ "\\begin{document}"\ "\\color[rgb]{%5,%6,%7}"\ "%8"\ "\\end{document}"); static const QLatin1String eqnHeader("\\begin{eqnarray*}%1\\end{eqnarray*}"); static const QLatin1String inlineEqnHeader("$%1$"); LatexRenderer::LatexRenderer(QObject* parent) : QObject(parent), d(new LatexRendererPrivate) { d->method=LatexMethod; d->isEquationOnly=false; d->equationType=InlineEquation; d->success=false; + d->texFile=nullptr; } LatexRenderer::~LatexRenderer() { delete d; } QString LatexRenderer::latexCode() const { return d->latexCode; } void LatexRenderer::setLatexCode(const QString& src) { d->latexCode=src; } QString LatexRenderer::header() const { return d->header; } void LatexRenderer::addHeader(const QString& header) { d->header.append(header); } void LatexRenderer::setHeader(const QString& header) { d->header=header; } LatexRenderer::Method LatexRenderer::method() const { return d->method; } void LatexRenderer::setMethod(LatexRenderer::Method method) { d->method=method; } void LatexRenderer::setEquationType(LatexRenderer::EquationType type) { d->equationType=type; } LatexRenderer::EquationType LatexRenderer::equationType() const { return d->equationType; } void LatexRenderer::setErrorMessage(const QString& msg) { d->errorMessage=msg; } QString LatexRenderer::errorMessage() const { return d->errorMessage; } bool LatexRenderer::renderingSuccessful() const { return d->success; } void LatexRenderer::setEquationOnly(bool isEquationOnly) { d->isEquationOnly=isEquationOnly; } bool LatexRenderer::isEquationOnly() const { return d->isEquationOnly; } QString LatexRenderer::imagePath() const { return d->latexFilename; } void LatexRenderer::render() { switch(d->method) { case LatexRenderer::LatexMethod: renderWithLatex(); break; case LatexRenderer::MmlMethod: renderWithMml(); break; }; } void LatexRenderer::renderBlocking() { QEventLoop event; connect(this, &LatexRenderer::done, &event, &QEventLoop::quit); connect(this, &LatexRenderer::error, &event, &QEventLoop::quit); render(); event.exec(); } void LatexRenderer::renderWithLatex() { qDebug()<<"rendering using latex method"; QString dir=QStandardPaths::writableLocation(QStandardPaths::TempLocation); - QTemporaryFile *texFile=new QTemporaryFile(dir + QDir::separator() + QLatin1String("cantor_tex-XXXXXX.tex")); - texFile->open(); + + if (d->texFile) + delete d->texFile; + + d->texFile=new QTemporaryFile(dir + QDir::separator() + QLatin1String("cantor_tex-XXXXXX.tex")); + d->texFile->open(); KColorScheme scheme(QPalette::Active); const QColor &backgroundColor=scheme.background().color(); const QColor &foregroundColor=scheme.foreground().color(); QString expressionTex=tex; expressionTex=expressionTex.arg(d->header) .arg(backgroundColor.redF()).arg(backgroundColor.greenF()).arg(backgroundColor.blueF()) .arg(foregroundColor.redF()).arg(foregroundColor.greenF()).arg(foregroundColor.blueF()); if(isEquationOnly()) { switch(equationType()) { case FullEquation: expressionTex=expressionTex.arg(eqnHeader); break; case InlineEquation: expressionTex=expressionTex.arg(inlineEqnHeader); break; } } expressionTex=expressionTex.arg(d->latexCode); // qDebug()<<"full tex:\n"<write(expressionTex.toUtf8()); - texFile->flush(); + d->texFile->write(expressionTex.toUtf8()); + d->texFile->flush(); - QString fileName = texFile->fileName(); + QString fileName = d->texFile->fileName(); qDebug()<<"fileName: "<latexFilename=fileName; d->latexFilename.replace(QLatin1String(".tex"), QLatin1String(".eps")); KProcess *p=new KProcess( this ); p->setWorkingDirectory(dir); (*p)<latexCommand()<start(); } void LatexRenderer::convertToPs() { QString dviFile=d->latexFilename; dviFile.replace(QLatin1String(".eps"), QLatin1String(".dvi")); KProcess *p=new KProcess( this ); qDebug()<<"converting to eps: "<dvipsCommand()<<"-E"<<"-o"<latexFilename<dvipsCommand()<latexFilename<start(); } void LatexRenderer::convertingDone() { QFileInfo info(d->latexFilename); qDebug() <<"remove temporary files for " << d->latexFilename; + + delete d->texFile; + d->texFile = nullptr; + QString pathWithoutExtention = info.path() + QDir::separator() + info.completeBaseName(); QFile::remove(pathWithoutExtention + QLatin1String(".log")); QFile::remove(pathWithoutExtention + QLatin1String(".aux")); QFile::remove(pathWithoutExtention + QLatin1String(".tex")); QFile::remove(pathWithoutExtention + QLatin1String(".dvi")); if(info.exists()) { d->success=true; emit done(); }else { d->success=false; setErrorMessage(QStringLiteral("failed to create the latex preview image")); emit error(); } } void LatexRenderer::renderWithMml() { qDebug()<<"WARNING: MML rendering not implemented yet!"; emit done(); } diff --git a/src/scripteditor/scripteditorwidget.cpp b/src/scripteditor/scripteditorwidget.cpp index 5cf05efe..d3ddca31 100644 --- a/src/scripteditor/scripteditorwidget.cpp +++ b/src/scripteditor/scripteditorwidget.cpp @@ -1,153 +1,161 @@ /* 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. --- Copyright (C) 2009 Alexander Rieder */ #include "scripteditorwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ScriptEditorWidget::ScriptEditorWidget(const QString& filter, const QString& highlightingMode, QWidget* parent) : KXmlGuiWindow(parent), m_filter(filter), m_editor(nullptr), m_script(nullptr), m_tmpFile(nullptr) { setObjectName(QStringLiteral("ScriptEditor")); KStandardAction::openNew(this, SLOT(newScript()), actionCollection()); KStandardAction::open(this, SLOT(open()), actionCollection()); KStandardAction::close(this, SLOT(close()), actionCollection()); QAction * runAction = actionCollection()->addAction(QStringLiteral("file_execute"), this, SLOT(run())); runAction->setIcon(QIcon::fromTheme(QStringLiteral("system-run"))); runAction->setText(i18n("Run Script")); KTextEditor::Editor* editor = KTextEditor::Editor::instance(); if (!editor) { KMessageBox::error(this, i18n("A KDE text-editor component could not be found;\n" "please check your KDE installation.")); } else { m_script=editor->createDocument(nullptr); m_editor=qobject_cast(m_script->createView(this)); m_script->setHighlightingMode(highlightingMode); KConfigGroup cg(KSharedConfig::openConfig(), "ScriptEditor"); setAutoSaveSettings(cg, true); setCentralWidget(m_editor); setupGUI(QSize(500,600), Default, QStringLiteral("cantor_scripteditor.rc")); guiFactory()->addClient(m_editor); KWindowConfig::restoreWindowSize(this->windowHandle(), cg); connect(m_script, &KTextEditor::Document::modifiedChanged, this, &ScriptEditorWidget::updateCaption); connect(m_script, &KTextEditor::Document::documentUrlChanged, this, &ScriptEditorWidget::updateCaption); updateCaption(); } } +ScriptEditorWidget::~ScriptEditorWidget() +{ + if (m_script) + delete m_script; + if (m_tmpFile) + delete m_tmpFile; +} + void ScriptEditorWidget::newScript() { QString highlightingMode = m_script->highlightingMode(); m_script->closeUrl(); m_script->setHighlightingMode(highlightingMode); } void ScriptEditorWidget::open() { QUrl url = QFileDialog::getOpenFileUrl(this, QString(), QUrl(), m_filter); m_script->openUrl(url); } void ScriptEditorWidget::open(const QUrl &url) { m_script->openUrl(url); } void ScriptEditorWidget::run() { QString filename; if(!m_script->url().isLocalFile()) { // If the script is not in a local file, write it to a temporary file if(m_tmpFile==nullptr) { m_tmpFile=new QTemporaryFile(); } else { m_tmpFile->resize(0); } m_tmpFile->open(); QString text=m_script->text(); m_tmpFile->write(text.toUtf8()); m_tmpFile->close(); filename=m_tmpFile->fileName(); }else { m_script->save(); filename=m_script->url().toLocalFile(); } qDebug()<<"running "<queryClose(); else return true; } void ScriptEditorWidget::updateCaption() { QString fileName = m_script->url().toLocalFile(); bool modified = m_script->isModified(); if (fileName.isEmpty()) { setCaption(i18n("Script Editor"), modified); }else { setCaption(i18n("Script Editor - %1", fileName), modified); } } diff --git a/src/scripteditor/scripteditorwidget.h b/src/scripteditor/scripteditorwidget.h index 2ae491b0..bba26cfe 100644 --- a/src/scripteditor/scripteditorwidget.h +++ b/src/scripteditor/scripteditorwidget.h @@ -1,62 +1,62 @@ /* 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. --- Copyright (C) 2009 Alexander Rieder */ #ifndef _SCRIPTEDITORWIDGET_H #define _SCRIPTEDITORWIDGET_H #include class QTemporaryFile; class QGridLayout; namespace KTextEditor { class View; class Document; } class ScriptEditorWidget : public KXmlGuiWindow { Q_OBJECT public: explicit ScriptEditorWidget( const QString& filter, const QString& highlightingMode, QWidget* parent = nullptr ); - ~ScriptEditorWidget() override = default; + ~ScriptEditorWidget() override; void open(const QUrl &url); Q_SIGNALS: void runScript(const QString& filename); private Q_SLOTS: void newScript(); void open(); void run(); void updateCaption(); protected: bool queryClose() override; private: QString m_filter; KTextEditor::View* m_editor; KTextEditor::Document* m_script; QTemporaryFile* m_tmpFile; }; #endif /* _SCRIPTEDITORWIDGET_H */