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 }