diff --git a/src/backends/maxima/maximaexpression.h b/src/backends/maxima/maximaexpression.h --- a/src/backends/maxima/maximaexpression.h +++ b/src/backends/maxima/maximaexpression.h @@ -22,8 +22,8 @@ #define _MAXIMAEXPRESSION_H #include "expression.h" -#include "KDirWatch" #include +#include class QTemporaryFile; @@ -62,9 +62,11 @@ Cantor::Result* parseResult(int* idx,QString& out,QString& textBuffer,QString& latexBuffer); QTemporaryFile *m_tempFile; - KDirWatch m_fileWatch; + QFileSystemWatcher m_fileWatch; bool m_isHelpRequest; bool m_isPlot; + Cantor::Result* m_plotResult; + int m_plotResultIndex; QString m_errorBuffer; bool m_gotErrorContent; }; diff --git a/src/backends/maxima/maximaexpression.cpp b/src/backends/maxima/maximaexpression.cpp --- a/src/backends/maxima/maximaexpression.cpp +++ b/src/backends/maxima/maximaexpression.cpp @@ -44,6 +44,8 @@ m_tempFile(nullptr), m_isHelpRequest(false), m_isPlot(false), + m_plotResult(nullptr), + m_plotResultIndex(-1), m_gotErrorContent(false) { } @@ -59,6 +61,8 @@ if(m_tempFile) m_tempFile->deleteLater(); m_tempFile=nullptr; + m_plotResult = nullptr; + m_plotResultIndex = -1; //check if this is a ?command if(command().startsWith(QLatin1String("??")) || command().startsWith(QLatin1String("describe(")) @@ -76,9 +80,9 @@ #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); + m_fileWatch.removePaths(m_fileWatch.files()); + m_fileWatch.addPath(m_tempFile->fileName()); + connect(&m_fileWatch, &QFileSystemWatcher::fileChanged, this, &MaximaExpression::imageChanged, Qt::UniqueConnection); } const QString& cmd=command(); @@ -651,7 +655,9 @@ errorContent += out.mid(lastResultEnd, promptStart - lastResultEnd).trimmed(); if (errorContent.isEmpty()) { - setStatus(Cantor::Expression::Done); + // For plots we set Done status in imageChanged + if (!m_isPlot || m_plotResult) + setStatus(Cantor::Expression::Done); } else { @@ -710,16 +716,29 @@ textContent = textContent.remove(outputLabel).trimmed(); //determine the actual result - Cantor::TextResult* result = nullptr; + Cantor::Result* result = nullptr; const int latexContentStart = resultContent.indexOf(QLatin1String("")); - if (latexContentStart != -1) + //Handle system maxima output for plotting commands + if (m_isPlot && textContent.endsWith(QString::fromLatin1("\"%1\"]").arg(m_tempFile->fileName()))) + { + m_plotResultIndex = results().size(); + // Gnuplot could generate plot before we parse text output from maxima and after + // If we already have plot result, just add it + // Else set info message, and replace it by real result in imageChanged function later + if (m_plotResult) + result = m_plotResult; + else + result = new Cantor::TextResult(i18n("Waiting for the plot result")); + } + else if (latexContentStart != -1) { //latex output is available const int latexContentEnd = resultContent.indexOf(QLatin1String("")); QString latexContent = resultContent.mid(latexContentStart + 14, latexContentEnd - latexContentStart - 14).trimmed(); qDebug()<<"latex content: " << latexContent; + Cantor::TextResult* textResult; //replace the \mbox{} environment, if available, by the eqnarray environment if (latexContent.indexOf(QLatin1String("\\mbox{")) != -1) { @@ -745,16 +764,17 @@ modifiedLatexContent.prepend(QLatin1String("\\begin{eqnarray*}")); modifiedLatexContent.append(QLatin1String("\\end{eqnarray*}")); - result = new Cantor::TextResult(modifiedLatexContent, textContent); + textResult = new Cantor::TextResult(modifiedLatexContent, textContent); qDebug()<<"modified latex content: " << modifiedLatexContent; } else { //no \mbox{} available, use what we've got. - result = new Cantor::TextResult(latexContent, textContent); + textResult = new Cantor::TextResult(latexContent, textContent); } - result->setFormat(Cantor::TextResult::LatexFormat); + textResult->setFormat(Cantor::TextResult::LatexFormat); + result = textResult; } else { @@ -772,14 +792,19 @@ 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()) ) ); + m_plotResult = new Cantor::EpsResult( QUrl::fromLocalFile(m_tempFile->fileName()) ); #else - setResult( new Cantor::ImageResult( QUrl::fromLocalFile(m_tempFile->fileName()) ) ); + m_plotResult = new Cantor::ImageResult( QUrl::fromLocalFile(m_tempFile->fileName()) ); #endif - setStatus(Cantor::Expression::Done); + // Check, that we already parse maxima output for this plot, and if not, keep it up to this moment + // If it's true, replace text info result by real plot and set status as Done + if (m_plotResultIndex != -1) + { + replaceResult(m_plotResultIndex, m_plotResult); + setStatus(Cantor::Expression::Done); + } } } diff --git a/src/backends/maxima/testmaxima.h b/src/backends/maxima/testmaxima.h --- a/src/backends/maxima/testmaxima.h +++ b/src/backends/maxima/testmaxima.h @@ -41,6 +41,8 @@ void testCommandQueue(); //tests doing a plot void testPlot(); + void testPlotWithAnotherTextResults(); + //tests a syntax error (not closing bracket) void testInvalidSyntax(); //tests if the expression numbering works diff --git a/src/backends/maxima/testmaxima.cpp b/src/backends/maxima/testmaxima.cpp --- a/src/backends/maxima/testmaxima.cpp +++ b/src/backends/maxima/testmaxima.cpp @@ -68,7 +68,7 @@ { if(QStandardPaths::findExecutable(QLatin1String("gnuplot")).isNull()) { - QSKIP("gnuplot not found maxima needs it for plotting", SkipSingle); + QSKIP("gnuplot not found, maxima needs it for plotting", SkipSingle); } Cantor::Expression* e=evalExp( QLatin1String("plot2d(sin(x), [x, -10,10])") ); @@ -90,6 +90,35 @@ QVERIFY( e->errorMessage().isNull() ); } +void TestMaxima::testPlotWithAnotherTextResults() +{ + if(QStandardPaths::findExecutable(QLatin1String("gnuplot")).isNull()) + { + QSKIP("gnuplot not found, maxima needs it for plotting", SkipSingle); + } + + Cantor::Expression* e=evalExp( QLatin1String( + "2*2; \n" + "plot2d(sin(x), [x, -10,10]); \n" + "4*4;" + )); + + QVERIFY( e!=nullptr ); + QVERIFY( e->errorMessage().isNull() ); + QCOMPARE(e->results().size(), 3); + + QCOMPARE(e->results()[0]->toHtml(), QLatin1String("4")); + +#ifdef WITH_EPS + QCOMPARE( e->results()[1]->type(), (int)Cantor::EpsResult::Type ); +#else + QCOMPARE( e->results()[1]->type(), (int)Cantor::ImageResult::Type ); +#endif + QVERIFY( !e->results()[1]->data().isNull() ); + + QCOMPARE(e->results()[2]->toHtml(), QLatin1String("16")); +} + void TestMaxima::testInvalidSyntax() { Cantor::Expression* e=evalExp( QLatin1String("2+2*(") ); diff --git a/src/commandentry.h b/src/commandentry.h --- a/src/commandentry.h +++ b/src/commandentry.h @@ -129,6 +129,8 @@ private Q_SLOTS: void invalidate(); void resultDeleted(); + void clearResultItems(); + void replaceResultItem(int index); void updateCompletions(); void completeCommandTo(const QString& completion, CommandEntry::CompletionMode mode = PreliminaryCompletion); diff --git a/src/commandentry.cpp b/src/commandentry.cpp --- a/src/commandentry.cpp +++ b/src/commandentry.cpp @@ -347,13 +347,14 @@ } m_informationItems.clear(); - m_expression = nullptr; // Delete any previous result - removeResult(); + clearResultItems(); - m_expression=expr; + m_expression = expr; connect(expr, SIGNAL(gotResult()), this, SLOT(updateEntry())); + connect(expr, SIGNAL(resultCleared()), this, SLOT(clearResultItems())); + connect(expr, SIGNAL(resultReplaced(int)), this, SLOT(replaceResultItem(int))); connect(expr, SIGNAL(idChanged()), this, SLOT(updatePrompt())); connect(expr, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(expressionChangedStatus(Cantor::Expression::Status))); connect(expr, SIGNAL(needsAdditionalInformation(QString)), this, SLOT(showAdditionalInformationPrompt(QString))); @@ -885,14 +886,25 @@ { m_expression->clearResult(); } +} +void CommandEntry::clearResultItems() +{ //fade out all result graphic objects for(auto* item : m_resultItems) fadeOutItem(item->graphicsObject()); m_resultItems.clear(); } +void CommandEntry::replaceResultItem(int index) +{ + ResultItem* previousItem = m_resultItems[index]; + m_resultItems[index] = ResultItem::create(this, m_expression->results()[index]); + previousItem->deleteLater(); + recalculateSize(); +} + void CommandEntry::removeContextHelp() { disconnect(m_commandItem->document(), SIGNAL(contentsChanged()), this, SLOT(completedLineChanged())); diff --git a/src/lib/expression.h b/src/lib/expression.h --- a/src/lib/expression.h +++ b/src/lib/expression.h @@ -208,6 +208,15 @@ * A Result of the Expression has arrived */ void gotResult(); + /** + * emitted when the results of the expression were deleted. + * @see clearResult() + */ + void resultCleared(); + /** + * emitted when the result at the position @c index was replaced by a new result. + */ + void resultReplaced(int index); /** * the status of the Expression has changed. * @param status the new status @@ -234,6 +243,8 @@ void addResult(Result*); + void replaceResult(int index, Result* result); + protected: //returns a string of latex commands, that is inserted into the header. //used for example if special packages are needed diff --git a/src/lib/expression.cpp b/src/lib/expression.cpp --- a/src/lib/expression.cpp +++ b/src/lib/expression.cpp @@ -108,8 +108,7 @@ void Expression::setResult(Result* result) { - qDeleteAll(d->results); - d->results.clear(); + clearResult(); addResult(result); } @@ -138,6 +137,22 @@ emit gotResult(); } +void Expression::clearResult() +{ + qDeleteAll(d->results); + d->results.clear(); + emit resultCleared(); +} + +void Expression::replaceResult(int index, Result* result) +{ + if (result) + { + d->results.insert(index, result); + emit resultReplaced(index); + } +} + Result* Expression::result() { if (!d->results.isEmpty()) @@ -151,12 +166,6 @@ return d->results; } -void Expression::clearResult() -{ - qDeleteAll(d->results); - d->results.clear(); -} - void Expression::setStatus(Expression::Status status) { d->status=status;