File indexing completed on 2022-12-06 18:50:23

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