diff --git a/src/backends/maxima/maximaexpression.cpp b/src/backends/maxima/maximaexpression.cpp index d7a2e0b4..46955153 100644 --- a/src/backends/maxima/maximaexpression.cpp +++ b/src/backends/maxima/maximaexpression.cpp @@ -1,639 +1,635 @@ /* 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-2012 Alexander Rieder */ #include "maximaexpression.h" #include #include "maximasession.h" #include "textresult.h" #include "epsresult.h" #include "imageresult.h" #include "helpresult.h" #include "latexresult.h" #include "settings.h" #include #include #include #include #include #include #include #include -MaximaExpression::MaximaExpression( Cantor::Session* session ) : Cantor::Expression(session) +MaximaExpression::MaximaExpression( Cantor::Session* session ) : Cantor::Expression(session), + m_tempFile(nullptr), + m_isHelpRequest(false), + m_isPlot(false), + m_gotErrorContent(false) { - qDebug(); - m_tempFile=nullptr; -} - -MaximaExpression::~MaximaExpression() -{ - } - void MaximaExpression::evaluate() { setStatus(Cantor::Expression::Computing); //until we get the real output Id from maxima, set it to invalid setId(-1); m_isHelpRequest=false; m_isPlot=false; m_gotErrorContent=false; if(m_tempFile) m_tempFile->deleteLater(); m_tempFile=nullptr; //check if this is a ?command if(command().startsWith(QLatin1Char('?'))||command().startsWith(QLatin1String("describe("))||command().startsWith(QLatin1String("example("))) m_isHelpRequest=true; if(command().contains(QRegExp(QLatin1String("(?:plot2d|plot3d)\\s*\\([^\\)]"))) && MaximaSettings::self()->integratePlots() && !command().contains(QLatin1String("ps_file"))) { m_isPlot=true; #ifdef WITH_EPS m_tempFile=new QTemporaryFile(QDir::tempPath() + QLatin1String("/cantor_maxima-XXXXXX.eps" )); #else m_tempFile=new QTemporaryFile(QDir::tempPath() + QLatin1String("/cantor_maxima-XXXXXX.png")); #endif m_tempFile->open(); disconnect(&m_fileWatch, &KDirWatch::dirty, this, &MaximaExpression::imageChanged); m_fileWatch.addFile(m_tempFile->fileName()); connect(&m_fileWatch, &KDirWatch::dirty, this, &MaximaExpression::imageChanged); } const QString& cmd=command(); bool isComment = true; int commentLevel = 0; bool inString = false; for (int i = 0; i < cmd.size(); ++i) { if (cmd[i] == QLatin1Char('\\')) { ++i; // skip the next character if (commentLevel == 0 && !inString) { isComment = false; } } else if (cmd[i] == QLatin1Char('"') && commentLevel == 0) { inString = !inString; isComment = false; } else if (cmd.mid(i,2) == QLatin1String("/*") && !inString) { ++commentLevel; ++i; } else if (cmd.mid(i,2) == QLatin1String("*/") && !inString) { if (commentLevel == 0) { qDebug() << "Comments mismatched!"; setErrorMessage(i18n("Error: Too many */")); setStatus(Cantor::Expression::Error); return; } ++i; --commentLevel; } else if (isComment && commentLevel == 0 && !cmd[i].isSpace()) { isComment = false; } } if (commentLevel > 0) { qDebug() << "Comments mismatched!"; setErrorMessage(i18n("Error: Too many /*")); setStatus(Cantor::Expression::Error); return; } if (inString) { qDebug() << "String not closed"; setErrorMessage(i18n("Error: expected \" before ;")); setStatus(Cantor::Expression::Error); return; } if(isComment) { setStatus(Cantor::Expression::Done); return; } dynamic_cast(session())->appendExpressionToQueue(this); } void MaximaExpression::interrupt() { qDebug()<<"interrupting"; dynamic_cast(session())->interrupt(this); setStatus(Cantor::Expression::Interrupted); } QString MaximaExpression::internalCommand() { QString cmd=command(); if(m_isPlot) { if(!m_tempFile) { qDebug()<<"plotting without tempFile"; return QString(); } QString fileName = m_tempFile->fileName(); #ifdef WITH_EPS const QString psParam=QLatin1String("[gnuplot_ps_term_command, \"set size 1.0, 1.0; set term postscript eps color solid \"]"); const QString plotParameters = QLatin1String("[ps_file, \"")+ fileName+QLatin1String("\"],")+psParam; #else const QString plotParameters = QLatin1String("[gnuplot_term, \"png size 500,340\"], [gnuplot_out_file, \"")+fileName+QLatin1String("\"]"); #endif cmd.replace(QRegExp(QLatin1String("((plot2d|plot3d)\\s*\\(.*)\\)([;\n]|$)")), QLatin1String("\\1, ")+plotParameters+QLatin1String(");")); } if (!cmd.endsWith(QLatin1Char('$'))) { if (!cmd.endsWith(QLatin1String(";"))) cmd+=QLatin1Char(';'); } //replace all newlines with spaces, as maxima isn't sensitive about //whitespaces, and without newlines the whole command //is executed at once, without outputting an input //prompt after each line cmd.replace(QLatin1Char('\n'), QLatin1Char(' ')); //lisp-quiet doesn't print a prompt after the command //is completed, which causes the parsing to hang. //replace the command with the non-quiet version cmd.replace(QRegExp(QLatin1String("^:lisp-quiet")), QLatin1String(":lisp")); return cmd; } void MaximaExpression::forceDone() { qDebug()<<"forcing Expression state to DONE"; setResult(nullptr); setStatus(Cantor::Expression::Done); } void MaximaExpression::addInformation(const QString& information) { qDebug()<<"adding information"; QString inf=information; if(!inf.endsWith(QLatin1Char(';'))) inf+=QLatin1Char(';'); Cantor::Expression::addInformation(inf); dynamic_cast(session())->sendInputToProcess(inf+QLatin1Char('\n')); } //The maxima backend is modified, so that it outputs //xml-style tags around outputs, input prompts etc. //the following are some simple helper functions to faciliate parsing inline void skipWhitespaces(int* idx, const QString& txt) { for(;*idx < txt.size() && (txt[*idx]).isSpace();++(*idx)); } QStringRef readXmlOpeningTag(int* idx, const QString& txt, bool* isComplete=nullptr) { qDebug()<<"trying to read an opening tag"; if (*idx >= txt.size()) return QStringRef(); skipWhitespaces(idx, txt); if(isComplete) *isComplete=false; if(txt[*idx]!=QLatin1Char('<')) { qDebug()<<"This is NOT AN OPENING TAG."<')) { if(isComplete) *isComplete=true; break; }else length++; } return QStringRef(&txt, startIndex, length); } QStringRef readXmlTagContent(int* idx, const QString& txt, const QStringRef& name, bool* isComplete=nullptr) { bool readingClosingTag=false; int contentStartIdx=*idx; int contentLength=0; int currentTagStartIdx=-1; int currentTagLength=0; if(isComplete) *isComplete=false; while(*idx0&&txt[(*idx)-1]==QLatin1Char('<')) { //remove the opening < contentLength--; currentTagStartIdx=*idx+1; currentTagLength=0; readingClosingTag=true; } else if(readingClosingTag) { if(c==QLatin1Char('>')) { const QStringRef currentTagName(&txt, currentTagStartIdx, currentTagLength); if(currentTagName==name) { //eat up the closing > ++(*idx); if(isComplete) (*isComplete)=true; break; } readingClosingTag=false; }else currentTagLength++; } else contentLength++; ++(*idx); } if(contentStartIdx+contentLength>txt.size()) { qDebug()<<"something is wrong with the content-length "<"), idx); int idx2=out.indexOf(QLatin1String(""), idx); idx1=(idx1==-1) ? out.size():idx1; idx2=(idx2==-1) ? out.size():idx2; int newIdx=qMin(idx1, idx2); if(newIdx>idx) { const QString& err=out.mid(idx, newIdx-idx); if(!err.isEmpty()) m_gotErrorContent=true; errorBuffer+=err; qDebug()<<"the unmatched part of the output is: "<0) { textBuffer.append(QLatin1String("\n")); latexBuffer.append(QLatin1String("\n")); } result=parseResult(&idx, out, textBuffer, latexBuffer); numResults++; qDebug()<<"got "< with their html code, so they won't be confused as html tags m_errorBuffer.replace( QLatin1Char('<') , QLatin1String("<")); m_errorBuffer.replace( QLatin1Char('>') , QLatin1String(">")); if(command().startsWith(QLatin1String(":lisp"))||command().startsWith(QLatin1String(":lisp-quiet"))) { if(result) { if(result->type()==Cantor::TextResult::Type) m_errorBuffer.prepend(dynamic_cast(result)->plain()+QLatin1String("\n")); else if(result->type()==Cantor::LatexResult::Type) m_errorBuffer.prepend(dynamic_cast(result)->plain()+QLatin1String("\n")); } Cantor::TextResult* result=new Cantor::TextResult(m_errorBuffer); setResult(result); setStatus(Cantor::Expression::Done); }else if(m_isHelpRequest) //Help Messages are also provided in the errorBuffer. { Cantor::HelpResult* result=new Cantor::HelpResult(m_errorBuffer); setResult(result); setStatus(Cantor::Expression::Done); }else { if(result) { qDebug()<<"result: "<toHtml(); if(result->type()==Cantor::TextResult::Type) m_errorBuffer.prepend(dynamic_cast(result)->plain()+QLatin1String("\n")); else if(result->type()==Cantor::LatexResult::Type) m_errorBuffer.prepend(dynamic_cast(result)->plain()+QLatin1String("\n")); } qDebug()<<"errorBuffer: "< with their html code, so they won't be confused as html tags text.replace( QLatin1Char('<') , QLatin1String("<")); text.replace( QLatin1Char('>') , QLatin1String(">")); QRegExp outputPromptRegexp=QRegExp(QLatin1Char('^')+MaximaSession::MaximaOutputPrompt.pattern()); int idxOfPrompt=outputPromptRegexp.indexIn(text); text.remove(idxOfPrompt, outputPromptRegexp.matchedLength()); //find the number if this output in the MaximaOutputPrompt QString prompt=outputPromptRegexp.cap(0).trimmed(); bool ok; QString idString=prompt.mid(3, prompt.length()-4); int id=idString.toInt(&ok); if(ok) setId(id); else setId(-1); qDebug()<<"prompt: "< element wasn't read completely, there //is no point in trying to render it. Use text for //incomplete results. if(!isLatexComplete ||(latexBuffer.trimmed().isEmpty()&&latex.isEmpty()) ||m_isHelpRequest||isInternal()) { qDebug()<<"using text"; result=new Cantor::TextResult(textBuffer); }else { qDebug()<<"using latex"; //strip away the latex code for the label. //it is contained in an \mbox{} call int i; int pcount=0; for(i=latex.indexOf(QLatin1String("\\mbox{"))+5;isetFormat(Cantor::TextResult::LatexFormat); } } return result; } void MaximaExpression::parseError(const QString& out) { m_errorBuffer.append(out); } void MaximaExpression::imageChanged() { qDebug()<<"the temp image has changed"; if(m_tempFile->size()>0) { #ifdef WITH_EPS setResult( new Cantor::EpsResult( QUrl::fromLocalFile(m_tempFile->fileName()) ) ); #else setResult( new Cantor::ImageResult( QUrl::fromLocalFile(m_tempFile->fileName()) ) ); #endif setStatus(Cantor::Expression::Done); } } QString MaximaExpression::additionalLatexHeaders() { return QString(); } diff --git a/src/backends/maxima/maximaexpression.h b/src/backends/maxima/maximaexpression.h index 48935bc5..b2a29c86 100644 --- a/src/backends/maxima/maximaexpression.h +++ b/src/backends/maxima/maximaexpression.h @@ -1,74 +1,71 @@ /* 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-2012 Alexander Rieder */ #ifndef _MAXIMAEXPRESSION_H #define _MAXIMAEXPRESSION_H #include "expression.h" #include "KDirWatch" #include -#include -class QTimer; class QTemporaryFile; class MaximaExpression : public Cantor::Expression { Q_OBJECT - public: - explicit MaximaExpression( Cantor::Session* session); - ~MaximaExpression() override; + +public: + explicit MaximaExpression(Cantor::Session*); void evaluate() Q_DECL_OVERRIDE; void interrupt() Q_DECL_OVERRIDE; - void addInformation(const QString& information) Q_DECL_OVERRIDE; + void addInformation(const QString&) Q_DECL_OVERRIDE; bool needsLatexResult(); //returns the command that should be send to //the Maxima process, it's different from the //command() for example to allow plot embedding QString internalCommand(); //Forces the status of this Expression to done void forceDone(); //reads from @param out until a prompt indicates that a new expression has started - bool parseOutput(QString& out); - void parseError(const QString& out); + bool parseOutput(QString&); + void parseError(const QString&); - private Q_SLOTS: +private Q_SLOTS: void imageChanged(); - private: +private: QString additionalLatexHeaders() Q_DECL_OVERRIDE; Cantor::Result* parseResult(int* idx,QString& out,QString& textBuffer,QString& latexBuffer); - private: + QTemporaryFile *m_tempFile; KDirWatch m_fileWatch; bool m_isHelpRequest; bool m_isPlot; - QTimer* m_askTimer; QString m_errorBuffer; bool m_gotErrorContent; }; #endif /* _MAXIMAEXPRESSION_H */ diff --git a/src/backends/octave/octaveexpression.cpp b/src/backends/octave/octaveexpression.cpp index 944d6d9b..2bd159af 100644 --- a/src/backends/octave/octaveexpression.cpp +++ b/src/backends/octave/octaveexpression.cpp @@ -1,178 +1,170 @@ /* Copyright (C) 2010 Miha Čančula 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 "octaveexpression.h" #include "octavesession.h" #include "defaultvariablemodel.h" #include "textresult.h" #include #include #include #include static const char* printCommand = "cantor_print();"; -OctaveExpression::OctaveExpression(Cantor::Session* session): Expression(session) +OctaveExpression::OctaveExpression(Cantor::Session* session): Expression(session), + m_plotPending(false), + m_finished(false), + m_error(false) { - qDebug() << "OctaveExpression construtor"; m_plotCommands << QLatin1String("plot") << QLatin1String("semilogx") << QLatin1String("semilogy") << QLatin1String("loglog") << QLatin1String("polar") << QLatin1String("mesh") << QLatin1String("contour") << QLatin1String("bar") << QLatin1String("stairs") << QLatin1String("errorbar") << QLatin1String("surf") << QLatin1String("sombrero") << QLatin1String("hist") << QLatin1String("fplot") << QLatin1String("imshow"); m_plotCommands << QLatin1String("cantor_plot2d") << QLatin1String("cantor_plot3d"); - - m_error = false; - m_plotPending = false; } - -OctaveExpression::~OctaveExpression() -{ - -} - - void OctaveExpression::interrupt() { qDebug() << "interrupt"; setStatus(Interrupted); } void OctaveExpression::evaluate() { qDebug() << "evaluate"; QString cmd = command(); QStringList cmdWords = cmd.split(QRegExp(QLatin1String("\\b")), QString::SkipEmptyParts); if (!cmdWords.contains(QLatin1String("help")) && !cmdWords.contains(QLatin1String("completion_matches"))) { foreach (const QString& plotCmd, m_plotCommands) { if (cmdWords.contains(plotCmd)) { setPlotPending(true); qDebug() << "Executing a plot command"; break; } } } if ( m_plotPending && !cmd.contains(QLatin1String("cantor_plot"))) { // This was a manual plot, we have to add a print command if (!cmd.endsWith(QLatin1Char(';')) && !cmd.endsWith(QLatin1Char(','))) { cmd += QLatin1Char(','); } cmd += QLatin1String(printCommand); setCommand(cmd); } m_finished = false; setStatus(Computing); OctaveSession* octaveSession = dynamic_cast(session()); if (octaveSession) { octaveSession->runExpression(this); } } void OctaveExpression::parseOutput ( QString output ) { qDebug() << "parseOutput: " << output; m_resultString += output; if (!m_resultString.trimmed().isEmpty()) { if (command().contains(QLatin1String("help"))) { setResult(new Cantor::HelpResult(m_resultString)); } else { setResult(new Cantor::TextResult(m_resultString)); } } } void OctaveExpression::parseError(QString error) { qDebug() << error; if (false && error.contains(QLatin1String("warning"))) { parseOutput(error); } else { m_error = true; setErrorMessage(error); setStatus(Error); } } void OctaveExpression::parsePlotFile(QString file) { qDebug() << "parsePlotFile"; if (QFile::exists(file)) { qDebug() << "OctaveExpression::parsePlotFile: " << file; setResult(new OctavePlotResult(QUrl::fromLocalFile(file))); setPlotPending(false); if (m_finished) { setStatus(Done); } } } void OctaveExpression::finalize() { qDebug() << "finalize: " << m_resultString; foreach ( const QString& line, m_resultString.simplified().split(QLatin1Char('\n'), QString::SkipEmptyParts) ) { if ((m_resultString.contains(QLatin1Char('='))) && !(command().startsWith(QLatin1String("help("))) && !(command().contains(QLatin1String("help "))) && !(command().contains(QLatin1String("type(")))) { qDebug() << line; // Probably a new variable QStringList parts = line.split(QLatin1Char('=')); if (parts.size() >= 2) { Cantor::DefaultVariableModel* model = dynamic_cast(session()->variableModel()); if (model) { model->addVariable(parts.first().trimmed(), parts.last().trimmed()); } } } } qDebug() << m_plotPending << m_error; m_finished = true; if (!m_plotPending) { setStatus(m_error ? Error : Done); } } void OctaveExpression::setPlotPending(bool plot) { m_plotPending = plot; } diff --git a/src/backends/octave/octaveexpression.h b/src/backends/octave/octaveexpression.h index ac8a78aa..3b2566b7 100644 --- a/src/backends/octave/octaveexpression.h +++ b/src/backends/octave/octaveexpression.h @@ -1,61 +1,59 @@ /* Copyright (C) 2010 Miha Čančula 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 OCTAVEEXPRESSION_H #define OCTAVEEXPRESSION_H #include #include #ifdef WITH_EPS #include "epsresult.h" typedef Cantor::EpsResult OctavePlotResult; #else #include "imageresult.h" typedef Cantor::ImageResult OctavePlotResult; #endif - class OctaveExpression : public Cantor::Expression { Q_OBJECT - public: - OctaveExpression(Cantor::Session* session); - ~OctaveExpression() override; - void interrupt() Q_DECL_OVERRIDE; - void evaluate() Q_DECL_OVERRIDE; - void parseOutput ( QString output ); - void parseError(QString error); - void parsePlotFile(QString file); - - void finalize(); - void setPlotPending(bool plot); - - private: - QString m_resultString; - int m_numberOfLines; - bool m_plotPending; - bool m_finished; - bool m_error; - QStringList m_plotCommands; +public: + OctaveExpression(Cantor::Session*); + + void interrupt() Q_DECL_OVERRIDE; + void evaluate() Q_DECL_OVERRIDE; + void parseOutput (QString); + void parseError(QString); + void parsePlotFile(QString file); + + void finalize(); + void setPlotPending(bool); + +private: + QString m_resultString; + bool m_plotPending; + bool m_finished; + bool m_error; + QStringList m_plotCommands; }; #endif // OCTAVEEXPRESSION_H diff --git a/src/backends/sage/sageexpression.cpp b/src/backends/sage/sageexpression.cpp index 4d756062..ec6f0917 100644 --- a/src/backends/sage/sageexpression.cpp +++ b/src/backends/sage/sageexpression.cpp @@ -1,267 +1,263 @@ /* 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 "sageexpression.h" #include "sagesession.h" #include "textresult.h" #include "imageresult.h" #include "animationresult.h" #include "helpresult.h" #include #include #include #include -SageExpression::SageExpression( Cantor::Session* session ) : Cantor::Expression(session) +SageExpression::SageExpression( Cantor::Session* session ) : Cantor::Expression(session), + m_isHelpRequest(false), + m_promptCount(0) { - qDebug(); -} - -SageExpression::~SageExpression() -{ - } void SageExpression::evaluate() { setStatus(Cantor::Expression::Computing); m_imagePath.clear(); m_isHelpRequest=false; //check if this is a ?command if(command().startsWith(QLatin1Char('?'))||command().endsWith(QLatin1Char('?'))) m_isHelpRequest=true; //coun't how many newlines are in the command, //as sage will output one "sage: " or "....:" for //each. m_promptCount=command().count(QLatin1Char('\n'))+2; dynamic_cast(session())->appendExpressionToQueue(this); } void SageExpression::interrupt() { qDebug()<<"interrupting"; dynamic_cast(session())->sendSignalToProcess(2); dynamic_cast(session())->waitForNextPrompt(); setStatus(Cantor::Expression::Interrupted); } void SageExpression::parseOutput(const QString& text) { QString output=text; //remove carriage returns, we only use \n internally output.remove(QLatin1Char('\r')); //replace appearing backspaces, as they mess the whole output up output.remove(QRegExp(QLatin1String(".\b"))); //replace Escape sequences (only tested with `ls` command) const QChar ESC(0x1b); output.remove(QRegExp(QString(ESC)+QLatin1String("\\][^\a]*\a"))); const QString promptRegexpBase(QLatin1String("(^|\\n)%1")); const QRegExp promptRegexp(promptRegexpBase.arg(QRegExp::escape(QLatin1String(SageSession::SagePrompt)))); const QRegExp altPromptRegexp(promptRegexpBase.arg(QRegExp::escape(QLatin1String(SageSession::SageAlternativePrompt)))); bool endsWithAlternativePrompt=output.endsWith(QLatin1String(SageSession::SageAlternativePrompt)); //remove all prompts. we do this in a loop, because after we removed the first prompt, //there could be a second one, that isn't matched by promptRegexp in the first run, because //it originally isn't at the beginning of a line. int index=-1, index2=-1; while ( (index=output.indexOf(promptRegexp)) != -1 || (index2=output.indexOf(altPromptRegexp)) != -1 ) { qDebug()<<"got prompt"<")); const bool isLatex=m_outputCache.contains(QLatin1String("\\newcommand{\\Bold}")); //Check if it's latex stuff if(isLatex) //It's latex stuff so encapsulate it into an eqnarray environment { stripped.prepend(QLatin1String("\\begin{eqnarray*}")); stripped.append(QLatin1String("\\end{eqnarray*}")); } //strip html tags if(isHtml) { stripped.remove( QRegExp( QLatin1String("<[a-zA-Z\\/][^>]*>") ) ); } else { //Replace < and > with their html code, so they won't be confused as html tags stripped.replace( QLatin1Char('<') , QLatin1String("<")); stripped.replace( QLatin1Char('>') , QLatin1String(">")); } if (stripped.endsWith(QLatin1Char('\n'))) stripped.chop(1); if (m_isHelpRequest) { //Escape whitespace stripped.replace( QLatin1Char(' '), QLatin1String(" ")); //make things quoted in `` `` bold stripped.replace(QRegExp(QLatin1String("``([^`]*)``")), QLatin1String("\\1")); result=new Cantor::HelpResult(stripped); } else { result=new Cantor::TextResult(stripped); } if(isLatex) result->setFormat(Cantor::TextResult::LatexFormat); setResult(result); } else { QMimeDatabase db; QMimeType type = db.mimeTypeForUrl(QUrl::fromLocalFile(m_imagePath)); if(type.inherits(QLatin1String("image/gif"))) setResult( new Cantor::AnimationResult(QUrl::fromLocalFile(m_imagePath ),i18n("Result of %1" , command() ) ) ); else setResult( new Cantor::ImageResult( QUrl::fromLocalFile(m_imagePath ),i18n("Result of %1" , command() ) ) ); } setStatus(Cantor::Expression::Done); } void SageExpression::onProcessError(const QString& msg) { QString errMsg=i18n("%1\nThe last output was: \n %2", msg, m_outputCache.trimmed()); setErrorMessage(errMsg); setStatus(Cantor::Expression::Error); } QString SageExpression::additionalLatexHeaders() { //The LaTeX sage needs the amsmath package and some specific macros. //So include them in the header. //More about the macros requirement in bug #312738 return QLatin1String("\\usepackage{amsmath}\n" \ "\\newcommand{\\ZZ}{\\Bold{Z}}\n" \ "\\newcommand{\\NN}{\\Bold{N}}\n" \ "\\newcommand{\\RR}{\\Bold{R}}\n" \ "\\newcommand{\\CC}{\\Bold{C}}\n" \ "\\newcommand{\\QQ}{\\Bold{Q}}\n" \ "\\newcommand{\\QQbar}{\\overline{\\QQ}}\n" \ "\\newcommand{\\GF}[1]{\\Bold{F}_{#1}}\n" \ "\\newcommand{\\Zp}[1]{\\ZZ_{#1}}\n" \ "\\newcommand{\\Qp}[1]{\\QQ_{#1}}\n" \ "\\newcommand{\\Zmod}[1]{\\ZZ/#1\\ZZ}\n" \ "\\newcommand{\\CDF}{\\Bold{C}}\n" \ "\\newcommand{\\CIF}{\\Bold{C}}\n" \ "\\newcommand{\\CLF}{\\Bold{C}}\n" \ "\\newcommand{\\RDF}{\\Bold{R}}\n" \ "\\newcommand{\\RIF}{\\Bold{I} \\Bold{R}}\n"\ "\\newcommand{\\RLF}{\\Bold{R}}\n" \ "\\newcommand{\\CFF}{\\Bold{CFF}}\n"); } diff --git a/src/backends/sage/sageexpression.h b/src/backends/sage/sageexpression.h index 0c12c33f..e9632621 100644 --- a/src/backends/sage/sageexpression.h +++ b/src/backends/sage/sageexpression.h @@ -1,54 +1,55 @@ /* 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 _SAGEEXPRESSION_H #define _SAGEEXPRESSION_H #include "expression.h" class SageExpression : public Cantor::Expression { Q_OBJECT public: - SageExpression( Cantor::Session* session); - ~SageExpression() override; + SageExpression(Cantor::Session*); void evaluate() Q_DECL_OVERRIDE; void interrupt() Q_DECL_OVERRIDE; - void parseOutput(const QString& text); - void parseError(const QString& text); + void parseOutput(const QString&); + void parseError(const QString&); - void addFileResult(const QString& path); + void addFileResult(const QString&); + + void onProcessError(const QString&); - void onProcessError(const QString& msg); public Q_SLOTS: void evalFinished(); protected: QString additionalLatexHeaders() Q_DECL_OVERRIDE; + private: QString m_outputCache; QString m_imagePath; bool m_isHelpRequest; int m_promptCount; }; #endif /* _SAGEEXPRESSION_H */ diff --git a/src/cantor_part.cpp b/src/cantor_part.cpp index eee703c0..470d5398 100644 --- a/src/cantor_part.cpp +++ b/src/cantor_part.cpp @@ -1,882 +1,881 @@ /* 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 "cantor_part.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "worksheet.h" #include "worksheetview.h" #include "searchbar.h" #include "scripteditorwidget.h" #include "lib/backend.h" #include "lib/extension.h" #include "lib/assistant.h" #include "lib/panelpluginhandler.h" #include "lib/panelplugin.h" #include "lib/worksheetaccess.h" #include "settings.h" //A concrete implementation of the WorksheetAccesssInterface class WorksheetAccessInterfaceImpl : public Cantor::WorksheetAccessInterface { //Q_OBJECT public: WorksheetAccessInterfaceImpl(QObject* parent, Worksheet* worksheet) : WorksheetAccessInterface(parent), m_worksheet(worksheet) { qDebug()<<"new worksheetaccess interface"; connect(worksheet, SIGNAL(sessionChanged()), this, SIGNAL(sessionChanged())); } ~WorksheetAccessInterfaceImpl() override { } QByteArray saveWorksheetToByteArray() Q_DECL_OVERRIDE { return m_worksheet->saveToByteArray(); } void loadWorksheetFromByteArray(QByteArray* data) Q_DECL_OVERRIDE { m_worksheet->load(data); } Cantor::Session* session() Q_DECL_OVERRIDE { return m_worksheet->session(); } // public Q_SLOTS: void evaluate() Q_DECL_OVERRIDE { m_worksheet->evaluate(); } void interrupt() Q_DECL_OVERRIDE { m_worksheet->interrupt(); } private: Worksheet* m_worksheet; }; -CantorPart::CantorPart( QWidget *parentWidget, QObject *parent, const QVariantList & args ): KParts::ReadWritePart(parent) +CantorPart::CantorPart( QWidget *parentWidget, QObject *parent, const QVariantList & args ): KParts::ReadWritePart(parent), + m_searchBar(nullptr), + m_initProgressDlg(nullptr), + m_showProgressDlg(true), + m_showBackendHelp(nullptr), + m_statusBarBlocked(false) { - m_showBackendHelp=nullptr; - m_initProgressDlg=nullptr; - m_statusBarBlocked=false; - m_showProgressDlg=true; - qDebug()<<"Created a CantorPart"; m_panelHandler=new Cantor::PanelPluginHandler(this); connect(m_panelHandler, SIGNAL(pluginsChanged()), this, SLOT(pluginsChanged())); QString backendName; if(args.isEmpty()) backendName=QLatin1String("null"); else backendName=args.first().toString(); foreach(const QVariant& arg, args) { if (arg.toString() == QLatin1String("--noprogress") ) { qWarning()<<"not showing the progress bar by request"; m_showProgressDlg=false; } } Cantor::Backend* b=Cantor::Backend::createBackend(backendName); if(!b) { KMessageBox::error(parentWidget, i18n("Backend %1 is not installed", backendName), i18n("Error - Cantor")); setWidget(new QWidget(parentWidget)); //fake being modified so the shell won't try to reuse this part ReadWritePart::setModified(true); return; } qDebug()<<"Backend "<name()<<" offers extensions: "<extensions(); QWidget* widget = new QWidget(parentWidget); QVBoxLayout* layout = new QVBoxLayout(widget); m_worksheet=new Worksheet(b, widget); m_worksheetview=new WorksheetView(m_worksheet, widget); m_worksheetview->setEnabled(false); //disable input until the session has successfully logged in and emits the ready signal connect(m_worksheet, SIGNAL(modified()), this, SLOT(setModified())); connect(m_worksheet, SIGNAL(showHelp(const QString&)), this, SIGNAL(showHelp(const QString&))); connect(m_worksheet, SIGNAL(sessionChanged()), this, SLOT(worksheetSessionChanged())); connect(actionCollection(), SIGNAL(inserted(QAction*)), m_worksheet, SLOT(registerShortcut(QAction*))); - m_searchBar = nullptr; layout->addWidget(m_worksheetview); // notify the part that this is our internal widget setWidget(widget); Cantor::WorksheetAccessInterface* iface=new WorksheetAccessInterfaceImpl(this, m_worksheet); // create our actions m_worksheet->createActions(actionCollection()); KStandardAction::saveAs(this, SLOT(fileSaveAs()), actionCollection()); m_save = KStandardAction::save(this, SLOT(save()), actionCollection()); m_save->setPriority(QAction::LowPriority); QAction * savePlain=new QAction(i18n("Save Plain Text"), actionCollection()); actionCollection()->addAction(QLatin1String("file_save_plain"), savePlain); savePlain->setIcon(QIcon::fromTheme(QLatin1String("document-save"))); connect(savePlain, SIGNAL(triggered()), this, SLOT(fileSavePlain())); QAction * undo=KStandardAction::undo(m_worksheet, SIGNAL(undo()), actionCollection()); undo->setPriority(QAction::LowPriority); connect(m_worksheet, SIGNAL(undoAvailable(bool)), undo, SLOT(setEnabled(bool))); QAction * redo=KStandardAction::redo(m_worksheet, SIGNAL(redo()), actionCollection()); redo->setPriority(QAction::LowPriority); connect(m_worksheet, SIGNAL(redoAvailable(bool)), redo, SLOT(setEnabled(bool))); QAction * cut=KStandardAction::cut(m_worksheet, SIGNAL(cut()), actionCollection()); cut->setPriority(QAction::LowPriority); connect(m_worksheet, SIGNAL(cutAvailable(bool)), cut, SLOT(setEnabled(bool))); QAction * copy=KStandardAction::copy(m_worksheet, SIGNAL(copy()), actionCollection()); copy->setPriority(QAction::LowPriority); connect(m_worksheet, SIGNAL(copyAvailable(bool)), copy, SLOT(setEnabled(bool))); QAction * paste=KStandardAction::paste(m_worksheet, SIGNAL(paste()), actionCollection()); paste->setPriority(QAction::LowPriority); connect(m_worksheet, SIGNAL(pasteAvailable(bool)), paste, SLOT(setEnabled(bool))); QAction * find=KStandardAction::find(this, SLOT(showSearchBar()), actionCollection()); find->setPriority(QAction::LowPriority); QAction * replace=KStandardAction::replace(this, SLOT(showExtendedSearchBar()), actionCollection()); replace->setPriority(QAction::LowPriority); m_findNext = KStandardAction::findNext(this, SLOT(findNext()), actionCollection()); m_findNext->setEnabled(false); m_findPrev = KStandardAction::findPrev(this, SLOT(findPrev()), actionCollection()); m_findPrev->setEnabled(false); QAction * latexExport=new QAction(i18n("Export to LaTeX"), actionCollection()); actionCollection()->addAction(QLatin1String("file_export_latex"), latexExport); latexExport->setIcon(QIcon::fromTheme(QLatin1String("document-export"))); connect(latexExport, SIGNAL(triggered()), this, SLOT(exportToLatex())); QAction * print = KStandardAction::print(this, SLOT(print()), actionCollection()); print->setPriority(QAction::LowPriority); QAction * printPreview = KStandardAction::printPreview(this, SLOT(printPreview()), actionCollection()); printPreview->setPriority(QAction::LowPriority); KStandardAction::zoomIn(m_worksheetview, SLOT(zoomIn()), actionCollection()); KStandardAction::zoomOut(m_worksheetview, SLOT(zoomOut()), actionCollection()); m_evaluate=new QAction(i18n("Evaluate Worksheet"), actionCollection()); actionCollection()->addAction(QLatin1String("evaluate_worksheet"), m_evaluate); m_evaluate->setIcon(QIcon::fromTheme(QLatin1String("system-run"))); connect(m_evaluate, SIGNAL(triggered()), this, SLOT(evaluateOrInterrupt())); m_typeset=new KToggleAction(i18n("Typeset using LaTeX"), actionCollection()); m_typeset->setChecked(Settings::self()->typesetDefault()); actionCollection()->addAction(QLatin1String("enable_typesetting"), m_typeset); connect(m_typeset, SIGNAL(toggled(bool)), this, SLOT(enableTypesetting(bool))); m_highlight=new KToggleAction(i18n("Syntax Highlighting"), actionCollection()); m_highlight->setChecked(Settings::self()->highlightDefault()); actionCollection()->addAction(QLatin1String("enable_highlighting"), m_highlight); connect(m_highlight, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableHighlighting(bool))); m_completion=new KToggleAction(i18n("Completion"), actionCollection()); m_completion->setChecked(Settings::self()->completionDefault()); actionCollection()->addAction(QLatin1String("enable_completion"), m_completion); connect(m_completion, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableCompletion(bool))); m_exprNumbering=new KToggleAction(i18n("Line Numbers"), actionCollection()); m_exprNumbering->setChecked(Settings::self()->expressionNumberingDefault()); actionCollection()->addAction(QLatin1String("enable_expression_numbers"), m_exprNumbering); connect(m_exprNumbering, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableExpressionNumbering(bool))); m_animateWorksheet=new KToggleAction(i18n("Animate Worksheet"), actionCollection()); m_animateWorksheet->setChecked(Settings::self()->animationDefault()); actionCollection()->addAction(QLatin1String("enable_animations"), m_animateWorksheet); connect(m_animateWorksheet, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableAnimations(bool))); QAction * restart=new QAction(i18n("Restart Backend"), actionCollection()); actionCollection()->addAction(QLatin1String("restart_backend"), restart); restart->setIcon(QIcon::fromTheme(QLatin1String("system-reboot"))); connect(restart, SIGNAL(triggered()), this, SLOT(restartBackend())); QAction * evaluateCurrent=new QAction(i18n("Evaluate Entry"), actionCollection()); actionCollection()->addAction(QLatin1String("evaluate_current"), evaluateCurrent); actionCollection()->setDefaultShortcut(evaluateCurrent, Qt::SHIFT + Qt::Key_Return); connect(evaluateCurrent, SIGNAL(triggered()), m_worksheet, SLOT(evaluateCurrentEntry())); QAction * insertCommandEntry=new QAction(i18n("Insert Command Entry"), actionCollection()); actionCollection()->addAction(QLatin1String("insert_command_entry"), insertCommandEntry); actionCollection()->setDefaultShortcut(insertCommandEntry, Qt::CTRL + Qt::Key_Return); connect(insertCommandEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertCommandEntry())); QAction * insertTextEntry=new QAction(i18n("Insert Text Entry"), actionCollection()); actionCollection()->addAction(QLatin1String("insert_text_entry"), insertTextEntry); connect(insertTextEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertTextEntry())); QAction * insertLatexEntry=new QAction(i18n("Insert Latex Entry"), actionCollection()); actionCollection()->addAction(QLatin1String("insert_latex_entry"), insertLatexEntry); connect(insertLatexEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertLatexEntry())); QAction * insertPageBreakEntry=new QAction(i18n("Insert Page Break"), actionCollection()); actionCollection()->addAction(QLatin1String("insert_page_break_entry"), insertPageBreakEntry); connect(insertPageBreakEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertPageBreakEntry())); QAction * insertImageEntry=new QAction(i18n("Insert Image"), actionCollection()); actionCollection()->addAction(QLatin1String("insert_image_entry"), insertImageEntry); connect(insertImageEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertImageEntry())); /* QAction * insertCommandEntryBefore=new QAction(i18n("Insert Command Entry Before"), actionCollection()); //insertCommandEntryBefore->setShortcut(Qt::CTRL + Qt::Key_Return); actionCollection()->addAction("insert_command_entry_before", insertCommandEntryBefore); connect(insertCommandEntryBefore, SIGNAL(triggered()), m_worksheet, SLOT(insertCommandEntryBefore())); QAction * insertTextEntryBefore=new QAction(i18n("Insert Text Entry Before"), actionCollection()); //insertTextEntryBefore->setShortcut(Qt::CTRL + Qt::Key_Return); actionCollection()->addAction("insert_text_entry_before", insertTextEntryBefore); connect(insertTextEntryBefore, SIGNAL(triggered()), m_worksheet, SLOT(insertTextEntryBefore())); QAction * insertPageBreakEntryBefore=new QAction(i18n("Insert Page Break Before"), actionCollection()); actionCollection()->addAction("insert_page_break_entry_before", insertPageBreakEntryBefore); connect(insertPageBreakEntryBefore, SIGNAL(triggered()), m_worksheet, SLOT(insertPageBreakEntryBefore())); QAction * insertImageEntryBefore=new QAction(i18n("Insert Image Entry Before"), actionCollection()); //insertTextEntryBefore->setShortcut(Qt::CTRL + Qt::Key_Return); actionCollection()->addAction("insert_image_entry_before", insertImageEntryBefore); connect(insertImageEntryBefore, SIGNAL(triggered()), m_worksheet, SLOT(insertImageEntryBefore())); */ QAction * removeCurrent=new QAction(i18n("Remove current Entry"), actionCollection()); actionCollection()->addAction(QLatin1String("remove_current"), removeCurrent); actionCollection()->setDefaultShortcut(removeCurrent, Qt::ShiftModifier + Qt::Key_Delete); connect(removeCurrent, SIGNAL(triggered()), m_worksheet, SLOT(removeCurrentEntry())); m_showBackendHelp=new QAction(i18n("Show %1 Help", b->name()) , actionCollection()); m_showBackendHelp->setIcon(QIcon::fromTheme(QLatin1String("help-contents"))); actionCollection()->addAction(QLatin1String("backend_help"), m_showBackendHelp); connect(m_showBackendHelp, SIGNAL(triggered()), this, SLOT(showBackendHelp())); QAction * publishWorksheet=new QAction(i18n("Publish Worksheet"), actionCollection()); publishWorksheet->setIcon(QIcon::fromTheme(QLatin1String("get-hot-new-stuff"))); actionCollection()->addAction(QLatin1String("file_publish_worksheet"), publishWorksheet); connect(publishWorksheet, SIGNAL(triggered()), this, SLOT(publishWorksheet())); KToggleAction* showEditor=new KToggleAction(i18n("Show Script Editor"), actionCollection()); showEditor->setChecked(false); actionCollection()->addAction(QLatin1String("show_editor"), showEditor); connect(showEditor, SIGNAL(toggled(bool)), this, SLOT(showScriptEditor(bool))); showEditor->setEnabled(b->extensions().contains(QLatin1String("ScriptExtension"))); QAction * showCompletion=new QAction(i18n("Show Completion"), actionCollection()); actionCollection()->addAction(QLatin1String("show_completion"), showCompletion); QList showCompletionShortcuts; showCompletionShortcuts << Qt::Key_Tab << Qt::CTRL + Qt::Key_Space; actionCollection()->setDefaultShortcuts(showCompletion, showCompletionShortcuts); connect(showCompletion, SIGNAL(triggered()), m_worksheet, SLOT(showCompletion())); // set our XML-UI resource file setXMLFile(QLatin1String("cantor_part.rc")); // we are read-write by default setReadWrite(true); // we are not modified since we haven't done anything yet setModified(false); worksheetSessionChanged(); } CantorPart::~CantorPart() { if (m_scriptEditor) { disconnect(m_scriptEditor, SIGNAL(destroyed()), this, SLOT(scriptEditorClosed())); delete m_scriptEditor; } if (m_searchBar) delete m_searchBar; } void CantorPart::setReadWrite(bool rw) { // notify your internal widget of the read-write state m_worksheetview->setInteractive(rw); ReadWritePart::setReadWrite(rw); } void CantorPart::setModified(bool modified) { // get a handle on our Save action and make sure it is valid if (!m_save) return; // if so, we either enable or disable it based on the current // state if (modified) m_save->setEnabled(true); else m_save->setEnabled(false); // in any event, we want our parent to do it's thing ReadWritePart::setModified(modified); } KAboutData& CantorPart::createAboutData() { // the non-i18n name here must be the same as the directory in // which the part's rc file is installed ('partrcdir' in the // Makefile) static KAboutData about(QLatin1String("cantorpart"), QLatin1String("Cantor"), QLatin1String(CANTOR_VERSION), i18n("CantorPart"), KAboutLicense::GPL, i18n("(C) 2009-2015 Alexander Rieder"), QString(), QLatin1String("http://edu.kde.org/cantor")); about.addAuthor( i18n("Alexander Rieder"), QString(), QLatin1String("alexanderrieder@gmail.com") ); return about; } bool CantorPart::openFile() { //don't crash if for some reason the worksheet is invalid if(m_worksheet==nullptr) { qWarning()<<"trying to open in an invalid cantor part"; return false; } m_worksheet->load(localFilePath()); // just for fun, set the status bar //setStatusMessage( m_url.prettyUrl() ); updateCaption(); return true; } bool CantorPart::saveFile() { // if we aren't read-write, return immediately if (isReadWrite() == false) return false; qDebug()<<"saving to: "<save( localFilePath() ); setModified(false); return true; } void CantorPart::fileSaveAs() { // this slot is called whenever the File->Save As menu is selected QString worksheetFilter = i18n("Cantor Worksheet (*.cws)"); QString filter = worksheetFilter; //if the backend supports scripts, also append their scriptFile endings to the filter Cantor::Backend * const backend=m_worksheet->session()->backend(); if (backend->extensions().contains(QLatin1String("ScriptExtension"))) { Cantor::ScriptExtension* e=dynamic_cast(backend->extension(QLatin1String("ScriptExtension"))); filter+=QLatin1Char('\n')+e->scriptFileFilter(); } QString selectedFilter; QString file_name = QFileDialog::getSaveFileName(widget(), i18n("Save as"), QString(), filter, &selectedFilter); if (file_name.isEmpty()) return; //depending on user's selection, save as a worksheet or as a plain script file if (selectedFilter == worksheetFilter) saveAs(QUrl::fromLocalFile(file_name)); else m_worksheet->savePlain(file_name); updateCaption(); } void CantorPart::fileSavePlain() { QString file_name = QFileDialog::getSaveFileName(widget(), i18n("Save"), QString(), QString()); if (!file_name.isEmpty()) m_worksheet->savePlain(file_name); } void CantorPart::exportToLatex() { // this slot is called whenever the File->Save As menu is selected, QString filter=i18n("*.tex|LaTeX Document"); QString file_name = QFileDialog::getSaveFileName(widget(), i18n("Export to LaTeX"), QString(), QString()); if (file_name.isEmpty() == false) m_worksheet->saveLatex(file_name); } void CantorPart::guiActivateEvent( KParts::GUIActivateEvent * event ) { KParts::ReadWritePart::guiActivateEvent(event); if(event->activated()) { if(m_scriptEditor) m_scriptEditor->show(); }else { if(m_scriptEditor) m_scriptEditor->hide(); } } void CantorPart::evaluateOrInterrupt() { qDebug()<<"evalorinterrupt"; if(m_worksheet->isRunning()) m_worksheet->interrupt(); else m_worksheet->evaluate(); } void CantorPart::restartBackend() { m_worksheet->session()->logout(); m_worksheet->session()->login(); } void CantorPart::worksheetStatusChanged(Cantor::Session::Status status) { qDebug()<<"wsStatusChange"<setText(i18n("Interrupt")); m_evaluate->setIcon(QIcon::fromTheme(QLatin1String("dialog-close"))); setStatusMessage(i18n("Calculating...")); }else { m_evaluate->setText(i18n("Evaluate Worksheet")); m_evaluate->setIcon(QIcon::fromTheme(QLatin1String("system-run"))); setStatusMessage(i18n("Ready")); } } void CantorPart::showSessionError(const QString& message) { qDebug()<<"Error: "<session(), SIGNAL(statusChanged(Cantor::Session::Status)), this, SLOT(worksheetStatusChanged(Cantor::Session::Status))); connect(m_worksheet->session(), SIGNAL(loginStarted()),this, SLOT(worksheetSessionLoginStarted())); connect(m_worksheet->session(), SIGNAL(loginDone()),this, SLOT(worksheetSessionLoginDone())); connect(m_worksheet->session(), SIGNAL(error(const QString&)), this, SLOT(showSessionError(const QString&))); loadAssistants(); m_panelHandler->setSession(m_worksheet->session()); adjustGuiToSession(); initialized(); } void CantorPart::initialized() { if (m_worksheet->isEmpty()) m_worksheet->appendCommandEntry(); m_worksheetview->setEnabled(true); m_worksheetview->setFocus(); setStatusMessage(i18n("Initialization complete")); updateCaption(); } void CantorPart::worksheetSessionLoginStarted() { setStatusMessage(i18n("Initializing...")); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); } void CantorPart::worksheetSessionLoginDone() { QApplication::restoreOverrideCursor(); } void CantorPart::enableTypesetting(bool enable) { m_worksheet->session()->setTypesettingEnabled(enable); } void CantorPart::showBackendHelp() { qDebug()<<"showing backends help"; Cantor::Backend* backend=m_worksheet->session()->backend(); QUrl url = backend->helpUrl(); qDebug()<<"launching url "<session()->backend()->name(), filename)); } void CantorPart::pluginsChanged() { foreach(Cantor::PanelPlugin* plugin, m_panelHandler->plugins()) { connect(plugin, SIGNAL(requestRunCommand(QString)), this, SLOT(runCommand(QString))); } } void CantorPart::loadAssistants() { qDebug()<<"loading assistants..."; QStringList assistantDirs; foreach(const QString &dir, QCoreApplication::libraryPaths()){ assistantDirs << dir + QDir::separator() + QLatin1String("cantor/assistants"); } QPluginLoader loader; foreach(const QString &dir, assistantDirs){ qDebug() << "dir: " << dir; QStringList assistants; QDir assistantDir = QDir(dir); assistants = assistantDir.entryList(); foreach (const QString &assistant, assistants){ if (assistant==QLatin1String(".") || assistant==QLatin1String("..")) continue; loader.setFileName(dir + QDir::separator() + assistant); if (!loader.load()){ qDebug() << "Error while loading assistant: " << assistant; continue; } KPluginFactory* factory = KPluginLoader(loader.fileName()).factory(); Cantor::Assistant* plugin = factory->create(this); Cantor::Backend* backend=worksheet()->session()->backend(); KPluginMetaData info(loader); plugin->setPluginInfo(info); plugin->setBackend(backend); qDebug()<<"plugin "<requiredExtensions(); bool supported=true; foreach(const QString& req, plugin->requiredExtensions()) supported=supported && backend->extensions().contains(req); qDebug()<<"plugin "<name(); if(supported) { plugin->initActions(); connect(plugin, SIGNAL(requested()), this, SLOT(runAssistant())); }else { removeChildClient(plugin); plugin->deleteLater(); } } } } void CantorPart::runAssistant() { Cantor::Assistant* a=qobject_cast(sender()); QStringList cmds=a->run(widget()); qDebug()<appendCommandEntry(cmd); } void CantorPart::showSearchBar() { if (!m_searchBar) { m_searchBar = new SearchBar(widget(), m_worksheet); widget()->layout()->addWidget(m_searchBar); connect(m_searchBar, SIGNAL(destroyed(QObject*)), this, SLOT(searchBarDeleted())); } m_findNext->setEnabled(true); m_findPrev->setEnabled(true); m_searchBar->showStandard(); m_searchBar->setFocus(); } void CantorPart::showExtendedSearchBar() { if (!m_searchBar) { m_searchBar = new SearchBar(widget(), m_worksheet); widget()->layout()->addWidget(m_searchBar); connect(m_searchBar, SIGNAL(destroyed(QObject*)), this, SLOT(searchBarDeleted())); } m_findNext->setEnabled(true); m_findPrev->setEnabled(true); m_searchBar->showExtended(); m_searchBar->setFocus(); } void CantorPart::findNext() { if (m_searchBar) m_searchBar->next(); } void CantorPart::findPrev() { if (m_searchBar) m_searchBar->prev(); } void CantorPart::searchBarDeleted() { m_searchBar = nullptr; m_findNext->setEnabled(false); m_findPrev->setEnabled(false); } void CantorPart::adjustGuiToSession() { #ifdef WITH_EPS m_typeset->setVisible(m_worksheet->session()->backend()->capabilities().testFlag(Cantor::Backend::LaTexOutput)); #else m_typeset->setVisible(false); #endif m_completion->setVisible(m_worksheet->session()->backend()->capabilities().testFlag(Cantor::Backend::Completion)); //this is 0 on the first call if(m_showBackendHelp) m_showBackendHelp->setText(i18n("Show %1 Help", m_worksheet->session()->backend()->name())); } void CantorPart::publishWorksheet() { int ret = KMessageBox::questionYesNo(widget(), i18n("Do you want to upload current Worksheet to public web server?"), i18n("Question - Cantor")); if (ret != KMessageBox::Yes) return; if (isModified()||url().isEmpty()) { ret = KMessageBox::warningContinueCancel(widget(), i18n("The Worksheet is not saved. You should save it before uploading."), i18n("Warning - Cantor"), KStandardGuiItem::save(), KStandardGuiItem::cancel()); if (ret != KMessageBox::Continue) return; if (!saveFile()) return; } qDebug()<<"uploading file "<session()->backend()->id().toLower()), widget()); dialog.setUploadFile(url()); dialog.exec(); } void CantorPart::print() { QPrinter printer; QPointer dialog = new QPrintDialog(&printer, widget()); // TODO: Re-enable print selection //if (m_worksheet->textCursor().hasSelection()) // dialog->addEnabledOption(QAbstractPrintDialog::PrintSelection); if (dialog->exec() == QDialog::Accepted) m_worksheet->print(&printer); delete dialog; } void CantorPart::printPreview() { QPrintPreviewDialog *dialog = new QPrintPreviewDialog(widget()); connect(dialog, SIGNAL(paintRequested(QPrinter*)), m_worksheet, SLOT(print(QPrinter*))); dialog->exec(); } void CantorPart::showScriptEditor(bool show) { if(show) { if (m_scriptEditor) { return; } Cantor::ScriptExtension* scriptE=dynamic_cast(m_worksheet->session()->backend()->extension(QLatin1String("ScriptExtension"))); if (!scriptE) { return; } m_scriptEditor=new ScriptEditorWidget(scriptE->scriptFileFilter(), scriptE->highlightingMode(), widget()->window()); connect(m_scriptEditor, SIGNAL(runScript(const QString&)), this, SLOT(runScript(const QString&))); connect(m_scriptEditor, SIGNAL(destroyed()), this, SLOT(scriptEditorClosed())); m_scriptEditor->show(); }else { m_scriptEditor->deleteLater(); } } void CantorPart::scriptEditorClosed() { QAction* showEditor = actionCollection()->action(QLatin1String("show_editor")); if (showEditor) { showEditor->setChecked(false); } } void CantorPart::runScript(const QString& file) { Cantor::Backend* backend=m_worksheet->session()->backend(); if(!backend->extensions().contains(QLatin1String("ScriptExtension"))) { KMessageBox::error(widget(), i18n("This backend does not support scripts."), i18n("Error - Cantor")); return; } Cantor::ScriptExtension* scriptE=dynamic_cast(backend->extension(QLatin1String("ScriptExtension"))); m_worksheet->appendCommandEntry(scriptE->runExternalScript(file)); } void CantorPart::blockStatusBar() { m_statusBarBlocked=true; } void CantorPart::unblockStatusBar() { m_statusBarBlocked=false; if(!m_cachedStatusMessage.isNull()) setStatusMessage(m_cachedStatusMessage); m_cachedStatusMessage.clear(); } void CantorPart::setStatusMessage(const QString& message) { if(!m_statusBarBlocked) emit setStatusBarText(message); else m_cachedStatusMessage=message; } void CantorPart::showImportantStatusMessage(const QString& message) { setStatusMessage(message); blockStatusBar(); QTimer::singleShot(3000, this, SLOT(unblockStatusBar())); } K_PLUGIN_FACTORY_WITH_JSON(CantorPartFactory, "cantor_part.json", registerPlugin();) #include "cantor_part.moc" diff --git a/src/cantor_part.h b/src/cantor_part.h index 129151ea..bf58617d 100644 --- a/src/cantor_part.h +++ b/src/cantor_part.h @@ -1,183 +1,180 @@ /* 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 CANTORPART_H #define CANTORPART_H #include #include #include class QWidget; class Worksheet; class WorksheetView; class SarchBar; class SearchBar; class ScriptEditorWidget; class KAboutData; class QAction; class KToggleAction; class QProgressDialog; namespace Cantor{ class PanelPluginHandler; } /** * This is a "Part". It that does all the real work in a KPart * application. * * @short Main Part * @author Alexander Rieder */ class CantorPart : public KParts::ReadWritePart { Q_OBJECT public: /** * Default constructor */ CantorPart(QWidget *parentWidget,QObject *parent, const QVariantList &args); /** * Destructor */ ~CantorPart() override; /** * This is a virtual function inherited from KParts::ReadWritePart. * A shell will use this to inform this Part if it should act * read-only */ void setReadWrite(bool rw) Q_DECL_OVERRIDE; /** * Reimplemented to disable and enable Save action */ void setModified(bool modified) Q_DECL_OVERRIDE; KAboutData& createAboutData(); Worksheet* worksheet(); Q_SIGNALS: void setCaption(const QString& caption); void showHelp(const QString& help); protected: /** * This must be implemented by each part */ bool openFile() Q_DECL_OVERRIDE; /** * This must be implemented by each read-write part */ bool saveFile() Q_DECL_OVERRIDE; /** * Called when this part becomes the active one, * or if it looses activity **/ void guiActivateEvent( KParts::GUIActivateEvent * event ) Q_DECL_OVERRIDE; void loadAssistants(); void adjustGuiToSession(); protected Q_SLOTS: void fileSaveAs(); void fileSavePlain(); void exportToLatex(); void evaluateOrInterrupt(); void restartBackend(); void enableTypesetting(bool enable); void showBackendHelp(); void print(); void printPreview(); void worksheetStatusChanged(Cantor::Session::Status stauts); void showSessionError(const QString& error); void worksheetSessionChanged(); void worksheetSessionLoginStarted(); void worksheetSessionLoginDone(); void initialized(); void updateCaption(); void pluginsChanged(); void runCommand(const QString& value); void runAssistant(); void publishWorksheet(); void showScriptEditor(bool show); void scriptEditorClosed(); void runScript(const QString& file); void showSearchBar(); void showExtendedSearchBar(); void findNext(); void findPrev(); void searchBarDeleted(); /** sets the status message, or cached it, if the StatusBar is blocked. * Use this method instead of "emit setStatusBarText" */ void setStatusMessage(const QString& message); /** Shows an important status message. It makes sure the message is displayed, * by blocking the statusbarText for 3 seconds */ void showImportantStatusMessage(const QString& message); /** Blocks the StatusBar for new messages, so the currently shown one won't be overridden */ void blockStatusBar(); /** Removes the block from the StatusBar, and shows the last one of the StatusMessages that where set during the block **/ void unblockStatusBar(); private: Worksheet *m_worksheet; WorksheetView *m_worksheetview; SearchBar *m_searchBar; QPointer m_scriptEditor; Cantor::PanelPluginHandler* m_panelHandler; QProgressDialog* m_initProgressDlg; bool m_showProgressDlg; QAction * m_evaluate; QAction * m_save; QAction * m_findNext; QAction * m_findPrev; KToggleAction* m_typeset; KToggleAction* m_highlight; KToggleAction* m_completion; KToggleAction* m_exprNumbering; KToggleAction* m_animateWorksheet; QAction * m_showBackendHelp; QString m_cachedStatusMessage; bool m_statusBarBlocked; - }; - - #endif // CANTORPART_H diff --git a/src/lib/expression.cpp b/src/lib/expression.cpp index 61e9c16d..ae6bdf34 100644 --- a/src/lib/expression.cpp +++ b/src/lib/expression.cpp @@ -1,281 +1,280 @@ /* 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 "expression.h" using namespace Cantor; #include #include "session.h" #include "result.h" #include "textresult.h" #include "imageresult.h" #include "latexresult.h" #include "settings.h" #include "latexrenderer.h" #include #include #include #include #include class Cantor::ExpressionPrivate { public: - ExpressionPrivate() { - result=nullptr; - session=nullptr; - isInternal=false; + ExpressionPrivate() : result(nullptr), status(Expression::Done), session(nullptr), + finishingBehavior(Expression::DoNotDelete), isInternal(false) + { } int id; QString command; QString error; QList information; Result* result; Expression::Status status; Session* session; Expression::FinishingBehavior finishingBehavior; bool isInternal; }; static const QString tex=QLatin1String("\\documentclass[12pt,fleqn]{article} \n "\ "\\usepackage{latexsym,amsfonts,amssymb,ulem} \n "\ "\\usepackage[dvips]{graphicx} \n "\ "\\setlength\\textwidth{5in} \n "\ "\\setlength{\\parindent}{0pt} \n "\ "%1 \n "\ "\\pagestyle{empty} \n "\ "\\begin{document} \n "\ "%2 \n "\ "\\end{document}\n"); Expression::Expression( Session* session ) : QObject( session ), d(new ExpressionPrivate) { d->session=session; d->id=session->nextExpressionId(); } Expression::~Expression() { delete d->result; delete d; } void Expression::setCommand(const QString& command) { d->command=command; } QString Expression::command() { return d->command; } void Expression::setErrorMessage(const QString& error) { d->error=error; } QString Expression::errorMessage() { return d->error; } void Expression::setResult(Result* result) { if(d->result) delete d->result; d->result=result; if(result!=nullptr) { qDebug()<<"settting result to a type "<type()<<" result"; #ifdef WITH_EPS //If it's text, and latex typesetting is enabled, render it if ( session()->isTypesettingEnabled()&& result->type()==TextResult::Type && dynamic_cast(result)->format()==TextResult::LatexFormat && !result->toHtml().trimmed().isEmpty() && finishingBehavior()!=DeleteOnFinish && !isInternal() ) { renderResultAsLatex(); } #endif } emit gotResult(); } Result* Expression::result() { return d->result; } void Expression::clearResult() { if(d->result) delete d->result; d->result=nullptr; } void Expression::setStatus(Expression::Status status) { d->status=status; emit statusChanged(status); if(status==Expression::Done&&d->finishingBehavior==Expression::DeleteOnFinish) deleteLater(); } Expression::Status Expression::status() { return d->status; } Session* Expression::session() { return d->session; } void Expression::renderResultAsLatex() { qDebug()<<"rendering as latex"; qDebug()<<"checking if it really is a formula that can be typeset"; LatexRenderer* renderer=new LatexRenderer(this); renderer->setLatexCode(result()->data().toString().trimmed()); renderer->addHeader(additionalLatexHeaders()); connect(renderer, &LatexRenderer::done, this, &Expression::latexRendered); connect(renderer, &LatexRenderer::error, this, &Expression::latexRendered); renderer->render(); } void Expression::latexRendered() { LatexRenderer* renderer=qobject_cast(sender()); qDebug()<<"rendered a result to "<imagePath(); //replace the textresult with the rendered latex image result //ImageResult* latex=new ImageResult( d->latexFilename ); if(renderer->renderingSuccessful()&&result()) { if (result()->type() == TextResult::Type) { TextResult* r=dynamic_cast(result()); LatexResult* latex=new LatexResult(r->data().toString().trimmed(), QUrl::fromLocalFile(renderer->imagePath()), r->plain()); setResult( latex ); } else if (result()->type() == LatexResult::Type) { LatexResult* previousLatexResult=dynamic_cast(result()); LatexResult* latex=new LatexResult(previousLatexResult->data().toString().trimmed(), QUrl::fromLocalFile(renderer->imagePath()), previousLatexResult->plain()); setResult( latex ); } }else { //if rendering with latex was not successful, just use the plain text version //if available TextResult* r=dynamic_cast(result()); setResult(new TextResult(r->plain())); qDebug()<<"error rendering latex: "<errorMessage(); } renderer->deleteLater(); } //saving code QDomElement Expression::toXml(QDomDocument& doc) { QDomElement expr=doc.createElement( QLatin1String("Expression") ); QDomElement cmd=doc.createElement( QLatin1String("Command") ); QDomText cmdText=doc.createTextNode( command() ); cmd.appendChild( cmdText ); expr.appendChild( cmd ); if ( result() ) { qDebug()<<"result: "<toXml( doc ); expr.appendChild( resXml ); } return expr; } void Expression::saveAdditionalData(KZip* archive) { //just pass this call to the result if(result()) result()->saveAdditionalData(archive); } void Expression::addInformation(const QString& information) { d->information.append(information); } QString Expression::additionalLatexHeaders() { return QString(); } int Expression::id() { return d->id; } void Expression::setId(int id) { d->id=id; emit idChanged(); } void Expression::setFinishingBehavior(Expression::FinishingBehavior behavior) { d->finishingBehavior=behavior; } Expression::FinishingBehavior Expression::finishingBehavior() { return d->finishingBehavior; } void Expression::setInternal(bool internal) { d->isInternal=internal; } bool Expression::isInternal() { return d->isInternal; } diff --git a/src/lib/session.cpp b/src/lib/session.cpp index 9795b109..a223ca0f 100644 --- a/src/lib/session.cpp +++ b/src/lib/session.cpp @@ -1,125 +1,121 @@ /* 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 "session.h" using namespace Cantor; #include "backend.h" class Cantor::SessionPrivate { public: - SessionPrivate() + SessionPrivate() : backend(nullptr), status(Session::Done), typesettingEnabled(false), expressionCount(0) { - backend=nullptr; - expressionCount=0; - typesettingEnabled=false; } Backend* backend; Session::Status status; bool typesettingEnabled; int expressionCount; }; -Session::Session( Backend* backend ) : QObject(backend), - d(new SessionPrivate) +Session::Session( Backend* backend ) : QObject(backend), d(new SessionPrivate) { d->backend=backend; } Session::~Session() { delete d; } Expression* Session::evaluateExpression(const QString& command) { return evaluateExpression(command, Expression::DoNotDelete); } Backend* Session::backend() { return d->backend; } Cantor::Session::Status Session::status() { return d->status; } void Session::changeStatus(Session::Status newStatus) { d->status=newStatus; emit statusChanged(newStatus); } void Session::setTypesettingEnabled(bool enable) { d->typesettingEnabled=enable; } bool Session::isTypesettingEnabled() { return d->typesettingEnabled; } CompletionObject* Session::completionFor(const QString& cmd, int index) { Q_UNUSED(cmd); Q_UNUSED(index); //Return 0 per default, so Backends not offering tab completions don't have //to reimplement this. This method should only be called on backends with //the Completion Capability flag return nullptr; } SyntaxHelpObject* Session::syntaxHelpFor(const QString& cmd) { Q_UNUSED(cmd); //Return 0 per default, so Backends not offering tab completions don't have //to reimplement this. This method should only be called on backends with //the SyntaxHelp Capability flag return nullptr; } QSyntaxHighlighter* Session::syntaxHighlighter(QObject* parent) { Q_UNUSED(parent); return nullptr; } QAbstractItemModel* Session::variableModel() { //Return 0 per default, so Backends not offering variable management don't //have to reimplement this. This method should only be called on backends with //VariableManagement Capability flag return nullptr; } int Session::nextExpressionId() { return d->expressionCount++; }