akregator/src

articleviewer.cpp
1/*
2 This file is part of Akregator.
3
4 Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net>
5 2005 Frank Osterfeld <frank.osterfeld at kdemail.net>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
20 As a special exception, permission is given to link this program
21 with any edition of TQt, and distribute the resulting executable,
22 without including the source code for TQt in the source distribution.
23*/
24
25#include <tqdatetime.h>
26#include <tqevent.h>
27#include <tqscrollview.h>
28#include <tqvaluelist.h>
29
30#include <tdeaction.h>
31#include <tdeapplication.h>
32#include <kdebug.h>
33#include <tdeglobalsettings.h>
34#include <tdehtmlview.h>
35#include <tdelocale.h>
36#include <tdeprocess.h>
37#include <krun.h>
38#include <tdestandarddirs.h>
39#include <kshell.h>
40#include <tdemessagebox.h>
41#include <tdeio/netaccess.h>
42#include <libtdepim/tdefileio.h>
43
44#include "aboutdata.h"
45#include "akregator_run.h"
46#include "akregatorconfig.h"
47#include "articleviewer.h"
48#include "feed.h"
49#include "folder.h"
50#include "article.h"
51#include "treenode.h"
52#include "treenodevisitor.h"
53#include "tagnode.h"
54#include "utils.h"
55
56namespace Akregator {
57
58// from kmail::headerstyle.cpp
59static inline TQString directionOf(const TQString &str)
60{
61 return str.isRightToLeft() ? "rtl" : "ltr" ;
62}
63
64class ArticleViewer::ShowSummaryVisitor : public TreeNodeVisitor
65{
66 public:
67
68 ShowSummaryVisitor(ArticleViewer* view) : m_view(view) {}
69 virtual ~ShowSummaryVisitor() {}
70
71 virtual bool visitFeed(Feed* node)
72 {
73 m_view->m_link = TQString();
74
75 TQString text;
76 text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ? "rtl" : "ltr");
77
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)");
82 else
83 text += i18n(" (1 unread article)", " (%n unread articles)", node->unread());
84 text += "</div>\n"; // headertitle
85 text += "</div>\n"; // /headerbox
86
87 if (!node->image().isNull()) // image
88 {
89 text += TQString("<div class=\"body\">");
90 TQString url=node->xmlUrl();
91 TQString file = url.replace("/", "_").replace(":", "_");
92 KURL u(m_view->m_imageDir);
93 u.setFileName(file);
94 text += TQString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(node->htmlUrl()).arg(u.url());
95 }
96 else text += "<div class=\"body\">";
97
98
99 if( !node->description().isEmpty() )
100 {
101 text += TQString("<div dir=\"%1\">").arg(Utils::stripTags(directionOf(node->description())));
102 text += i18n("<b>Description:</b> %1<br><br>").arg(node->description());
103 text += "</div>\n"; // /description
104 }
105
106 if ( !node->htmlUrl().isEmpty() )
107 {
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());
110 text += "</div>\n"; // / link
111 }
112
113 //text += i18n("<b>Unread articles:</b> %1").arg(node->unread());
114 text += "</div>"; // /body
115
116 m_view->renderContent(text);
117 return true;
118 }
119
120 virtual bool visitFolder(Folder* node)
121 {
122 m_view->m_link = TQString();
123
124 TQString text;
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)");
129 else
130 text += i18n(" (1 unread article)", " (%n unread articles)", node->unread());
131 text += TQString("</div>\n");
132 text += "</div>\n"; // /headerbox
133
134 m_view->renderContent(text);
135 return true;
136 }
137
138 virtual bool visitTagNode(TagNode* node)
139 {
140 m_view->m_link = TQString();
141
142 TQString text;
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)");
147 else
148 text += i18n(" (1 unread article)", " (%n unread articles)", node->unread());
149 text += TQString("</div>\n");
150 text += "</div>\n"; // /headerbox
151
152 m_view->renderContent(text);
153 return true;
154 }
155
156 private:
157
158 ArticleViewer* m_view;
159};
160
161ArticleViewer::ArticleViewer(TQWidget *parent, const char *name)
162 : Viewer(parent, name), m_htmlFooter(), m_currentText(), m_node(0), m_viewMode(NormalView)
163{
164 setJScriptEnabled(false);
165 setJavaEnabled(false);
166 setPluginsEnabled(false);
167
168 m_showSummaryVisitor = new ShowSummaryVisitor(this);
169 setXMLFile(locate("data", "akregator/articleviewer.rc"), true);
170
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" );
175
176 connect(this, TQ_SIGNAL(selectionChanged()), this, TQ_SLOT(slotSelectionChanged()));
177
178 connect(tdeApp, TQ_SIGNAL(tdedisplayPaletteChanged()), this, TQ_SLOT(slotPaletteOrFontChanged()) );
179 connect(tdeApp, TQ_SIGNAL(tdedisplayFontChanged()), this, TQ_SLOT(slotPaletteOrFontChanged()) );
180
181 m_imageDir.setPath(TDEGlobal::dirs()->saveLocation("cache", "akregator/Media/"));
182 m_htmlFooter = "</body></html>";
183}
184
185ArticleViewer::~ArticleViewer()
186{
187 delete m_showSummaryVisitor;
188}
189
191{
192 const TQColorGroup & cg = TQApplication::palette().active();
193
194 // from kmail::headerstyle.cpp
195 m_normalModeCSS = TQString(
196 "@media screen, print {"
197 "body {\n"
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(
207 "a {\n"
208 + TQString(" color: %1 ! important;\n")
209 + TQString(!Settings::underlineLinks() ? " text-decoration: none ! important;\n" : "")
210 + "}\n\n"
211 +".headerbox {\n"
212 +" background: %2 ! important;\n"
213 +" color: %3 ! important;\n"
214 +" border:1px solid #000;\n"
215 +" margin-bottom: 10pt;\n"
216// +" width: 99%;\n"
217 + "}\n\n")
218 .arg(cg.link().name())
219 .arg(cg.background().name())
220 .arg(cg.text().name());
221
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());
230
231 m_normalModeCSS += TQString(
232 ".headertitle {\n"
233 " background: %1 ! important;\n"
234 " padding:2px;\n"
235 " color: %2 ! important;\n"
236 " font-weight: bold;\n"
237 "}\n\n"
238 ".header {\n"
239 " font-weight: bold;\n"
240 " padding:2px;\n"
241 " margin-right: 5px;\n"
242 "}\n\n"
243 ".headertext {\n"
244 "}\n\n"
245 ".headimage {\n"
246 " float: right;\n"
247 " margin-left: 5px;\n"
248 "}\n\n").arg(cg.highlight().name())
249 .arg(cg.highlightedText().name());
250
251 m_normalModeCSS += TQString(
252 "body { clear: none; }\n\n"
253 ".content {\n"
254 " display: block;\n"
255 " margin-bottom: 6px;\n"
256 "}\n\n"
257 // these rules make sure that there is no leading space between the header and the first of the text
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"
266 "}\n\n"); // @media screen, print
267}
268
270{
271 const TQColorGroup & cg = TQApplication::palette().active();
272
273 // from kmail::headerstyle.cpp
274 m_combinedModeCSS = TQString (
275// "<style type=\"text/css\">\n"
276 "@media screen, print {"
277 "body {\n"
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 += (
287 "a {\n"
288 + TQString(" color: %1 ! important;\n")
289 + TQString(!Settings::underlineLinks() ? " text-decoration: none ! important;\n" : "")
290 + "}\n\n"
291 +".headerbox {\n"
292 +" background: %2 ! important;\n"
293 +" color: %3 ! important;\n"
294 +" border:1px solid #000;\n"
295 +" margin-bottom: 10pt;\n"
296// +" width: 99%;\n"
297 + "}\n\n")
298 .arg(cg.link().name())
299 .arg(cg.background().name())
300 .arg(cg.text().name());
301
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(
311 ".headertitle {\n"
312 " background: %1 ! important;\n"
313 " padding:2px;\n"
314 " color: %2 ! important;\n"
315 " font-weight: bold;\n"
316 "}\n\n"
317 ".header {\n"
318 " font-weight: bold;\n"
319 " padding:2px;\n"
320 " margin-right: 5px;\n"
321 "}\n\n"
322 ".headertext {\n"
323 "}\n\n"
324 ".headimage {\n"
325 " float: right;\n"
326 " margin-left: 5px;\n"
327 "}\n\n").arg(cg.highlight().name())
328 .arg(cg.highlightedText().name());
329
330 m_combinedModeCSS += TQString(
331 "body { clear: none; }\n\n"
332 ".content {\n"
333 " display: block;\n"
334 " margin-bottom: 6px;\n"
335 "}\n\n"
336 // these rules make sure that there is no leading space between the header and the first of the text
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"
345 "}\n\n"); // @media screen, print
346}
347
349{
350 beginWriting();
351 write(m_currentText);
352 endWriting();
353}
354
355bool ArticleViewer::openURL(const KURL& url)
356{
357 if (!m_article.isNull() && m_article.feed()->loadLinkedWebsite())
358 {
359 return Viewer::openURL(url);
360 }
361 else
362 {
363 reload();
364 return true;
365 }
366}
367
368void ArticleViewer::displayAboutPage()
369{
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 ( tdeApp->reverseLayout() )
374 content = content.arg( "@import \"%1\";" ).arg( locate( "data", "libtdepim/about/kde_infopage_rtl.css" ) );
375 else
376 content = content.arg( "" );
377
378 begin(KURL( location ));
379 TQString info =
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'>&nbsp; &nbsp; The Trinity Team</p>\n")
393 .arg(AKREGATOR_VERSION) // Akregator version
394 .arg("https://trinitydesktop.org/"); // Trinity homepage URL
395
396 TQString fontSize = TQString::number( pointsToPixel( Settings::mediumFontSize() ));
397 TQString appTitle = i18n("Akregator");
398 TQString catchPhrase = ""; //not enough space for a catch phrase at default window size i18n("Part of the Kontact Suite");
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));
401 end();
402}
403
405{
406 TQString text;
407 text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ? "rtl" : "ltr");
408
409 if (!article.title().isEmpty())
410 {
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("<", "&lt;").replace(">", "&gt;"); // TODO: better leave things escaped in the parser
415 if (article.link().isValid())
416 text += "</a>";
417 text += "</div>\n";
418 }
419 if (article.pubDate().isValid())
420 {
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"; // TODO: might need RTL?
425 }
426 TQString author = article.author();
427 if (!author.isEmpty())
428 {
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"; // TODO: might need RTL?
433 }
434 text += "</div>\n"; // end headerbox
435
436 if (feed && !feed->image().isNull())
437 {
438 TQString file = Utils::fileNameForUrl(feed->xmlUrl());
439 KURL u(m_imageDir);
440 u.setFileName(file);
441 text += TQString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->htmlUrl()).arg(u.url());
442 }
443
444
445
446 if (!article.description().isEmpty())
447 {
448 text += TQString("<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) );
449 text += "<span class=\"content\">"+article.description()+"</span>";
450 text += "</div>";
451 }
452
453 text += "<div class=\"body\">";
454
455 if (article.commentsLink().isValid())
456 {
457 text += "<a class=\"contentlink\" href=\"";
458 text += article.commentsLink().url();
459 text += "\">" + i18n( "Comments");
460 if (article.comments())
461 {
462 text += " ("+ TQString::number(article.comments()) +")";
463 }
464 text += "</a>";
465 }
466
467 if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
468 {
469 text += "<p><a class=\"contentlink\" href=\"";
470 // in case link isn't valid, fall back to the guid permaLink.
471 if (article.link().isValid())
472 {
473 text += article.link().url();
474 }
475 else
476 {
477 text += article.guid();
478 }
479 text += "\">" + i18n( "Complete Story" ) + "</a></p>";
480 }
481 text += "</div>";
482
483 if (!article.enclosure().isNull())
484 {
485 //TQString url = article.enclosure().url();
486 //TQString type = article.enclosure().type();
487 //int length = article.enclosure().length();
488 //TQString lengthStr = TDEIO::convertSize(length);
489
490 //text += TQString("<hr><div><a href=\"%1\">%2</a> (%3, %4)</div>").arg(url).arg(url).arg(lengthStr).arg(type);
491 }
492 //kdDebug() << text << endl;
493 return text;
494
495}
496
498{
499 TQString text;
500 text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ? "rtl" : "ltr");
501
502 KURL link = article.link();
503
504 if (!article.title().isEmpty())
505 {
506 text += TQString("<div class=\"headertitle\" dir=\"%1\">\n").arg(directionOf(Utils::stripTags(article.title())));
507 if (link.isValid())
508 text += "<a href=\""+link.url()+"\">";
509 text += article.title().replace("<", "&lt;").replace(">", "&gt;"); // TODO: better leave things escaped in the parser
510 if (link.isValid())
511 text += "</a>";
512 text += "</div>\n";
513 }
514 if (article.pubDate().isValid())
515 {
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"; // TODO: might need RTL?
520 }
521
522 TQString author = article.author();
523 if (!author.isEmpty())
524 {
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"; // TODO: might need RTL?
529 }
530
531 text += "</div>\n"; // end headerbox
532
533 if (feed && !feed->image().isNull())
534 {
535 TQString file = Utils::fileNameForUrl(feed->xmlUrl());
536 KURL u(m_imageDir);
537 u.setFileName(file);
538 text += TQString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->htmlUrl()).arg(u.url());
539 }
540
541
542
543 if (!article.description().isEmpty())
544 {
545 text += TQString("<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) );
546 text += "<span class=\"content\">"+article.description()+"</span>";
547 text += "</div>";
548 }
549
550 text += "<div class=\"body\">";
551
552 if (article.commentsLink().isValid())
553 {
554 text += "<a class=\"contentlink\" href=\"";
555 text += article.commentsLink().url();
556 text += "\">" + i18n( "Comments");
557 if (article.comments())
558 {
559 text += " ("+ TQString::number(article.comments()) +")";
560 }
561 text += "</a>";
562 }
563
564 if (link.isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
565 {
566 text += "<p><a class=\"contentlink\" href=\"";
567 // in case link isn't valid, fall back to the guid permaLink.
568 if (link.isValid())
569 {
570 text += link.url();
571 }
572 else
573 {
574 text += article.guid();
575 }
576 text += "\">" + i18n( "Complete Story" ) + "</a></p>";
577 }
578 text += "</div>";
579 //kdDebug() << text << endl;
580 return text;
581
582}
583
584void ArticleViewer::renderContent(const TQString& text)
585{
586 closeURL();
587 m_currentText = text;
588 beginWriting();
589 //kdDebug() << text << endl;
590 write(text);
591 endWriting();
592}
593
595{
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);
598 begin(m_link);
599 setUserStyleSheet(m_viewMode == CombinedView ? m_combinedModeCSS : m_normalModeCSS);
600 write(head);
601}
602
604{
605 write(m_htmlFooter);
606 //kdDebug() << m_htmlFooter << endl;
607 end();
608}
609
610void ArticleViewer::slotShowSummary(TreeNode* node)
611{
612 m_viewMode = SummaryView;
613
614 if (!node)
615 {
616 slotClear();
617 return;
618 }
619
620 if (node != m_node)
621 {
622 disconnectFromNode(m_node);
623 connectToNode(node);
624 m_node = node;
625 }
626 m_showSummaryVisitor->visit(node);
627}
628
629
631{
632 m_viewMode = NormalView;
633 disconnectFromNode(m_node);
634 m_article = article;
635 m_node = 0;
636 m_link = article.link();
637 if (article.feed()->loadLinkedWebsite())
638 openURL(article.link());
639 else
640 renderContent( formatArticleNormalMode(article.feed(), article) );
641}
642
644{
645 if (m_statusFilter == statusFilter && m_textFilter == textFilter)
646 return;
647
648 m_textFilter = textFilter;
649 m_statusFilter = statusFilter;
650
652}
653
655{
656 if (m_viewMode != CombinedView)
657 return;
658
659 if (!m_node)
660 return slotClear();
661
662 TQValueList<Article> articles = m_node->articles();
663 qHeapSort(articles);
664 TQValueList<Article>::ConstIterator end = articles.end();
665 TQValueList<Article>::ConstIterator it = articles.begin();
666
667 TQString text;
668
669 int num = 0;
670 TQTime spent;
671 spent.start();
672
673 for ( ; it != end; ++it)
674 {
675 if ( !(*it).isDeleted() && m_textFilter.matches(*it) && m_statusFilter.matches(*it) )
676 {
677 text += "<p><div class=\"article\">"+formatArticleCombinedMode(0, *it)+"</div><p>";
678 ++num;
679 }
680 }
681 //kdDebug() << "Combined view rendering: (" << num << " articles):\n" << "generating HTML: " << spent.elapsed() << "ms " << endl;
682 renderContent(text);
683 //kdDebug() << "HTML rendering: " << spent.elapsed() << "ms" << endl;
684
685
686}
687
688void ArticleViewer::slotArticlesUpdated(TreeNode* /*node*/, const TQValueList<Article>& /*list*/)
689{
690 if (m_viewMode == CombinedView)
692}
693
694void ArticleViewer::slotArticlesAdded(TreeNode* /*node*/, const TQValueList<Article>& /*list*/)
695{
696}
697
698void ArticleViewer::slotArticlesRemoved(TreeNode* /*node*/, const TQValueList<Article>& /*list*/)
699{
700}
701
702/* testingtesting :)
703void ArticleViewer::slotPopupMenu(KXMLGUIClient*, const TQPoint& p, const KURL& kurl, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags, mode_t)
704{
705 kdDebug() << m_link << endl;
706 kdDebug() << kurl.url() << endl;
707}*/
708
709
711{
712 disconnectFromNode(m_node);
713 m_node = 0;
714 m_article = Article();
715
716 renderContent(TQString());
717}
718
720{
721 m_viewMode = CombinedView;
722
723 if (node != m_node)
724 disconnectFromNode(m_node);
725
726 connectToNode(node);
727
728 m_article = Article();
729 m_node = node;
730
731 if (node && !node->articles().isEmpty())
732 m_link = node->articles().first().link();
733 else
734 m_link = KURL();
735
737}
738
739void ArticleViewer::keyPressEvent(TQKeyEvent* e)
740{
741 e->ignore();
742}
743
744void ArticleViewer::urlSelected(const TQString &url, int button, int state, const TQString& _target, KParts::URLArgs args)
745{
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");
751 }
752 }
753 else
754 Viewer::urlSelected(url, button, state, _target, args);
755}
756
757void ArticleViewer::slotPaletteOrFontChanged()
758{
761 reload();
762}
763
764void ArticleViewer::connectToNode(TreeNode* node)
765{
766 if (node)
767 {
768 if (m_viewMode == CombinedView)
769 {
770// connect( node, TQ_SIGNAL(signalChanged(TreeNode*)), this, TQ_SLOT(slotUpdateCombinedView() ) );
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>&)));
774 }
775 if (m_viewMode == SummaryView)
776 connect( node, TQ_SIGNAL(signalChanged(TreeNode*)), this, TQ_SLOT(slotShowSummary(TreeNode*) ) );
777
778 connect( node, TQ_SIGNAL(signalDestroyed(TreeNode*)), this, TQ_SLOT(slotClear() ) );
779 }
780}
781
782void ArticleViewer::disconnectFromNode(TreeNode* node)
783{
784 if (node)
785 {
786// disconnect( node, TQ_SIGNAL(signalChanged(TreeNode*)), this, TQ_SLOT(slotUpdateCombinedView() ) );
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>&)));
792
793 }
794}
795
796}
797#include "articleviewer.moc"
798
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.
Definition: article.h:58
represents a feed
Definition: feed.h:63
const TQPixmap & image() const
returns the feed image
Definition: feed.cpp:349
const TQString & xmlUrl() const
returns the url of the actual feed source (rss/rdf/atom file)
Definition: feed.cpp:351
const TQString & htmlUrl() const
returns the URL of the HTML page of this feed
Definition: feed.cpp:355
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...
Definition: treenode.h:52
virtual TQValueList< Article > articles(const TQString &tag=TQString())=0
Returns a sequence of the articles this node contains.