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 <kstandarddirs.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 
56 namespace Akregator {
57 
58 // from kmail::headerstyle.cpp
59 static inline TQString directionOf(const TQString &str)
60 {
61  return str.isRightToLeft() ? "rtl" : "ltr" ;
62 }
63 
64 class 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 
161 ArticleViewer::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(kapp, TQ_SIGNAL(tdedisplayPaletteChanged()), this, TQ_SLOT(slotPaletteOrFontChanged()) );
179  connect(kapp, 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 
185 ArticleViewer::~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 
355 bool 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 
368 void 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 ( kapp->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 
404 TQString ArticleViewer::formatArticleNormalMode(Feed* feed, const Article& article)
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 
584 void 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 
610 void 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 
688 void ArticleViewer::slotArticlesUpdated(TreeNode* /*node*/, const TQValueList<Article>& /*list*/)
689 {
690  if (m_viewMode == CombinedView)
692 }
693 
694 void ArticleViewer::slotArticlesAdded(TreeNode* /*node*/, const TQValueList<Article>& /*list*/)
695 {
696 }
697 
698 void ArticleViewer::slotArticlesRemoved(TreeNode* /*node*/, const TQValueList<Article>& /*list*/)
699 {
700 }
701 
702 /* testingtesting :)
703 void 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 
739 void ArticleViewer::keyPressEvent(TQKeyEvent* e)
740 {
741  e->ignore();
742 }
743 
744 void 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 
757 void ArticleViewer::slotPaletteOrFontChanged()
758 {
761  reload();
762 }
763 
764 void 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 
782 void 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.