File indexing completed on 2024-04-28 11:21:01

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2012 Martin Kuettler <martin.kuettler@gmail.com>
0004     SPDX-FileCopyrightText: 2018-2022 by Alexander Semke (alexander.semke@web.de)
0005 */
0006 
0007 #include "textresultitem.h"
0008 #include "commandentry.h"
0009 #include "lib/textresult.h"
0010 #include "lib/latexresult.h"
0011 #include "lib/renderer.h"
0012 #include "lib/mimeresult.h"
0013 #include "lib/htmlresult.h"
0014 #ifdef WITH_EPS
0015 #include "mathrendertask.h"
0016 #endif
0017 #include "settings.h"
0018 #include "worksheetview.h"
0019 
0020 #include <QApplication>
0021 #include <QFileDialog>
0022 #include <QTextCursor>
0023 
0024 #include <KStandardAction>
0025 #include <KLocalizedString>
0026 
0027 TextResultItem::TextResultItem(WorksheetEntry* parent, Cantor::Result* result)
0028     : WorksheetTextItem(parent), ResultItem(result)
0029 {
0030     connect(this, SIGNAL(collapseActionSizeChanged()), parent, SLOT(recalculateSize()));
0031     setTextInteractionFlags(Qt::TextSelectableByMouse);
0032     update();
0033 
0034     auto* textResult = dynamic_cast<Cantor::TextResult*>(result);
0035     if (textResult && textResult->isWarning())
0036         setDefaultTextColor(qApp->palette().color(QPalette::Highlight));
0037 
0038     // So useful behaviour:
0039     // If we have HtmlResult, but after setting we have empty document
0040     // So show Plain version - it more useful
0041     // We do it here, because we need it one
0042     if (document()->characterCount() && document()->characterAt(0) == QChar::ParagraphSeparator)
0043     {
0044         auto* hr = static_cast<Cantor::HtmlResult*>(m_result);
0045         hr->setFormat(Cantor::HtmlResult::PlainAlternative);
0046         setHtml(hr->toHtml());
0047     }
0048 }
0049 
0050 double TextResultItem::setGeometry(double x, double y, double w)
0051 {
0052     WorksheetTextItem::setGeometry(x, y, w);
0053     collapseExtraLines();
0054     return height();
0055 }
0056 
0057 void TextResultItem::populateMenu(QMenu* menu, QPointF)
0058 {
0059     auto* copy = KStandardAction::copy(this, SLOT(copy()), menu);
0060     copy->setText(i18n("Copy result"));
0061     menu->addAction(copy);
0062     ResultItem::addCommonActions(this, menu);
0063 
0064     auto* res = result();
0065     if (res->type() == Cantor::LatexResult::Type) {
0066         QAction* showCodeAction = nullptr;
0067         auto* lres = static_cast<Cantor::LatexResult*>(res);
0068         if (lres->isCodeShown())
0069             showCodeAction = menu->addAction(i18n("Show Rendered"));
0070         else
0071             showCodeAction = menu->addAction(i18n("Show Code"));
0072 
0073         connect(showCodeAction, &QAction::triggered, this, &TextResultItem::toggleLatexCode);
0074     } else if (res->type() == Cantor::HtmlResult::Type) {
0075         auto* hres = static_cast<Cantor::HtmlResult*>(res);
0076         switch (hres->format())
0077         {
0078             case Cantor::HtmlResult::Html:
0079                 connect(menu->addAction(i18n("Show HTML Code")), &QAction::triggered, this, &TextResultItem::showHtmlSource);
0080                 if (!hres->plain().isEmpty())
0081                     connect(menu->addAction(i18n("Show Plain Alternative")), &QAction::triggered, this, &TextResultItem::showPlain);
0082                 break;
0083 
0084             case Cantor::HtmlResult::HtmlSource:
0085                 connect(menu->addAction(i18n("Show Html")), &QAction::triggered, this, &TextResultItem::showHtml);
0086                 if (!hres->plain().isEmpty())
0087                     connect(menu->addAction(i18n("Show Plain Alternative")), &QAction::triggered, this, &TextResultItem::showPlain);
0088                 break;
0089 
0090             case Cantor::HtmlResult::PlainAlternative:
0091                 connect(menu->addAction(i18n("Show HTML")), &QAction::triggered, this, &TextResultItem::showHtml);
0092                 connect(menu->addAction(i18n("Show HTML Code")), &QAction::triggered, this, &TextResultItem::showHtmlSource);
0093                 break;
0094 
0095         }
0096     }
0097 }
0098 
0099 void TextResultItem::update()
0100 {
0101     Q_ASSERT(
0102         m_result->type() == Cantor::TextResult::Type
0103         || m_result->type() == Cantor::LatexResult::Type
0104         || m_result->type() == Cantor::MimeResult::Type
0105         || m_result->type() == Cantor::HtmlResult::Type
0106     );
0107     switch(m_result->type()) {
0108     case Cantor::TextResult::Type:
0109         setPlainText(static_cast<Cantor::TextResult*>(m_result)->plain());
0110         break;
0111     case Cantor::MimeResult::Type:
0112     case Cantor::HtmlResult::Type:
0113         setHtml(m_result->toHtml());
0114         break;
0115     case Cantor::LatexResult::Type:
0116         setLatex(static_cast<Cantor::LatexResult*>(m_result));
0117         break;
0118     default:
0119         break;
0120     }
0121 }
0122 
0123 void TextResultItem::setLatex(Cantor::LatexResult* result)
0124 {
0125     QTextCursor cursor = textCursor();
0126     cursor.movePosition(QTextCursor::Start);
0127     cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
0128     QString latex = result->toLatex().trimmed();
0129     if (latex.startsWith(QLatin1String("\\begin{eqnarray*}")) &&
0130         latex.endsWith(QLatin1String("\\end{eqnarray*}"))) {
0131         latex = latex.mid(17);
0132         latex = latex.left(latex.size() - 15);
0133     }
0134 
0135 #ifdef WITH_EPS
0136     if (result->isCodeShown()) {
0137         if (latex.isEmpty())
0138             cursor.removeSelectedText();
0139         else
0140             cursor.insertText(latex);
0141     } else {
0142         QTextImageFormat format;
0143 
0144         if (!result->image().isNull() && worksheet()->renderer()->scale() == 1.0)
0145         {
0146             cursor.insertText(QString(QChar::ObjectReplacementCharacter), toFormat(result->image(), latex));
0147         }
0148         else
0149         {
0150             QString uuid = Cantor::LatexRenderer::genUuid();
0151             auto* renderer = qobject_cast<Worksheet*>(scene())->renderer();;
0152             format = renderer->render(cursor.document(), Cantor::Renderer::EPS, result->url(), uuid);
0153             format.setProperty(Cantor::Renderer::CantorFormula,
0154                             Cantor::Renderer::LatexFormula);
0155             format.setProperty(Cantor::Renderer::Code, latex);
0156             format.setProperty(Cantor::Renderer::Delimiter, QLatin1String("$$"));
0157             if(format.isValid())
0158                 cursor.insertText(QString(QChar::ObjectReplacementCharacter), format);
0159             else
0160                 cursor.insertText(i18n("Cannot render Eps file. You may need additional packages"));
0161         }
0162     }
0163 #else
0164     cursor.insertText(QString(QChar::ObjectReplacementCharacter), toFormat(result->image(), latex));
0165 #endif
0166 }
0167 
0168 double TextResultItem::width() const
0169 {
0170     return WorksheetTextItem::width();
0171 }
0172 
0173 double TextResultItem::height() const
0174 {
0175     return WorksheetTextItem::height();
0176 }
0177 
0178 void TextResultItem::toggleLatexCode()
0179 {
0180      auto* lr = static_cast<Cantor::LatexResult*>(result());
0181      if(lr->isCodeShown())
0182          lr->showRendered();
0183      else
0184          lr->showCode();
0185 
0186      parentEntry()->updateEntry();
0187 }
0188 
0189 void TextResultItem::showHtml()
0190 {
0191      auto* hr = static_cast<Cantor::HtmlResult*>(result());
0192      hr->setFormat(Cantor::HtmlResult::Html);
0193      parentEntry()->updateEntry();
0194 }
0195 
0196 void TextResultItem::showHtmlSource()
0197 {
0198      auto* hr = static_cast<Cantor::HtmlResult*>(result());
0199      hr->setFormat(Cantor::HtmlResult::HtmlSource);
0200      parentEntry()->updateEntry();
0201 }
0202 
0203 void TextResultItem::showPlain()
0204 {
0205      auto* hr = static_cast<Cantor::HtmlResult*>(result());
0206      hr->setFormat(Cantor::HtmlResult::PlainAlternative);
0207      parentEntry()->updateEntry();
0208 }
0209 
0210 void TextResultItem::saveResult()
0211 {
0212     const auto& fileName = QFileDialog::getSaveFileName(worksheet()->worksheetView(), i18n("Save text result"), QString(),  i18n("Text Files (*.txt)"));
0213     if (!fileName.isEmpty())
0214         result()->save(fileName);
0215 }
0216 
0217 void TextResultItem::deleteLater()
0218 {
0219     WorksheetTextItem::deleteLater();
0220 }
0221 
0222 QTextImageFormat TextResultItem::toFormat(const QImage& image, const QString& latex)
0223 {
0224     QTextImageFormat format;
0225 
0226     QUrl internal;
0227     internal.setScheme(QLatin1String("internal"));
0228     internal.setPath(Cantor::LatexRenderer::genUuid());
0229 
0230     document()->addResource(QTextDocument::ImageResource, internal, QVariant(image) );
0231 
0232     format.setName(internal.url());
0233     format.setProperty(Cantor::Renderer::CantorFormula, Cantor::Renderer::LatexFormula);
0234     //format.setProperty(Cantor::EpsRenderer::ImagePath, filename);
0235     format.setProperty(Cantor::Renderer::Code, latex);
0236     format.setProperty(Cantor::Renderer::Delimiter, QLatin1String("$$"));
0237 
0238     return format;
0239 }
0240 
0241 void TextResultItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
0242 {
0243     if (!m_isCollapsed)
0244         return;
0245 
0246     m_userCollapseOverride = !m_userCollapseOverride;
0247     if (m_isCollapsed)
0248     {
0249         if (m_userCollapseOverride)
0250         {
0251             update();
0252         }
0253         else
0254         {
0255             m_isCollapsed = false;
0256             collapseExtraLines();
0257         }
0258         emit collapseActionSizeChanged();
0259     }
0260     QGraphicsTextItem::mouseDoubleClickEvent(event);
0261 }
0262 
0263 int TextResultItem::visibleLineCount()
0264 {
0265     int lineCounter = 0;
0266     QTextCursor cursor(document());
0267     if(!cursor.isNull())
0268     {
0269         cursor.movePosition(QTextCursor::Start);
0270         bool isNotDone = true;
0271         while( isNotDone )
0272         {
0273             isNotDone = cursor.movePosition( QTextCursor::Down );
0274             lineCounter++;
0275         }
0276     }
0277 
0278     return lineCounter;
0279 }
0280 
0281 void TextResultItem::collapseExtraLines()
0282 {
0283     if (m_userCollapseOverride)
0284         return;
0285 
0286     int limit = Settings::visibleLinesLimit();
0287 
0288     // If limit disable (0 is for unlimited mode), then exit
0289     if (limit == 0)
0290         return;
0291 
0292     // for situation, when we have collapsed text result and resized Cantor window
0293     if (m_isCollapsed && (int)width() != m_widthWhenCollapsed)
0294     {
0295         update();
0296         m_isCollapsed = false;
0297     }
0298 
0299     if (visibleLineCount() > limit)
0300     {
0301         QTextCursor cursor(document());
0302         cursor.movePosition(QTextCursor::Start);
0303         if (limit > 4)
0304         {
0305             for (int i = 0; i < limit-4; i++)
0306                 cursor.movePosition(QTextCursor::Down);
0307 
0308             cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
0309             cursor.movePosition(QTextCursor::Up, QTextCursor::KeepAnchor);
0310 
0311             cursor.insertText(QLatin1String("\n\n...\n\n"));
0312         }
0313         else
0314         {
0315             for (int i = 0; i < limit-1; i++)
0316                 cursor.movePosition(QTextCursor::Down);
0317             cursor.movePosition(QTextCursor::EndOfLine);
0318 
0319             QString replacer = QLatin1String("...");
0320             for (int i = 0; i < replacer.length(); i++)
0321                 cursor.movePosition(QTextCursor::Left);
0322 
0323             cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
0324             cursor.insertText(replacer);
0325         }
0326 
0327         m_isCollapsed = true;
0328         m_widthWhenCollapsed = (int)width();
0329     }
0330 }