Warning, file /pim/akregator/src/articleviewerwidget.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 This file is part of Akregator. 0003 0004 SPDX-FileCopyrightText: 2004 Teemu Rytilahti <tpr@d5k.net> 0005 SPDX-FileCopyrightText: 2005 Frank Osterfeld <osterfeld@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0 0008 */ 0009 0010 #include "articleviewerwidget.h" 0011 0012 #include "actionmanager.h" 0013 #include "akregator_debug.h" 0014 #include "akregatorconfig.h" 0015 #include "articleformatter.h" 0016 #include "articlejobs.h" 0017 #include "feed.h" 0018 #include "openurlrequest.h" 0019 #include "treenode.h" 0020 0021 #include <KActionCollection> 0022 0023 #include <QElapsedTimer> 0024 0025 #include "articleviewer-ng/webengine/articlehtmlwebenginewriter.h" 0026 #include "articleviewer-ng/webengine/articleviewerwebengine.h" 0027 #include "articleviewer-ng/webengine/articleviewerwebenginewidgetng.h" 0028 #include "defaultnormalviewformatter.h" 0029 #include <QGridLayout> 0030 #include <QKeyEvent> 0031 0032 #include "defaultcombinedviewformatter.h" 0033 0034 using namespace Akregator; 0035 using namespace Akregator::Filters; 0036 0037 ArticleViewerWidget::ArticleViewerWidget(const QString &grantleeDirectory, KActionCollection *ac, QWidget *parent) 0038 : QWidget(parent) 0039 , m_node(nullptr) 0040 , m_articleViewerWidgetNg(new Akregator::ArticleViewerWebEngineWidgetNg(ac, this)) 0041 , m_grantleeDirectory(grantleeDirectory) 0042 { 0043 auto layout = new QGridLayout(this); 0044 layout->setContentsMargins({}); 0045 layout->addWidget(m_articleViewerWidgetNg); 0046 m_articleHtmlWriter = new Akregator::ArticleHtmlWebEngineWriter(m_articleViewerWidgetNg->articleViewerNg(), this); 0047 connect(m_articleViewerWidgetNg->articleViewerNg(), &ArticleViewerWebEngine::signalOpenUrlRequest, this, &ArticleViewerWidget::signalOpenUrlRequest); 0048 connect(m_articleViewerWidgetNg->articleViewerNg(), &ArticleViewerWebEngine::showStatusBarMessage, this, &ArticleViewerWidget::showStatusBarMessage); 0049 } 0050 0051 ArticleViewerWidget::~ArticleViewerWidget() = default; 0052 0053 QSharedPointer<ArticleFormatter> ArticleViewerWidget::normalViewFormatter() 0054 { 0055 if (!m_normalViewFormatter.data()) { 0056 m_normalViewFormatter = 0057 QSharedPointer<ArticleFormatter>(new DefaultNormalViewFormatter(m_grantleeDirectory, m_articleViewerWidgetNg->articleViewerNg())); 0058 } 0059 return m_normalViewFormatter; 0060 } 0061 0062 QSharedPointer<ArticleFormatter> ArticleViewerWidget::combinedViewFormatter() 0063 { 0064 if (!m_combinedViewFormatter.data()) { 0065 m_combinedViewFormatter = 0066 QSharedPointer<ArticleFormatter>(new DefaultCombinedViewFormatter(m_grantleeDirectory, m_articleViewerWidgetNg->articleViewerNg())); 0067 } 0068 return m_combinedViewFormatter; 0069 } 0070 0071 void ArticleViewerWidget::slotZoomChangeInFrame(qreal value) 0072 { 0073 m_articleViewerWidgetNg->articleViewerNg()->setZoomFactor(value); 0074 } 0075 0076 void ArticleViewerWidget::slotCopy() 0077 { 0078 m_articleViewerWidgetNg->articleViewerNg()->slotCopy(); 0079 } 0080 0081 void ArticleViewerWidget::slotSelectionChanged() 0082 { 0083 ActionManager::getInstance()->action(QStringLiteral("viewer_copy"))->setEnabled(!m_articleViewerWidgetNg->articleViewerNg()->selectedText().isEmpty()); 0084 } 0085 0086 void ArticleViewerWidget::slotPrint() 0087 { 0088 m_articleViewerWidgetNg->slotPrint(); 0089 } 0090 0091 void ArticleViewerWidget::slotPrintPreview() 0092 { 0093 m_articleViewerWidgetNg->slotPrintPreview(); 0094 } 0095 0096 void ArticleViewerWidget::connectToNode(TreeNode *node) 0097 { 0098 if (node) { 0099 if (m_viewMode == CombinedView) { 0100 connect(node, &TreeNode::signalChanged, this, &ArticleViewerWidget::slotUpdateCombinedView); 0101 connect(node, &TreeNode::signalArticlesAdded, this, &ArticleViewerWidget::slotArticlesAdded); 0102 connect(node, &TreeNode::signalArticlesRemoved, this, &ArticleViewerWidget::slotArticlesRemoved); 0103 connect(node, &TreeNode::signalArticlesUpdated, this, &ArticleViewerWidget::slotArticlesUpdated); 0104 } else if (m_viewMode == SummaryView) { 0105 connect(node, &TreeNode::signalChanged, this, &ArticleViewerWidget::slotShowSummary); 0106 } 0107 0108 connect(node, &TreeNode::signalDestroyed, this, &ArticleViewerWidget::slotClear); 0109 } 0110 } 0111 0112 void ArticleViewerWidget::disconnectFromNode(TreeNode *node) 0113 { 0114 if (node) { 0115 node->disconnect(this); 0116 } 0117 } 0118 0119 void ArticleViewerWidget::renderContent(const QString &text) 0120 { 0121 m_currentText = text; 0122 reload(); 0123 } 0124 0125 void ArticleViewerWidget::beginWriting() 0126 { 0127 m_articleHtmlWriter->begin(); 0128 } 0129 0130 void ArticleViewerWidget::endWriting() 0131 { 0132 m_articleHtmlWriter->end(); 0133 } 0134 0135 void ArticleViewerWidget::slotShowSummary(TreeNode *node) 0136 { 0137 m_viewMode = SummaryView; 0138 0139 if (!node) { 0140 slotClear(); 0141 return; 0142 } 0143 0144 if (node != m_node) { 0145 disconnectFromNode(m_node); 0146 connectToNode(node); 0147 m_node = node; 0148 } 0149 0150 const QString summary = normalViewFormatter()->formatSummary(node); 0151 m_link.clear(); 0152 renderContent(summary); 0153 0154 setArticleActionsEnabled(false); 0155 } 0156 0157 void ArticleViewerWidget::showArticle(const Akregator::Article &article) 0158 { 0159 if (article.isNull() || article.isDeleted()) { 0160 slotClear(); 0161 return; 0162 } 0163 0164 const QUrl xmlUrl = QUrl(article.feed()->xmlUrl()); 0165 qCDebug(AKREGATOR_LOG) << "showing Article - xmlUrl:" << xmlUrl; 0166 m_articleHtmlWriter->setBaseUrl(xmlUrl); 0167 m_viewMode = NormalView; 0168 disconnectFromNode(m_node); 0169 m_article = article; 0170 m_node = nullptr; 0171 m_link = article.link(); 0172 if (article.feed()->loadLinkedWebsite()) { 0173 openUrl(article.link()); 0174 } else { 0175 renderContent(normalViewFormatter()->formatArticles(QList<Akregator::Article>() << article, ArticleFormatter::ShowIcon)); 0176 } 0177 0178 setArticleActionsEnabled(true); 0179 } 0180 0181 bool ArticleViewerWidget::openUrl(const QUrl &url) 0182 { 0183 if (!m_article.isNull() && m_article.feed()->loadLinkedWebsite()) { 0184 m_articleViewerWidgetNg->articleViewerNg()->load(url); 0185 } else { 0186 reload(); 0187 } 0188 return true; 0189 } 0190 0191 void ArticleViewerWidget::setFilters(const std::vector<QSharedPointer<const AbstractMatcher>> &filters) 0192 { 0193 if (filters == m_filters) { 0194 return; 0195 } 0196 0197 m_filters = filters; 0198 0199 slotUpdateCombinedView(); 0200 } 0201 0202 void ArticleViewerWidget::slotUpdateCombinedView() 0203 { 0204 if (m_viewMode != CombinedView) { 0205 return; 0206 } 0207 0208 if (!m_node) { 0209 return slotClear(); 0210 } 0211 0212 m_articleViewerWidgetNg->saveCurrentPosition(); 0213 QString text; 0214 0215 int num = 0; 0216 QElapsedTimer spent; 0217 spent.start(); 0218 0219 const auto filterEnd = m_filters.cend(); 0220 0221 QList<Article> articles; 0222 for (const Article &i : std::as_const(m_articles)) { 0223 if (i.isDeleted()) { 0224 continue; 0225 } 0226 0227 auto func = [i](const QSharedPointer<const Filters::AbstractMatcher> &matcher) -> bool { 0228 return !matcher->matches(i); 0229 }; 0230 if (std::find_if(m_filters.cbegin(), filterEnd, func) != filterEnd) { 0231 continue; 0232 } 0233 articles << i; 0234 ++num; 0235 } 0236 text = combinedViewFormatter()->formatArticles(articles, ArticleFormatter::NoIcon); 0237 0238 qCDebug(AKREGATOR_LOG) << "Combined view rendering: (" << num << " articles):" 0239 << "generating HTML:" << spent.elapsed() << "ms"; 0240 renderContent(text); 0241 qCDebug(AKREGATOR_LOG) << "HTML rendering:" << spent.elapsed() << "ms"; 0242 } 0243 0244 void ArticleViewerWidget::slotArticlesUpdated(TreeNode * /*node*/, const QList<Article> & /*list*/) 0245 { 0246 if (m_viewMode == CombinedView) { 0247 slotUpdateCombinedView(); 0248 } 0249 } 0250 0251 void ArticleViewerWidget::slotArticlesAdded(TreeNode * /*node*/, const QList<Article> &list) 0252 { 0253 if (m_viewMode == CombinedView) { 0254 // TODO sort list, then merge 0255 m_articles << list; 0256 std::sort(m_articles.begin(), m_articles.end()); 0257 slotUpdateCombinedView(); 0258 } 0259 } 0260 0261 void ArticleViewerWidget::slotArticlesRemoved(TreeNode * /*node*/, const QList<Article> &list) 0262 { 0263 Q_UNUSED(list) 0264 0265 if (m_viewMode == CombinedView) { 0266 slotUpdateCombinedView(); 0267 } 0268 } 0269 0270 void ArticleViewerWidget::slotClear() 0271 { 0272 disconnectFromNode(m_node); 0273 m_node = nullptr; 0274 m_article = Article(); 0275 m_articles.clear(); 0276 0277 renderContent(QString()); 0278 } 0279 0280 void ArticleViewerWidget::showNode(TreeNode *node) 0281 { 0282 m_viewMode = CombinedView; 0283 0284 if (node != m_node) { 0285 disconnectFromNode(m_node); 0286 } 0287 0288 connectToNode(node); 0289 0290 m_articles.clear(); 0291 m_article = Article(); 0292 m_node = node; 0293 0294 delete m_listJob; 0295 0296 m_listJob = node->createListJob(); 0297 connect(m_listJob.data(), &ArticleListJob::finished, this, &ArticleViewerWidget::slotArticlesListed); 0298 m_listJob->start(); 0299 0300 slotUpdateCombinedView(); 0301 } 0302 0303 qreal ArticleViewerWidget::zoomFactor() const 0304 { 0305 return m_articleViewerWidgetNg->articleViewerNg()->zoomFactor(); 0306 } 0307 0308 void ArticleViewerWidget::slotArticlesListed(KJob *job) 0309 { 0310 Q_ASSERT(job); 0311 Q_ASSERT(job == m_listJob); 0312 0313 TreeNode *node = m_listJob->node(); 0314 0315 if (job->error() || !node) { 0316 if (!node) { 0317 qCWarning(AKREGATOR_LOG) << "Node to be listed is already deleted"; 0318 } else { 0319 qCWarning(AKREGATOR_LOG) << job->errorText(); 0320 } 0321 slotUpdateCombinedView(); 0322 return; 0323 } 0324 0325 m_articles = m_listJob->articles(); 0326 std::sort(m_articles.begin(), m_articles.end()); 0327 0328 if (!m_articles.isEmpty()) { 0329 m_link = m_articles.first().link(); 0330 } else { 0331 m_link = QUrl(); 0332 } 0333 0334 slotUpdateCombinedView(); 0335 } 0336 0337 void ArticleViewerWidget::keyPressEvent(QKeyEvent *e) 0338 { 0339 e->ignore(); 0340 } 0341 0342 void ArticleViewerWidget::updateAfterConfigChanged() 0343 { 0344 switch (m_viewMode) { 0345 case NormalView: 0346 if (!m_article.isNull()) { 0347 renderContent(normalViewFormatter()->formatArticles(QList<Akregator::Article>() << m_article, ArticleFormatter::ShowIcon)); 0348 } 0349 break; 0350 case CombinedView: 0351 slotUpdateCombinedView(); 0352 break; 0353 case SummaryView: 0354 slotShowSummary(m_node); 0355 break; 0356 } 0357 m_articleViewerWidgetNg->articleViewerNg()->updateSecurity(); 0358 } 0359 0360 void ArticleViewerWidget::reload() 0361 { 0362 beginWriting(); 0363 m_articleHtmlWriter->queue(m_currentText); 0364 endWriting(); 0365 } 0366 0367 QSize ArticleViewerWidget::sizeHint() const 0368 { 0369 // Increase height a bit so that we can (roughly) read 25 lines of text 0370 QSize sh = QWidget::sizeHint(); 0371 sh.setHeight(qMax(sh.height(), 25 * fontMetrics().height())); 0372 return sh; 0373 } 0374 0375 void ArticleViewerWidget::displayAboutPage() 0376 { 0377 m_articleViewerWidgetNg->articleViewerNg()->showAboutPage(); 0378 } 0379 0380 void ArticleViewerWidget::setArticleActionsEnabled(bool enabled) 0381 { 0382 ActionManager::getInstance()->setArticleActionsEnabled(enabled); 0383 } 0384 0385 Akregator::ArticleViewerWebEngineWidgetNg *ArticleViewerWidget::articleViewerWidgetNg() const 0386 { 0387 return m_articleViewerWidgetNg; 0388 } 0389 0390 #include "moc_articleviewerwidget.cpp"