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

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2009 Alexander Rieder <alexanderrieder@gmail.com>
0004 */
0005 
0006 #include "expression.h"
0007 #include "latexrenderer.h"
0008 using namespace Cantor;
0009 
0010 #include <config-cantorlib.h>
0011 
0012 #include "session.h"
0013 #include "result.h"
0014 #include "textresult.h"
0015 #include "imageresult.h"
0016 #include "latexresult.h"
0017 #include "settings.h"
0018 
0019 #include <QDebug>
0020 #include <QFileInfo>
0021 #include <QString>
0022 #include <QFileSystemWatcher>
0023 
0024 #include <KProcess>
0025 #include <KZip>
0026 
0027 class Cantor::ExpressionPrivate
0028 {
0029 public:
0030     ExpressionPrivate() : id(-1) {}
0031 
0032     int id{-1};
0033     QString command;
0034     QString error;
0035     QList<QString> information;
0036     QVector<Result*> results;
0037     Expression::Status status{Expression::Done};
0038     Session* session{nullptr};
0039     Expression::FinishingBehavior finishingBehavior{Expression::DoNotDelete};
0040     bool internal{false};
0041     bool helpRequest{false};
0042     QFileSystemWatcher* fileWatcher{nullptr};
0043 };
0044 
0045 static const QString tex=QLatin1String("\\documentclass[12pt,fleqn]{article}          \n "\
0046                          "\\usepackage{latexsym,amsfonts,amssymb,ulem}  \n "\
0047                          "\\usepackage[dvips]{graphicx}                 \n "\
0048                          "\\setlength\\textwidth{5in}                   \n "\
0049                          "\\setlength{\\parindent}{0pt}                 \n "\
0050                          "%1                                            \n "\
0051                          "\\pagestyle{empty}                            \n "\
0052                          "\\begin{document}                             \n "\
0053                          "%2                                            \n "\
0054                          "\\end{document}\n");
0055 
0056 
0057 Expression::Expression( Session* session, bool internal ) : QObject( session ),
0058                                              d(new ExpressionPrivate)
0059 {
0060     d->session=session;
0061     d->internal = internal;
0062     if (!internal && session)
0063         d->id=session->nextExpressionId();
0064     else
0065         d->id = -1;
0066 }
0067 
0068 Expression::Expression( Session* session, bool internal, int id ) : QObject( session ), d(new ExpressionPrivate)
0069 {
0070     d->session = session;
0071     d->internal = internal;
0072     d->id = id;
0073 }
0074 
0075 Expression::~Expression()
0076 {
0077     qDeleteAll(d->results);
0078     if (d->fileWatcher)
0079         delete d->fileWatcher;
0080 
0081     delete d;
0082 }
0083 
0084 void Expression::setCommand(const QString& command)
0085 {
0086     d->command=command;
0087 }
0088 
0089 QString Expression::command()
0090 {
0091     return d->command;
0092 }
0093 
0094 QString Expression::internalCommand()
0095 {
0096     return d->command;
0097 }
0098 
0099 void Expression::interrupt()
0100 {
0101     qDebug()<<"interrupting";
0102     setStatus(Cantor::Expression::Interrupted);
0103 }
0104 
0105 void Expression::setErrorMessage(const QString& error)
0106 {
0107     d->error=error;
0108 }
0109 
0110 QString Expression::errorMessage()
0111 {
0112     return d->error;
0113 }
0114 
0115 void Expression::setResult(Result* result)
0116 {
0117     clearResults();
0118     addResult(result);
0119 }
0120 
0121 void Expression::addResult(Result* result)
0122 {
0123     if(result!=nullptr)
0124     {
0125         qDebug()<<"setting result to a type "<<result->type()<<" result";
0126         #ifdef WITH_EPS
0127         //If it's text, and latex typesetting is enabled, render it
0128         if ( session() &&
0129              session()->isTypesettingEnabled()&&
0130              result->type()==TextResult::Type &&
0131              static_cast<TextResult*>(result)->format()==TextResult::LatexFormat &&
0132              !result->toHtml().trimmed().isEmpty() &&
0133              finishingBehavior()!=DeleteOnFinish &&
0134              !isInternal()
0135             )
0136         {
0137             renderResultAsLatex(result);
0138             return;
0139         }
0140         #endif
0141     }
0142 
0143     d->results << result;
0144     emit gotResult();
0145 }
0146 
0147 void Expression::clearResults()
0148 {
0149     qDeleteAll(d->results);
0150     d->results.clear();
0151     emit resultsCleared();
0152 }
0153 
0154 void Expression::removeResult(Result* result)
0155 {
0156     int index = d->results.indexOf(result);
0157     d->results.remove(index);
0158     delete result;
0159     emit resultRemoved(index);
0160 }
0161 
0162 void Expression::replaceResult(int index, Result* result)
0163 {
0164     if (result)
0165     {
0166         //insert the new result
0167         d->results.insert(index, result);
0168 
0169         //delete the previous result
0170         Result* oldResult = d->results.at(index+1);
0171         d->results.remove(index+1);
0172         delete oldResult;
0173 
0174         //notify about the replacement
0175         emit resultReplaced(index);
0176     }
0177 }
0178 
0179 Result* Expression::result()
0180 {
0181     if (!d->results.isEmpty())
0182         return d->results.first();
0183     else
0184         return nullptr;
0185 }
0186 
0187 const QVector<Result*>& Expression::results() const
0188 {
0189     return d->results;
0190 }
0191 
0192 void Expression::setStatus(Expression::Status status)
0193 {
0194     d->status=status;
0195     emit statusChanged(status);
0196 
0197     bool isFinished = status == Expression::Done || status == Expression::Error || status == Expression::Interrupted;
0198     if (isFinished)
0199     {
0200         emit expressionFinished(status);
0201         if(d->finishingBehavior==Expression::DeleteOnFinish)
0202             deleteLater();
0203     }
0204 }
0205 
0206 Expression::Status Expression::status()
0207 {
0208     return d->status;
0209 }
0210 
0211 Session* Expression::session()
0212 {
0213     return d->session;
0214 }
0215 void Expression::renderResultAsLatex(Result* result)
0216 {
0217     LatexRenderer* renderer=new LatexRenderer(this);
0218     renderer->setLatexCode(result->data().toString().trimmed());
0219     renderer->addHeader(additionalLatexHeaders());
0220 
0221     connect(renderer, &LatexRenderer::done, [=] { latexRendered(renderer, result); });
0222     connect(renderer, &LatexRenderer::error, [=] { latexRendered(renderer, result); });
0223 
0224     renderer->render();
0225 }
0226 
0227 void Expression::latexRendered(LatexRenderer* renderer, Result* result)
0228 {
0229     qDebug()<<"rendered a result to "<<renderer->imagePath();
0230     //replace the textresult with the rendered latex image result
0231     //ImageResult* latex=new ImageResult( d->latexFilename );
0232     if(renderer->renderingSuccessful())
0233     {
0234         if (result->type() == TextResult::Type)
0235         {
0236             TextResult* r = static_cast<TextResult*>(result);
0237             LatexResult* latex=new LatexResult(r->data().toString().trimmed(), QUrl::fromLocalFile(renderer->imagePath()), r->plain());
0238             addResult( latex );
0239         }
0240         else if (result->type() == LatexResult::Type)
0241         {
0242             LatexResult* previousLatexResult = static_cast<LatexResult*>(result);
0243             LatexResult* latex=new LatexResult(previousLatexResult->data().toString().trimmed(), QUrl::fromLocalFile(renderer->imagePath()), previousLatexResult->plain());
0244             addResult( latex );
0245         }
0246     }else
0247     {
0248         //if rendering with latex was not successful, just use the plain text version
0249         //if available
0250         TextResult* r=dynamic_cast<TextResult*>(result);
0251         if (r)
0252             addResult(new TextResult(r->plain()));
0253         qDebug()<<"error rendering latex: "<<renderer->errorMessage();
0254     }
0255 
0256     delete result;
0257 
0258     renderer->deleteLater();
0259 }
0260 
0261 void Expression::addInformation(const QString& information)
0262 {
0263     d->information.append(information);
0264 }
0265 
0266 QString Expression::additionalLatexHeaders()
0267 {
0268     return QString();
0269 }
0270 
0271 QFileSystemWatcher* Expression::fileWatcher() {
0272     if (!d->fileWatcher)
0273         d->fileWatcher = new QFileSystemWatcher();
0274 
0275     return d->fileWatcher;
0276 }
0277 
0278 int Expression::id()
0279 {
0280     return d->id;
0281 }
0282 
0283 void Expression::setId(int id)
0284 {
0285     d->id=id;
0286     emit idChanged();
0287 }
0288 
0289 void Expression::setFinishingBehavior(Expression::FinishingBehavior behavior)
0290 {
0291     d->finishingBehavior=behavior;
0292 }
0293 
0294 Expression::FinishingBehavior Expression::finishingBehavior()
0295 {
0296     return d->finishingBehavior;
0297 }
0298 
0299 bool Expression::isInternal() const
0300 {
0301     return d->internal;
0302 }
0303 
0304 void Expression::setIsHelpRequest(bool value)
0305 {
0306     d->helpRequest = value;
0307 }
0308 
0309 bool Expression::isHelpRequest() const
0310 {
0311     return d->helpRequest;
0312 }