25 #include <tqdatetime.h>
27 #include <tqscrollview.h>
28 #include <tqvaluelist.h>
31 #include <kapplication.h>
33 #include <kglobalsettings.h>
34 #include <khtmlview.h>
38 #include <kstandarddirs.h>
40 #include <kmessagebox.h>
41 #include <kio/netaccess.h>
42 #include <libkdepim/kfileio.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
70 virtual bool visitFeed(Feed* node)
72 m_view->m_link = TQString();
75 text = TQString(
"<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ?
"rtl" :
"ltr");
77 text += TQString(
"<div class=\"headertitle\" dir=\"%1\">").arg(directionOf(Utils::stripTags(node->title())));
78 text += node->title();
79 if(node->unread() == 0)
80 text += i18n(
" (no unread articles)");
82 text += i18n(
" (1 unread article)",
" (%n unread articles)", node->unread());
86 if (!node->image().isNull())
88 text += TQString(
"<div class=\"body\">");
89 TQString url=node->xmlUrl();
90 TQString file = url.replace(
"/",
"_").replace(
":",
"_");
91 KURL u(m_view->m_imageDir);
93 text += TQString(
"<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(node->htmlUrl()).arg(u.url());
95 else text +=
"<div class=\"body\">";
98 if( !node->description().isEmpty() )
100 text += TQString(
"<div dir=\"%1\">").arg(Utils::stripTags(directionOf(node->description())));
101 text += i18n(
"<b>Description:</b> %1<br><br>").arg(node->description());
105 if ( !node->htmlUrl().isEmpty() )
107 text += TQString(
"<div dir=\"%1\">").arg(directionOf(node->htmlUrl()));
108 text += i18n(
"<b>Homepage:</b> <a href=\"%1\">%2</a>").arg(node->htmlUrl()).arg(node->htmlUrl());
115 m_view->renderContent(text);
119 virtual bool visitFolder(Folder* node)
121 m_view->m_link = TQString();
124 text = TQString(
"<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ?
"rtl" :
"ltr");
125 text += TQString(
"<div class=\"headertitle\" dir=\"%1\">%2").arg(directionOf(Utils::stripTags(node->title()))).arg(node->title());
126 if(node->unread() == 0)
127 text += i18n(
" (no unread articles)");
129 text += i18n(
" (1 unread article)",
" (%n unread articles)", node->unread());
130 text += TQString(
"</div>\n");
133 m_view->renderContent(text);
137 virtual bool visitTagNode(TagNode* node)
139 m_view->m_link = TQString();
142 text = TQString(
"<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ?
"rtl" :
"ltr");
143 text += TQString(
"<div class=\"headertitle\" dir=\"%1\">%2").arg(directionOf(Utils::stripTags(node->title()))).arg(node->title());
144 if(node->unread() == 0)
145 text += i18n(
" (no unread articles)");
147 text += i18n(
" (1 unread article)",
" (%n unread articles)", node->unread());
148 text += TQString(
"</div>\n");
151 m_view->renderContent(text);
161 : Viewer(parent, name), m_htmlFooter(), m_currentText(), m_node(0), m_viewMode(NormalView)
163 setJScriptEnabled(
false);
164 setJavaEnabled(
false);
165 setPluginsEnabled(
false);
167 m_showSummaryVisitor =
new ShowSummaryVisitor(
this);
168 setXMLFile(locate(
"data",
"akregator/articleviewer.rc"),
true);
172 new KAction( i18n(
"&Scroll Up"), TQString(),
"Up",
this, TQT_SLOT(slotScrollUp()), actionCollection(),
"articleviewer_scroll_up" );
173 new KAction( i18n(
"&Scroll Down"), TQString(),
"Down",
this, TQT_SLOT(slotScrollDown()), actionCollection(),
"articleviewer_scroll_down" );
175 connect(
this, TQT_SIGNAL(selectionChanged()),
this, TQT_SLOT(slotSelectionChanged()));
177 connect(kapp, TQT_SIGNAL(kdisplayPaletteChanged()),
this, TQT_SLOT(slotPaletteOrFontChanged()) );
178 connect(kapp, TQT_SIGNAL(kdisplayFontChanged()),
this, TQT_SLOT(slotPaletteOrFontChanged()) );
180 m_imageDir.setPath(KGlobal::dirs()->saveLocation(
"cache",
"akregator/Media/"));
181 m_htmlFooter =
"</body></html>";
184 ArticleViewer::~ArticleViewer()
186 delete m_showSummaryVisitor;
191 const TQColorGroup & cg = TQApplication::palette().active();
194 m_normalModeCSS = TQString(
195 "@media screen, print {"
197 " font-family: \"%1\" ! important;\n"
198 " font-size: %2 ! important;\n"
199 " color: %3 ! important;\n"
200 " background: %4 ! important;\n"
201 "}\n\n").arg(Settings::standardFont())
202 .arg(TQString::number(pointsToPixel(Settings::mediumFontSize()))+
"px")
203 .arg(cg.text().name())
204 .arg(cg.base().name());
205 m_normalModeCSS += TQString(
207 + TQString(
" color: %1 ! important;\n")
208 + TQString(!Settings::underlineLinks() ?
" text-decoration: none ! important;\n" :
"")
211 +
" background: %2 ! important;\n"
212 +
" color: %3 ! important;\n"
213 +
" border:1px solid #000;\n"
214 +
" margin-bottom: 10pt;\n"
217 .arg(cg.link().name())
218 .arg(cg.background().name())
219 .arg(cg.text().name());
221 m_normalModeCSS += TQString(
".headertitle a:link { color: %1 ! important; }\n"
222 ".headertitle a:visited { color: %2 ! important; }\n"
223 ".headertitle a:hover{ color: %3 ! important; }\n"
224 ".headertitle a:active { color: %4 ! important; }\n")
225 .arg(cg.highlightedText().name())
226 .arg(cg.highlightedText().name())
227 .arg(cg.highlightedText().name())
228 .arg(cg.highlightedText().name());
230 m_normalModeCSS += TQString(
232 " background: %1 ! important;\n"
234 " color: %2 ! important;\n"
235 " font-weight: bold;\n"
238 " font-weight: bold;\n"
240 " margin-right: 5px;\n"
246 " margin-left: 5px;\n"
247 "}\n\n").arg(cg.highlight().name())
248 .arg(cg.highlightedText().name());
250 m_normalModeCSS += TQString(
251 "body { clear: none; }\n\n"
254 " margin-bottom: 6px;\n"
257 ".content > P:first-child {\n margin-top: 1px; }\n"
258 ".content > DIV:first-child {\n margin-top: 1px; }\n"
259 ".content > BR:first-child {\n display: none; }\n"
260 "iframe {display: none !important; }\n"
261 "frame {display: none !important; }\n"
262 "frameset {display: none !important; }\n"
263 "object {display: none !important; }\n"
264 "applet {display: none !important; }\n"
270 const TQColorGroup & cg = TQApplication::palette().active();
273 m_combinedModeCSS = TQString (
275 "@media screen, print {"
277 " font-family: \"%1\" ! important;\n"
278 " font-size: %2 ! important;\n"
279 " color: %3 ! important;\n"
280 " background: %4 ! important;\n"
281 "}\n\n").arg(Settings::standardFont())
282 .arg(TQString::number(pointsToPixel(Settings::mediumFontSize()))+
"px")
283 .arg(cg.text().name())
284 .arg(cg.base().name());
285 m_combinedModeCSS += (
287 + TQString(
" color: %1 ! important;\n")
288 + TQString(!Settings::underlineLinks() ?
" text-decoration: none ! important;\n" :
"")
291 +
" background: %2 ! important;\n"
292 +
" color: %3 ! important;\n"
293 +
" border:1px solid #000;\n"
294 +
" margin-bottom: 10pt;\n"
297 .arg(cg.link().name())
298 .arg(cg.background().name())
299 .arg(cg.text().name());
301 m_combinedModeCSS += TQString(
".headertitle a:link { color: %1 ! important; }\n"
302 ".headertitle a:visited { color: %2 ! important; }\n"
303 ".headertitle a:hover{ color: %3 ! important; }\n"
304 ".headertitle a:active { color: %4 ! important; }\n")
305 .arg(cg.highlightedText().name())
306 .arg(cg.highlightedText().name())
307 .arg(cg.highlightedText().name())
308 .arg(cg.highlightedText().name());
309 m_combinedModeCSS += TQString(
311 " background: %1 ! important;\n"
313 " color: %2 ! important;\n"
314 " font-weight: bold;\n"
317 " font-weight: bold;\n"
319 " margin-right: 5px;\n"
325 " margin-left: 5px;\n"
326 "}\n\n").arg(cg.highlight().name())
327 .arg(cg.highlightedText().name());
329 m_combinedModeCSS += TQString(
330 "body { clear: none; }\n\n"
333 " margin-bottom: 6px;\n"
336 ".content > P:first-child {\n margin-top: 1px; }\n"
337 ".content > DIV:first-child {\n margin-top: 1px; }\n"
338 ".content > BR:first-child {\n display: none; }\n"
339 "iframe {display: none !important; }\n"
340 "frame {display: none !important; }\n"
341 "frameset {display: none !important; }\n"
342 "object {display: none !important; }\n"
343 "applet {display: none !important; }\n"
350 write(m_currentText);
354 bool ArticleViewer::openURL(
const KURL& url)
356 if (!m_article.isNull() && m_article.feed()->loadLinkedWebsite())
358 return Viewer::openURL(url);
367 void ArticleViewer::displayAboutPage()
369 TQString location = locate(
"data",
"akregator/about/main.html");
370 TQString content = KPIM::kFileToString(location);
371 content = content.arg( locate(
"data",
"libkdepim/about/kde_infopage.css" ) );
372 if ( kapp->reverseLayout() )
373 content = content.arg(
"@import \"%1\";" ).arg( locate(
"data",
"libkdepim/about/kde_infopage_rtl.css" ) );
375 content = content.arg(
"" );
377 begin(KURL( location ));
379 i18n(
"%1: Akregator version; %2: help:// URL; %3: homepage URL; "
380 "--- end of comment ---",
381 "<h2 style='margin-top: 0px;'>Welcome to Akregator %1</h2>"
382 "<p>Akregator is an RSS feed aggregator for the K Desktop Environment. "
383 "Feed aggregators provide a convenient way to browse different kinds of "
384 "content, including news, blogs, and other content from online sites. "
385 "Instead of checking all your favorite web sites manually for updates, "
386 "Akregator collects the content for you.</p>"
387 "<p>For more information about using Akregator, check the "
388 "<a href=\"%3\">Akregator website</a>. If you do not want to see this page anymore, <a href=\"config:/disable_introduction\">click here</a>.</p>"
389 "<p>We hope that you will enjoy Akregator.</p>\n"
390 "<p>Thank you,</p>\n"
391 "<p style='margin-bottom: 0px'> The Akregator Team</p>\n")
392 .arg(AKREGATOR_VERSION)
393 .arg(
"http://akregator.kde.org/");
395 TQString fontSize = TQString::number( pointsToPixel( Settings::mediumFontSize() ));
396 TQString appTitle = i18n(
"Akregator");
397 TQString catchPhrase =
"";
398 TQString quickDescription = i18n(
"An RSS feed reader for the K Desktop Environment.");
399 write(content.arg(fontSize).arg(appTitle).arg(catchPhrase).arg(quickDescription).arg(info));
406 text = TQString(
"<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ?
"rtl" :
"ltr");
408 if (!article.title().isEmpty())
410 text += TQString(
"<div class=\"headertitle\" dir=\"%1\">\n").arg(directionOf(Utils::stripTags(article.title())));
411 if (article.link().isValid())
412 text +=
"<a href=\""+article.link().url()+
"\">";
413 text += article.title().replace(
"<",
"<").replace(
">",
">");
414 if (article.link().isValid())
418 if (article.pubDate().isValid())
420 text += TQString(
"<span class=\"header\" dir=\"%1\">").arg(directionOf(i18n(
"Date")));
421 text += TQString (
"%1:").arg(i18n(
"Date"));
422 text +=
"</span><span class=\"headertext\">";
423 text += KGlobal::locale()->formatDateTime(article.pubDate(),
false,
false)+
"</span>\n";
425 TQString author = article.author();
426 if (!author.isEmpty())
428 text += TQString(
"<br/><span class=\"header\" dir=\"%1\">").arg(directionOf(i18n(
"Author")));
429 text += TQString (
"%1:").arg(i18n(
"Author"));
430 text +=
"</span><span class=\"headertext\">";
431 text += author+
"</span>\n";
435 if (feed && !feed->
image().isNull())
437 TQString file = Utils::fileNameForUrl(feed->
xmlUrl());
440 text += TQString(
"<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->
htmlUrl()).arg(u.url());
445 if (!article.description().isEmpty())
447 text += TQString(
"<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) );
448 text +=
"<span class=\"content\">"+article.description()+
"</span>";
452 text +=
"<div class=\"body\">";
454 if (article.commentsLink().isValid())
456 text +=
"<a class=\"contentlink\" href=\"";
457 text += article.commentsLink().url();
458 text +=
"\">" + i18n(
"Comments");
459 if (article.comments())
461 text +=
" ("+ TQString::number(article.comments()) +
")";
466 if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
468 text +=
"<p><a class=\"contentlink\" href=\"";
470 if (article.link().isValid())
472 text += article.link().url();
476 text += article.guid();
478 text +=
"\">" + i18n(
"Complete Story" ) +
"</a></p>";
482 if (!article.enclosure().isNull())
499 text = TQString(
"<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ?
"rtl" :
"ltr");
501 KURL link = article.link();
503 if (!article.title().isEmpty())
505 text += TQString(
"<div class=\"headertitle\" dir=\"%1\">\n").arg(directionOf(Utils::stripTags(article.title())));
507 text +=
"<a href=\""+link.url()+
"\">";
508 text += article.title().replace(
"<",
"<").replace(
">",
">");
513 if (article.pubDate().isValid())
515 text += TQString(
"<span class=\"header\" dir=\"%1\">").arg(directionOf(i18n(
"Date")));
516 text += TQString (
"%1:").arg(i18n(
"Date"));
517 text +=
"</span><span class=\"headertext\">";
518 text += KGlobal::locale()->formatDateTime(article.pubDate(),
false,
false)+
"</span>\n";
521 TQString author = article.author();
522 if (!author.isEmpty())
524 text += TQString(
"<br/><span class=\"header\" dir=\"%1\">").arg(directionOf(i18n(
"Author")));
525 text += TQString (
"%1:").arg(i18n(
"Author"));
526 text +=
"</span><span class=\"headertext\">";
527 text += author+
"</span>\n";
532 if (feed && !feed->
image().isNull())
534 TQString file = Utils::fileNameForUrl(feed->
xmlUrl());
537 text += TQString(
"<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->
htmlUrl()).arg(u.url());
542 if (!article.description().isEmpty())
544 text += TQString(
"<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) );
545 text +=
"<span class=\"content\">"+article.description()+
"</span>";
549 text +=
"<div class=\"body\">";
551 if (article.commentsLink().isValid())
553 text +=
"<a class=\"contentlink\" href=\"";
554 text += article.commentsLink().url();
555 text +=
"\">" + i18n(
"Comments");
556 if (article.comments())
558 text +=
" ("+ TQString::number(article.comments()) +
")";
563 if (link.isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
565 text +=
"<p><a class=\"contentlink\" href=\"";
573 text += article.guid();
575 text +=
"\">" + i18n(
"Complete Story" ) +
"</a></p>";
586 m_currentText = text;
595 TQString head = TQString(
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n <html><head><title>.</title></head>");
596 view()->setContentsPos(0,0);
598 setUserStyleSheet(m_viewMode == CombinedView ? m_combinedModeCSS : m_normalModeCSS);
609 void ArticleViewer::slotShowSummary(
TreeNode* node)
611 m_viewMode = SummaryView;
621 disconnectFromNode(m_node);
625 m_showSummaryVisitor->visit(node);
631 m_viewMode = NormalView;
632 disconnectFromNode(m_node);
635 m_link = article.link();
636 if (article.feed()->loadLinkedWebsite())
637 openURL(article.link());
644 if (m_statusFilter == statusFilter && m_textFilter == textFilter)
647 m_textFilter = textFilter;
648 m_statusFilter = statusFilter;
655 if (m_viewMode != CombinedView)
661 TQValueList<Article> articles = m_node->
articles();
663 TQValueList<Article>::ConstIterator end = articles.end();
664 TQValueList<Article>::ConstIterator it = articles.begin();
672 for ( ; it != end; ++it)
674 if ( !(*it).isDeleted() && m_textFilter.matches(*it) && m_statusFilter.matches(*it) )
687 void ArticleViewer::slotArticlesUpdated(
TreeNode* ,
const TQValueList<Article>& )
689 if (m_viewMode == CombinedView)
693 void ArticleViewer::slotArticlesAdded(TreeNode* ,
const TQValueList<Article>& )
697 void ArticleViewer::slotArticlesRemoved(TreeNode* ,
const TQValueList<Article>& )
711 disconnectFromNode(m_node);
720 m_viewMode = CombinedView;
723 disconnectFromNode(m_node);
730 if (node && !node->
articles().isEmpty())
731 m_link = node->
articles().first().link();
738 void ArticleViewer::keyPressEvent(TQKeyEvent* e)
743 void ArticleViewer::urlSelected(
const TQString &url,
int button,
int state,
const TQString& _target, KParts::URLArgs args)
745 if(url ==
"config:/disable_introduction") {
746 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) {
747 KConfig *conf = Settings::self()->config();
748 conf->setGroup(
"General");
749 conf->writeEntry(
"Disable Introduction",
"true");
753 Viewer::urlSelected(url, button, state, _target, args);
756 void ArticleViewer::slotPaletteOrFontChanged()
763 void ArticleViewer::connectToNode(TreeNode* node)
767 if (m_viewMode == CombinedView)
770 connect( node, TQT_SIGNAL(signalArticlesAdded(TreeNode*,
const TQValueList<Article>&)),
this, TQT_SLOT(slotArticlesAdded(TreeNode*,
const TQValueList<Article>&)));
771 connect( node, TQT_SIGNAL(signalArticlesRemoved(TreeNode*,
const TQValueList<Article>&)),
this, TQT_SLOT(slotArticlesRemoved(TreeNode*,
const TQValueList<Article>&)));
772 connect( node, TQT_SIGNAL(signalArticlesUpdated(TreeNode*,
const TQValueList<Article>&)),
this, TQT_SLOT(slotArticlesUpdated(TreeNode*,
const TQValueList<Article>&)));
774 if (m_viewMode == SummaryView)
775 connect( node, TQT_SIGNAL(signalChanged(TreeNode*)),
this, TQT_SLOT(slotShowSummary(TreeNode*) ) );
777 connect( node, TQT_SIGNAL(signalDestroyed(TreeNode*)),
this, TQT_SLOT(
slotClear() ) );
781 void ArticleViewer::disconnectFromNode(TreeNode* node)
786 disconnect( node, TQT_SIGNAL(signalDestroyed(TreeNode*)),
this, TQT_SLOT(
slotClear() ) );
787 disconnect( node, TQT_SIGNAL(signalChanged(TreeNode*)),
this, TQT_SLOT(slotShowSummary(TreeNode*) ) );
788 disconnect( node, TQT_SIGNAL(signalArticlesAdded(TreeNode*,
const TQValueList<Article>&)),
this, TQT_SLOT(slotArticlesAdded(TreeNode*,
const TQValueList<Article>&)));
789 disconnect( node, TQT_SIGNAL(signalArticlesRemoved(TreeNode*,
const TQValueList<Article>&)),
this, TQT_SLOT(slotArticlesRemoved(TreeNode*,
const TQValueList<Article>&)));
790 disconnect( node, TQT_SIGNAL(signalArticlesUpdated(TreeNode*,
const TQValueList<Article>&)),
this, TQT_SLOT(slotArticlesUpdated(TreeNode*,
const TQValueList<Article>&)));
796 #include "articleviewer.moc"