25 #include <tqdatetime.h>
27 #include <tqscrollview.h>
28 #include <tqvaluelist.h>
30 #include <tdeaction.h>
31 #include <tdeapplication.h>
33 #include <tdeglobalsettings.h>
34 #include <tdehtmlview.h>
35 #include <tdelocale.h>
36 #include <tdeprocess.h>
38 #include <kstandarddirs.h>
40 #include <tdemessagebox.h>
41 #include <tdeio/netaccess.h>
42 #include <libtdepim/tdefileio.h>
44 #include "aboutdata.h"
45 #include "akregator_run.h"
46 #include "akregatorconfig.h"
47 #include "articleviewer.h"
52 #include "treenodevisitor.h"
59 static inline TQString directionOf(
const TQString &str)
61 return str.isRightToLeft() ?
"rtl" :
"ltr" ;
64 class ArticleViewer::ShowSummaryVisitor :
public TreeNodeVisitor
69 virtual ~ShowSummaryVisitor() {}
71 virtual bool visitFeed(Feed* node)
73 m_view->m_link = TQString();
76 text = TQString(
"<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ?
"rtl" :
"ltr");
78 text += TQString(
"<div class=\"headertitle\" dir=\"%1\">").arg(directionOf(Utils::stripTags(node->title())));
79 text += node->title();
80 if(node->unread() == 0)
81 text += i18n(
" (no unread articles)");
83 text += i18n(
" (1 unread article)",
" (%n unread articles)", node->unread());
87 if (!node->image().isNull())
89 text += TQString(
"<div class=\"body\">");
90 TQString url=node->xmlUrl();
91 TQString file = url.replace(
"/",
"_").replace(
":",
"_");
92 KURL u(m_view->m_imageDir);
94 text += TQString(
"<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(node->htmlUrl()).arg(u.url());
96 else text +=
"<div class=\"body\">";
99 if( !node->description().isEmpty() )
101 text += TQString(
"<div dir=\"%1\">").arg(Utils::stripTags(directionOf(node->description())));
102 text += i18n(
"<b>Description:</b> %1<br><br>").arg(node->description());
106 if ( !node->htmlUrl().isEmpty() )
108 text += TQString(
"<div dir=\"%1\">").arg(directionOf(node->htmlUrl()));
109 text += i18n(
"<b>Homepage:</b> <a href=\"%1\">%2</a>").arg(node->htmlUrl()).arg(node->htmlUrl());
116 m_view->renderContent(text);
120 virtual bool visitFolder(Folder* node)
122 m_view->m_link = TQString();
125 text = TQString(
"<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ?
"rtl" :
"ltr");
126 text += TQString(
"<div class=\"headertitle\" dir=\"%1\">%2").arg(directionOf(Utils::stripTags(node->title()))).arg(node->title());
127 if(node->unread() == 0)
128 text += i18n(
" (no unread articles)");
130 text += i18n(
" (1 unread article)",
" (%n unread articles)", node->unread());
131 text += TQString(
"</div>\n");
134 m_view->renderContent(text);
138 virtual bool visitTagNode(TagNode* node)
140 m_view->m_link = TQString();
143 text = TQString(
"<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ?
"rtl" :
"ltr");
144 text += TQString(
"<div class=\"headertitle\" dir=\"%1\">%2").arg(directionOf(Utils::stripTags(node->title()))).arg(node->title());
145 if(node->unread() == 0)
146 text += i18n(
" (no unread articles)");
148 text += i18n(
" (1 unread article)",
" (%n unread articles)", node->unread());
149 text += TQString(
"</div>\n");
152 m_view->renderContent(text);
162 : Viewer(parent, name), m_htmlFooter(), m_currentText(), m_node(0), m_viewMode(NormalView)
164 setJScriptEnabled(
false);
165 setJavaEnabled(
false);
166 setPluginsEnabled(
false);
168 m_showSummaryVisitor =
new ShowSummaryVisitor(
this);
169 setXMLFile(locate(
"data",
"akregator/articleviewer.rc"),
true);
173 new TDEAction( i18n(
"&Scroll Up"), TQString(),
"Up",
this, TQ_SLOT(slotScrollUp()), actionCollection(),
"articleviewer_scroll_up" );
174 new TDEAction( i18n(
"&Scroll Down"), TQString(),
"Down",
this, TQ_SLOT(slotScrollDown()), actionCollection(),
"articleviewer_scroll_down" );
176 connect(
this, TQ_SIGNAL(selectionChanged()),
this, TQ_SLOT(slotSelectionChanged()));
178 connect(kapp, TQ_SIGNAL(tdedisplayPaletteChanged()),
this, TQ_SLOT(slotPaletteOrFontChanged()) );
179 connect(kapp, TQ_SIGNAL(tdedisplayFontChanged()),
this, TQ_SLOT(slotPaletteOrFontChanged()) );
181 m_imageDir.setPath(TDEGlobal::dirs()->saveLocation(
"cache",
"akregator/Media/"));
182 m_htmlFooter =
"</body></html>";
185 ArticleViewer::~ArticleViewer()
187 delete m_showSummaryVisitor;
192 const TQColorGroup & cg = TQApplication::palette().active();
195 m_normalModeCSS = TQString(
196 "@media screen, print {"
198 " font-family: \"%1\" ! important;\n"
199 " font-size: %2 ! important;\n"
200 " color: %3 ! important;\n"
201 " background: %4 ! important;\n"
202 "}\n\n").arg(Settings::standardFont())
203 .arg(TQString::number(pointsToPixel(Settings::mediumFontSize()))+
"px")
204 .arg(cg.text().name())
205 .arg(cg.base().name());
206 m_normalModeCSS += TQString(
208 + TQString(
" color: %1 ! important;\n")
209 + TQString(!Settings::underlineLinks() ?
" text-decoration: none ! important;\n" :
"")
212 +
" background: %2 ! important;\n"
213 +
" color: %3 ! important;\n"
214 +
" border:1px solid #000;\n"
215 +
" margin-bottom: 10pt;\n"
218 .arg(cg.link().name())
219 .arg(cg.background().name())
220 .arg(cg.text().name());
222 m_normalModeCSS += TQString(
".headertitle a:link { color: %1 ! important; }\n"
223 ".headertitle a:visited { color: %2 ! important; }\n"
224 ".headertitle a:hover{ color: %3 ! important; }\n"
225 ".headertitle a:active { color: %4 ! important; }\n")
226 .arg(cg.highlightedText().name())
227 .arg(cg.highlightedText().name())
228 .arg(cg.highlightedText().name())
229 .arg(cg.highlightedText().name());
231 m_normalModeCSS += TQString(
233 " background: %1 ! important;\n"
235 " color: %2 ! important;\n"
236 " font-weight: bold;\n"
239 " font-weight: bold;\n"
241 " margin-right: 5px;\n"
247 " margin-left: 5px;\n"
248 "}\n\n").arg(cg.highlight().name())
249 .arg(cg.highlightedText().name());
251 m_normalModeCSS += TQString(
252 "body { clear: none; }\n\n"
255 " margin-bottom: 6px;\n"
258 ".content > P:first-child {\n margin-top: 1px; }\n"
259 ".content > DIV:first-child {\n margin-top: 1px; }\n"
260 ".content > BR:first-child {\n display: none; }\n"
261 "iframe {display: none !important; }\n"
262 "frame {display: none !important; }\n"
263 "frameset {display: none !important; }\n"
264 "object {display: none !important; }\n"
265 "applet {display: none !important; }\n"
271 const TQColorGroup & cg = TQApplication::palette().active();
274 m_combinedModeCSS = TQString (
276 "@media screen, print {"
278 " font-family: \"%1\" ! important;\n"
279 " font-size: %2 ! important;\n"
280 " color: %3 ! important;\n"
281 " background: %4 ! important;\n"
282 "}\n\n").arg(Settings::standardFont())
283 .arg(TQString::number(pointsToPixel(Settings::mediumFontSize()))+
"px")
284 .arg(cg.text().name())
285 .arg(cg.base().name());
286 m_combinedModeCSS += (
288 + TQString(
" color: %1 ! important;\n")
289 + TQString(!Settings::underlineLinks() ?
" text-decoration: none ! important;\n" :
"")
292 +
" background: %2 ! important;\n"
293 +
" color: %3 ! important;\n"
294 +
" border:1px solid #000;\n"
295 +
" margin-bottom: 10pt;\n"
298 .arg(cg.link().name())
299 .arg(cg.background().name())
300 .arg(cg.text().name());
302 m_combinedModeCSS += TQString(
".headertitle a:link { color: %1 ! important; }\n"
303 ".headertitle a:visited { color: %2 ! important; }\n"
304 ".headertitle a:hover{ color: %3 ! important; }\n"
305 ".headertitle a:active { color: %4 ! important; }\n")
306 .arg(cg.highlightedText().name())
307 .arg(cg.highlightedText().name())
308 .arg(cg.highlightedText().name())
309 .arg(cg.highlightedText().name());
310 m_combinedModeCSS += TQString(
312 " background: %1 ! important;\n"
314 " color: %2 ! important;\n"
315 " font-weight: bold;\n"
318 " font-weight: bold;\n"
320 " margin-right: 5px;\n"
326 " margin-left: 5px;\n"
327 "}\n\n").arg(cg.highlight().name())
328 .arg(cg.highlightedText().name());
330 m_combinedModeCSS += TQString(
331 "body { clear: none; }\n\n"
334 " margin-bottom: 6px;\n"
337 ".content > P:first-child {\n margin-top: 1px; }\n"
338 ".content > DIV:first-child {\n margin-top: 1px; }\n"
339 ".content > BR:first-child {\n display: none; }\n"
340 "iframe {display: none !important; }\n"
341 "frame {display: none !important; }\n"
342 "frameset {display: none !important; }\n"
343 "object {display: none !important; }\n"
344 "applet {display: none !important; }\n"
351 write(m_currentText);
355 bool ArticleViewer::openURL(
const KURL& url)
357 if (!m_article.isNull() && m_article.feed()->loadLinkedWebsite())
359 return Viewer::openURL(url);
368 void ArticleViewer::displayAboutPage()
370 TQString location = locate(
"data",
"akregator/about/main.html");
371 TQString content = KPIM::kFileToString(location);
372 content = content.arg( locate(
"data",
"libtdepim/about/kde_infopage.css" ) );
373 if ( kapp->reverseLayout() )
374 content = content.arg(
"@import \"%1\";" ).arg( locate(
"data",
"libtdepim/about/kde_infopage_rtl.css" ) );
376 content = content.arg(
"" );
378 begin(KURL( location ));
380 i18n(
"%1: Akregator version; %2: help:// URL; %3: homepage URL; "
381 "--- end of comment ---",
382 "<h2 style='margin-top: 0px;'>Welcome to Akregator %1</h2>"
383 "<p>Akregator is an RSS feed aggregator for the Trinity Desktop Environment. "
384 "Feed aggregators provide a convenient way to browse different kinds of "
385 "content, including news, blogs, and other content from online sites. "
386 "Instead of checking all your favorite web sites manually for updates, "
387 "Akregator collects the content for you.</p>"
388 "<p>For more information about using Akregator, check the "
389 "<a href=\"%3\">Trinity website</a>. If you do not want to see this page anymore, <a href=\"config:/disable_introduction\">click here</a>.</p>"
390 "<p>We hope that you will enjoy Akregator.</p>\n"
391 "<p>Thank you,</p>\n"
392 "<p style='margin-bottom: 0px'> The Trinity Team</p>\n")
393 .arg(AKREGATOR_VERSION)
394 .arg(
"https://trinitydesktop.org/");
396 TQString fontSize = TQString::number( pointsToPixel( Settings::mediumFontSize() ));
397 TQString appTitle = i18n(
"Akregator");
398 TQString catchPhrase =
"";
399 TQString quickDescription = i18n(
"An RSS feed reader for the Trinity Desktop Environment.");
400 write(content.arg(fontSize).arg(appTitle).arg(catchPhrase).arg(quickDescription).arg(info));
407 text = TQString(
"<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ?
"rtl" :
"ltr");
409 if (!article.title().isEmpty())
411 text += TQString(
"<div class=\"headertitle\" dir=\"%1\">\n").arg(directionOf(Utils::stripTags(article.title())));
412 if (article.link().isValid())
413 text +=
"<a href=\""+article.link().url()+
"\">";
414 text += article.title().replace(
"<",
"<").replace(
">",
">");
415 if (article.link().isValid())
419 if (article.pubDate().isValid())
421 text += TQString(
"<span class=\"header\" dir=\"%1\">").arg(directionOf(i18n(
"Date")));
422 text += TQString (
"%1:").arg(i18n(
"Date"));
423 text +=
"</span><span class=\"headertext\">";
424 text += TDEGlobal::locale()->formatDateTime(article.pubDate(),
false,
false)+
"</span>\n";
426 TQString author = article.author();
427 if (!author.isEmpty())
429 text += TQString(
"<br/><span class=\"header\" dir=\"%1\">").arg(directionOf(i18n(
"Author")));
430 text += TQString (
"%1:").arg(i18n(
"Author"));
431 text +=
"</span><span class=\"headertext\">";
432 text += author+
"</span>\n";
436 if (feed && !feed->
image().isNull())
438 TQString file = Utils::fileNameForUrl(feed->
xmlUrl());
441 text += TQString(
"<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->
htmlUrl()).arg(u.url());
446 if (!article.description().isEmpty())
448 text += TQString(
"<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) );
449 text +=
"<span class=\"content\">"+article.description()+
"</span>";
453 text +=
"<div class=\"body\">";
455 if (article.commentsLink().isValid())
457 text +=
"<a class=\"contentlink\" href=\"";
458 text += article.commentsLink().url();
459 text +=
"\">" + i18n(
"Comments");
460 if (article.comments())
462 text +=
" ("+ TQString::number(article.comments()) +
")";
467 if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
469 text +=
"<p><a class=\"contentlink\" href=\"";
471 if (article.link().isValid())
473 text += article.link().url();
477 text += article.guid();
479 text +=
"\">" + i18n(
"Complete Story" ) +
"</a></p>";
483 if (!article.enclosure().isNull())
500 text = TQString(
"<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ?
"rtl" :
"ltr");
502 KURL link = article.link();
504 if (!article.title().isEmpty())
506 text += TQString(
"<div class=\"headertitle\" dir=\"%1\">\n").arg(directionOf(Utils::stripTags(article.title())));
508 text +=
"<a href=\""+link.url()+
"\">";
509 text += article.title().replace(
"<",
"<").replace(
">",
">");
514 if (article.pubDate().isValid())
516 text += TQString(
"<span class=\"header\" dir=\"%1\">").arg(directionOf(i18n(
"Date")));
517 text += TQString (
"%1:").arg(i18n(
"Date"));
518 text +=
"</span><span class=\"headertext\">";
519 text += TDEGlobal::locale()->formatDateTime(article.pubDate(),
false,
false)+
"</span>\n";
522 TQString author = article.author();
523 if (!author.isEmpty())
525 text += TQString(
"<br/><span class=\"header\" dir=\"%1\">").arg(directionOf(i18n(
"Author")));
526 text += TQString (
"%1:").arg(i18n(
"Author"));
527 text +=
"</span><span class=\"headertext\">";
528 text += author+
"</span>\n";
533 if (feed && !feed->
image().isNull())
535 TQString file = Utils::fileNameForUrl(feed->
xmlUrl());
538 text += TQString(
"<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->
htmlUrl()).arg(u.url());
543 if (!article.description().isEmpty())
545 text += TQString(
"<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) );
546 text +=
"<span class=\"content\">"+article.description()+
"</span>";
550 text +=
"<div class=\"body\">";
552 if (article.commentsLink().isValid())
554 text +=
"<a class=\"contentlink\" href=\"";
555 text += article.commentsLink().url();
556 text +=
"\">" + i18n(
"Comments");
557 if (article.comments())
559 text +=
" ("+ TQString::number(article.comments()) +
")";
564 if (link.isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
566 text +=
"<p><a class=\"contentlink\" href=\"";
574 text += article.guid();
576 text +=
"\">" + i18n(
"Complete Story" ) +
"</a></p>";
587 m_currentText = text;
596 TQString head = TQString(
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n <html><head><title>.</title></head>");
597 view()->setContentsPos(0,0);
599 setUserStyleSheet(m_viewMode == CombinedView ? m_combinedModeCSS : m_normalModeCSS);
610 void ArticleViewer::slotShowSummary(
TreeNode* node)
612 m_viewMode = SummaryView;
622 disconnectFromNode(m_node);
626 m_showSummaryVisitor->visit(node);
632 m_viewMode = NormalView;
633 disconnectFromNode(m_node);
636 m_link = article.link();
637 if (article.feed()->loadLinkedWebsite())
638 openURL(article.link());
645 if (m_statusFilter == statusFilter && m_textFilter == textFilter)
648 m_textFilter = textFilter;
649 m_statusFilter = statusFilter;
656 if (m_viewMode != CombinedView)
662 TQValueList<Article> articles = m_node->
articles();
664 TQValueList<Article>::ConstIterator end = articles.end();
665 TQValueList<Article>::ConstIterator it = articles.begin();
673 for ( ; it != end; ++it)
675 if ( !(*it).isDeleted() && m_textFilter.matches(*it) && m_statusFilter.matches(*it) )
688 void ArticleViewer::slotArticlesUpdated(
TreeNode* ,
const TQValueList<Article>& )
690 if (m_viewMode == CombinedView)
694 void ArticleViewer::slotArticlesAdded(TreeNode* ,
const TQValueList<Article>& )
698 void ArticleViewer::slotArticlesRemoved(TreeNode* ,
const TQValueList<Article>& )
712 disconnectFromNode(m_node);
721 m_viewMode = CombinedView;
724 disconnectFromNode(m_node);
731 if (node && !node->
articles().isEmpty())
732 m_link = node->
articles().first().link();
739 void ArticleViewer::keyPressEvent(TQKeyEvent* e)
744 void ArticleViewer::urlSelected(
const TQString &url,
int button,
int state,
const TQString& _target, KParts::URLArgs args)
746 if(url ==
"config:/disable_introduction") {
747 if(KMessageBox::questionYesNo( widget(), i18n(
"Are you sure you want to disable this introduction page?"), i18n(
"Disable Introduction Page"), i18n(
"Disable"), i18n(
"Keep Enabled") ) == KMessageBox::Yes) {
748 TDEConfig *conf = Settings::self()->config();
749 conf->setGroup(
"General");
750 conf->writeEntry(
"Disable Introduction",
"true");
754 Viewer::urlSelected(url, button, state, _target, args);
757 void ArticleViewer::slotPaletteOrFontChanged()
764 void ArticleViewer::connectToNode(TreeNode* node)
768 if (m_viewMode == CombinedView)
771 connect( node, TQ_SIGNAL(signalArticlesAdded(TreeNode*,
const TQValueList<Article>&)),
this, TQ_SLOT(slotArticlesAdded(TreeNode*,
const TQValueList<Article>&)));
772 connect( node, TQ_SIGNAL(signalArticlesRemoved(TreeNode*,
const TQValueList<Article>&)),
this, TQ_SLOT(slotArticlesRemoved(TreeNode*,
const TQValueList<Article>&)));
773 connect( node, TQ_SIGNAL(signalArticlesUpdated(TreeNode*,
const TQValueList<Article>&)),
this, TQ_SLOT(slotArticlesUpdated(TreeNode*,
const TQValueList<Article>&)));
775 if (m_viewMode == SummaryView)
776 connect( node, TQ_SIGNAL(signalChanged(TreeNode*)),
this, TQ_SLOT(slotShowSummary(TreeNode*) ) );
778 connect( node, TQ_SIGNAL(signalDestroyed(TreeNode*)),
this, TQ_SLOT(
slotClear() ) );
782 void ArticleViewer::disconnectFromNode(TreeNode* node)
787 disconnect( node, TQ_SIGNAL(signalDestroyed(TreeNode*)),
this, TQ_SLOT(
slotClear() ) );
788 disconnect( node, TQ_SIGNAL(signalChanged(TreeNode*)),
this, TQ_SLOT(slotShowSummary(TreeNode*) ) );
789 disconnect( node, TQ_SIGNAL(signalArticlesAdded(TreeNode*,
const TQValueList<Article>&)),
this, TQ_SLOT(slotArticlesAdded(TreeNode*,
const TQValueList<Article>&)));
790 disconnect( node, TQ_SIGNAL(signalArticlesRemoved(TreeNode*,
const TQValueList<Article>&)),
this, TQ_SLOT(slotArticlesRemoved(TreeNode*,
const TQValueList<Article>&)));
791 disconnect( node, TQ_SIGNAL(signalArticlesUpdated(TreeNode*,
const TQValueList<Article>&)),
this, TQ_SLOT(slotArticlesUpdated(TreeNode*,
const TQValueList<Article>&)));
797 #include "articleviewer.moc"
void slotClear()
Clears the canvas and disconnects from the currently observed node (if in combined view mode).
void generateNormalModeCSS()
generates the CSS used for rendering in single article mode (normal and wide screen view)
void slotUpdateCombinedView()
Update view if combined view mode is set.
ArticleViewer(TQWidget *parent, const char *name)
Constructor.
void reload()
Repaints the view.
TQString formatArticleNormalMode(Feed *feed, const Article &article)
Takes an article and renders it as HTML with settings for normal view and widescreen view.
void generateCombinedModeCSS()
generates the CSS for combined view mode
void beginWriting()
Resets the canvas and adds writes the HTML header to it.
void renderContent(const TQString &body)
renders body.
void slotShowArticle(const Article &article)
Show single article (normal view)
void endWriting()
Finishes writing to the canvas and completes the HTML (by adding closing tags)
TQString formatArticleCombinedMode(Feed *feed, const Article &article)
Takes an article and renders it as HTML with settings for combined view.
void slotSetFilter(const Akregator::Filters::ArticleMatcher &textFilter, const Akregator::Filters::ArticleMatcher &statusFilter)
Set filters textFilter and statusFilter which will be used if the viewer is in combined view mode.
void slotShowNode(TreeNode *node)
Shows the articles of the tree node node (combined view).
A proxy class for RSS::Article with some additional methods to assist sorting.
const TQPixmap & image() const
returns the feed image
const TQString & xmlUrl() const
returns the url of the actual feed source (rss/rdf/atom file)
const TQString & htmlUrl() const
returns the URL of the HTML page of this feed
a powerful matcher supporting multiple criterions, which can be combined via logical OR or AND
Abstract base class for all kind of elements in the feed tree, like feeds and feed groups (and search...
virtual TQValueList< Article > articles(const TQString &tag=TQString())=0
Returns a sequence of the articles this node contains.