Warning, file /education/cantor/src/lib/session.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2009 Alexander Rieder <alexanderrieder@gmail.com> 0004 SPDX-FileCopyrightText: 2019 Alexander Semke <alexander.semke@web.de> 0005 */ 0006 0007 #include "session.h" 0008 using namespace Cantor; 0009 0010 #include <map> 0011 0012 #include "backend.h" 0013 #include "textresult.h" 0014 0015 #include <QTimer> 0016 #include <QQueue> 0017 #include <QDebug> 0018 #include <KMessageBox> 0019 #include <KLocalizedString> 0020 0021 class Cantor::SessionPrivate 0022 { 0023 public: 0024 SessionPrivate() { } 0025 0026 Backend* backend{nullptr}; 0027 Session::Status status{Session::Disable}; 0028 bool typesettingEnabled{false}; 0029 int expressionCount{0}; 0030 QList<Cantor::Expression*> expressionQueue; 0031 DefaultVariableModel* variableModel{nullptr}; 0032 QList<GraphicPackage> usableGraphicPackages; 0033 QList<GraphicPackage> enabledGraphicPackages; 0034 QList<QString> ignorableGraphicPackageIds; 0035 bool needUpdate{false}; 0036 }; 0037 0038 Session::Session(Backend* backend ) : QObject(backend), d(new SessionPrivate) 0039 { 0040 d->backend = backend; 0041 0042 #ifdef WITH_EPS 0043 if (Cantor::LatexRenderer::isLatexAvailable()) 0044 d->typesettingEnabled = Settings::self()->typesetDefault(); 0045 #endif 0046 } 0047 0048 Session::Session(Backend* backend, DefaultVariableModel* model) : QObject(backend), d(new SessionPrivate) 0049 { 0050 d->backend = backend; 0051 d->variableModel = model; 0052 0053 #ifdef WITH_EPS 0054 if (Cantor::LatexRenderer::isLatexAvailable()) 0055 d->typesettingEnabled = Settings::self()->typesetDefault(); 0056 #endif 0057 } 0058 0059 Session::~Session() 0060 { 0061 delete d; 0062 } 0063 0064 void Cantor::Session::testGraphicsPackages(QList<GraphicPackage> packages) 0065 { 0066 std::map<QString, bool> handlingStatus; 0067 0068 QEventLoop loop; 0069 for (GraphicPackage& package : packages) 0070 { 0071 if (GraphicPackage::findById(package, d->usableGraphicPackages) != -1) 0072 continue; 0073 0074 handlingStatus[package.id()] = false; 0075 Expression* expr = package.isAvailable(this); 0076 0077 connect(expr, &Expression::expressionFinished, [this, expr, &package, &loop, &handlingStatus](Expression::Status status) { 0078 if (status == Expression::Status::Done) { 0079 if (expr->result() != nullptr 0080 && expr->result()->type() == TextResult::Type 0081 && expr->result()->data().toString() == QLatin1String("1")) { 0082 this->d->usableGraphicPackages.push_back(package); 0083 } 0084 } else { 0085 qDebug() << "test presence command for" << package.id() << "finished because of" << (status == Expression::Error ? "error" : "interrupt"); 0086 if (status == Expression::Error && expr) 0087 qDebug() << "error message:" << expr->errorMessage(); 0088 } 0089 0090 handlingStatus[package.id()] = true; 0091 0092 bool allExpersionsFinished = true; 0093 for (auto& iter : handlingStatus) 0094 { 0095 if (iter.second == false) 0096 { 0097 allExpersionsFinished = false; 0098 break; 0099 } 0100 } 0101 0102 if (allExpersionsFinished) 0103 loop.exit(); 0104 }); 0105 } 0106 // If handlingStatus size is empty (it means, that no connections have been done), then we will stay in the 'loop' event loop forever 0107 if (handlingStatus.size() != 0) 0108 loop.exec(); 0109 } 0110 0111 void Session::logout() 0112 { 0113 if (d->status == Session::Running) 0114 interrupt(); 0115 0116 if (d->variableModel) 0117 { 0118 d->variableModel->clearVariables(); 0119 d->variableModel->clearFunctions(); 0120 } 0121 0122 d->expressionCount = 0; 0123 changeStatus(Status::Disable); 0124 0125 // Clean graphic package state 0126 d->enabledGraphicPackages.clear(); 0127 d->ignorableGraphicPackageIds.clear(); 0128 d->usableGraphicPackages.clear(); 0129 } 0130 0131 QList<Expression*>& Cantor::Session::expressionQueue() const 0132 { 0133 return d->expressionQueue; 0134 } 0135 0136 void Session::enqueueExpression(Expression* expr) 0137 { 0138 d->expressionQueue.append(expr); 0139 0140 //run the newly added expression immediately if it's the only one in the queue 0141 if (d->expressionQueue.size() == 1) 0142 { 0143 changeStatus(Cantor::Session::Running); 0144 runFirstExpression(); 0145 } 0146 else 0147 expr->setStatus(Cantor::Expression::Queued); 0148 } 0149 0150 void Session::runFirstExpression() 0151 { 0152 0153 } 0154 0155 void Session::finishFirstExpression(bool setDoneAfterUpdate) 0156 { 0157 if (!d->expressionQueue.isEmpty()) 0158 { 0159 auto first = d->expressionQueue.takeFirst(); 0160 d->needUpdate |= !first->isInternal() && !first->isHelpRequest(); 0161 } 0162 0163 if (d->expressionQueue.isEmpty()) 0164 if (d->variableModel && d->needUpdate) 0165 { 0166 d->variableModel->update(); 0167 d->needUpdate = false; 0168 0169 // Some variable models could update internal lists without running expressions 0170 // or don't need to be updated at all like for Maxima being in Lisp-mode. 0171 // So, if after update queue still empty, set status to Done 0172 // setDoneAfterUpdate used for compatibility with some backends, like R - TODO: check why this is requried 0173 if (setDoneAfterUpdate && d->expressionQueue.isEmpty()) 0174 changeStatus(Done); 0175 else if (d->expressionQueue.isEmpty()) 0176 changeStatus(Done); 0177 } 0178 else 0179 changeStatus(Done); 0180 else 0181 runFirstExpression(); 0182 } 0183 0184 void Session::currentExpressionStatusChanged(Cantor::Expression::Status status) 0185 { 0186 auto* expression = expressionQueue().first(); 0187 qDebug() << "expression status changed: command = " << expression->command() << ", status = " << status; 0188 0189 switch (status) 0190 { 0191 case Cantor::Expression::Done: 0192 case Cantor::Expression::Error: 0193 qDebug()<<"################################## EXPRESSION END ###############################################"; 0194 disconnect(expression, &Cantor::Expression::statusChanged, this, &Session::currentExpressionStatusChanged); 0195 finishFirstExpression(); 0196 break; 0197 default: 0198 break; 0199 } 0200 } 0201 0202 Backend* Session::backend() 0203 { 0204 return d->backend; 0205 } 0206 0207 Cantor::Session::Status Session::status() 0208 { 0209 return d->status; 0210 } 0211 0212 void Session::changeStatus(Session::Status newStatus) 0213 { 0214 d->status = newStatus; 0215 emit statusChanged(newStatus); 0216 } 0217 0218 void Session::setTypesettingEnabled(bool enable) 0219 { 0220 d->typesettingEnabled = enable; 0221 } 0222 0223 bool Session::isTypesettingEnabled() 0224 { 0225 return d->typesettingEnabled; 0226 } 0227 0228 void Session::setWorksheetPath(const QString&) { } 0229 0230 CompletionObject* Session::completionFor(const QString&, int) 0231 { 0232 //Return nullptr per default, so Backends not offering tab completions don't have 0233 //to reimplement this. This method should only be called on backends with 0234 //the Completion Capability flag 0235 0236 return nullptr; 0237 } 0238 0239 SyntaxHelpObject* Session::syntaxHelpFor(const QString&) 0240 { 0241 //Return nullptr per default, so Backends not offering tab completions don't have 0242 //to reimplement this. This method should only be called on backends with 0243 //the SyntaxHelp Capability flag 0244 return nullptr; 0245 } 0246 0247 QSyntaxHighlighter* Session::syntaxHighlighter(QObject*) 0248 { 0249 return nullptr; 0250 } 0251 0252 DefaultVariableModel* Session::variableModel() const 0253 { 0254 //By default, there is variableModel in session, used by syntax higlighter for variable analyzing 0255 //The model store only variable names by default. 0256 //In backends with VariableManagement Capability flag, this model also used for Cantor variable doc panel 0257 return d->variableModel; 0258 } 0259 0260 QAbstractItemModel* Session::variableDataModel() const 0261 { 0262 return variableModel(); 0263 } 0264 0265 void Session::updateVariables() 0266 { 0267 if (d->variableModel) 0268 { 0269 d->variableModel->update(); 0270 d->needUpdate = false; 0271 } 0272 } 0273 0274 void Cantor::Session::setVariableModel(Cantor::DefaultVariableModel* model) 0275 { 0276 d->variableModel = model; 0277 } 0278 0279 int Session::nextExpressionId() 0280 { 0281 return d->expressionCount++; 0282 } 0283 0284 QString Session::locateCantorFile(const QString& partialPath, QStandardPaths::LocateOptions options) 0285 { 0286 QString file = QStandardPaths::locate(QStandardPaths::AppDataLocation, partialPath, options); 0287 0288 if (file.isEmpty()) 0289 file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("cantor/") + partialPath, options); 0290 0291 return file; 0292 } 0293 0294 QStringList Session::locateAllCantorFiles(const QString& partialPath, QStandardPaths::LocateOptions options) 0295 { 0296 QStringList files = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, partialPath, options); 0297 0298 if (files.isEmpty()) 0299 files = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("cantor/") + partialPath, options); 0300 0301 return files; 0302 } 0303 0304 void Cantor::Session::reportSessionCrash(const QString& additionalInfo) 0305 { 0306 // Reporting about crashing backend in session without backend has not sense 0307 if (d->backend == nullptr) 0308 return; 0309 0310 if (additionalInfo.isEmpty()) 0311 KMessageBox::error(nullptr, i18n("%1 process has died unexpectedly. All calculation results are lost.", d->backend->name()), i18n("Error - Cantor")); 0312 else 0313 KMessageBox::error(nullptr, i18n("%1 process has died unexpectedly with message \"%2\". All calculation results are lost.", d->backend->name(), additionalInfo), i18n("Error - Cantor")); 0314 logout(); 0315 } 0316 0317 QList<Cantor::GraphicPackage> Cantor::Session::usableGraphicPackages() 0318 { 0319 return d->usableGraphicPackages; 0320 } 0321 0322 const QList<Cantor::GraphicPackage>& Cantor::Session::enabledGraphicPackages() const 0323 { 0324 return d->enabledGraphicPackages; 0325 } 0326 0327 QString Cantor::Session::graphicPackageErrorMessage(QString packageId) const 0328 { 0329 Q_UNUSED(packageId); 0330 return QString(); 0331 } 0332 0333 void Cantor::Session::updateEnabledGraphicPackages(const QList<Cantor::GraphicPackage>& newEnabledPackages, const QString& additionalInfo) 0334 { 0335 if (newEnabledPackages.isEmpty()) 0336 { 0337 if (!d->enabledGraphicPackages.isEmpty()) 0338 { 0339 for (const GraphicPackage& package : d->enabledGraphicPackages) 0340 evaluateExpression(package.disableSupportCommand(), Cantor::Expression::DeleteOnFinish, true); 0341 } 0342 d->enabledGraphicPackages.clear(); 0343 } 0344 else 0345 { 0346 QList<GraphicPackage> packagesExceptIgnored; 0347 for (const GraphicPackage& package : newEnabledPackages) 0348 if (d->ignorableGraphicPackageIds.contains(package.id()) == false) 0349 packagesExceptIgnored.append(package); 0350 0351 testGraphicsPackages(packagesExceptIgnored); 0352 0353 QList<GraphicPackage> unavailablePackages; 0354 QList<GraphicPackage> willEnabledPackages; 0355 0356 for (const GraphicPackage& package : packagesExceptIgnored) 0357 { 0358 if (GraphicPackage::findById(package, usableGraphicPackages()) != -1) 0359 willEnabledPackages.append(package); 0360 else 0361 unavailablePackages.append(package); 0362 } 0363 0364 for (const GraphicPackage& package : d->enabledGraphicPackages) 0365 if (GraphicPackage::findById(package, willEnabledPackages) == -1) 0366 evaluateExpression(package.disableSupportCommand(), Cantor::Expression::DeleteOnFinish, true); 0367 0368 for (const GraphicPackage& newPackage : willEnabledPackages) 0369 if (GraphicPackage::findById(newPackage, d->enabledGraphicPackages) == -1) 0370 evaluateExpression(newPackage.enableSupportCommand(additionalInfo), Cantor::Expression::DeleteOnFinish, true); 0371 0372 d->enabledGraphicPackages = willEnabledPackages; 0373 0374 for (const Cantor::GraphicPackage& notEnabledPackage : unavailablePackages) 0375 { 0376 if (d->ignorableGraphicPackageIds.contains(notEnabledPackage.id()) == false) 0377 { 0378 KMessageBox::information(nullptr, i18n( 0379 "You choose support for %1 graphic package, but the support can't be "\ 0380 "activated due to the missing requirements, so integration for this package will be disabled. %2", 0381 notEnabledPackage.name(), graphicPackageErrorMessage(notEnabledPackage.id())), i18n("Cantor") 0382 ); 0383 0384 d->ignorableGraphicPackageIds.append(notEnabledPackage.id()); 0385 } 0386 } 0387 } 0388 }