File indexing completed on 2023-05-30 10:40:16
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 }