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 <kaction.h>
31 #include <kapplication.h>
32 #include <kdebug.h>
33 #include <kglobalsettings.h>
34 #include <khtmlview.h>
35 #include <klocale.h>
36 #include <kprocess.h>
37 #include <krun.h>
38 #include <kstandarddirs.h>
39 #include <kshell.h>
40 #include <kmessagebox.h>
41 #include <kio/netaccess.h>
42 #include <libkdepim/kfileio.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 
70  virtual bool visitFeed(Feed* node)
71  {
72  m_view->m_link = TQString();
73 
74  TQString text;
75  text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ? "rtl" : "ltr");
76 
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)");
81  else
82  text += i18n(" (1 unread article)", " (%n unread articles)", node->unread());
83  text += "</div>\n"; // headertitle
84  text += "</div>\n"; // /headerbox
85 
86  if (!node->image().isNull()) // image
87  {
88  text += TQString("<div class=\"body\">");
89  TQString url=node->xmlUrl();
90  TQString file = url.replace("/", "_").replace(":", "_");
91  KURL u(m_view->m_imageDir);
92  u.setFileName(file);
93  text += TQString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(node->htmlUrl()).arg(u.url());
94  }
95  else text += "<div class=\"body\">";
96 
97 
98  if( !node->description().isEmpty() )
99  {
100  text += TQString("<div dir=\"%1\">").arg(Utils::stripTags(directionOf(node->description())));
101  text += i18n("<b>Description:</b> %1<br><br>").arg(node->description());
102  text += "</div>\n"; // /description
103  }
104 
105  if ( !node->htmlUrl().isEmpty() )
106  {
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());
109  text += "</div>\n"; // / link
110  }
111 
112  //text += i18n("<b>Unread articles:</b> %1").arg(node->unread());
113  text += "</div>"; // /body
114 
115  m_view->renderContent(text);
116  return true;
117  }
118 
119  virtual bool visitFolder(Folder* node)
120  {
121  m_view->m_link = TQString();
122 
123  TQString text;
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)");
128  else
129  text += i18n(" (1 unread article)", " (%n unread articles)", node->unread());
130  text += TQString("</div>\n");
131  text += "</div>\n"; // /headerbox
132 
133  m_view->renderContent(text);
134  return true;
135  }
136 
137  virtual bool visitTagNode(TagNode* node)
138  {
139  m_view->m_link = TQString();
140 
141  TQString text;
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)");
146  else
147  text += i18n(" (1 unread article)", " (%n unread articles)", node->unread());
148  text += TQString("</div>\n");
149  text += "</div>\n"; // /headerbox
150 
151  m_view->renderContent(text);
152  return true;
153  }
154 
155  private:
156 
157  ArticleViewer* m_view;
158 };
159 
160 ArticleViewer::ArticleViewer(TQWidget *parent, const char *name)
161  : Viewer(parent, name), m_htmlFooter(), m_currentText(), m_node(0), m_viewMode(NormalView)
162 {
163  setJScriptEnabled(false);
164  setJavaEnabled(false);
165  setPluginsEnabled(false);
166 
167  m_showSummaryVisitor = new ShowSummaryVisitor(this);
168  setXMLFile(locate("data", "akregator/articleviewer.rc"), true);
169 
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" );
174 
175  connect(this, TQT_SIGNAL(selectionChanged()), this, TQT_SLOT(slotSelectionChanged()));
176 
177  connect(kapp, TQT_SIGNAL(kdisplayPaletteChanged()), this, TQT_SLOT(slotPaletteOrFontChanged()) );
178  connect(kapp, TQT_SIGNAL(kdisplayFontChanged()), this, TQT_SLOT(slotPaletteOrFontChanged()) );
179 
180  m_imageDir.setPath(KGlobal::dirs()->saveLocation("cache", "akregator/Media/"));
181  m_htmlFooter = "</body></html>";
182 }
183 
184 ArticleViewer::~ArticleViewer()
185 {
186  delete m_showSummaryVisitor;
187 }
188 
190 {
191  const TQColorGroup & cg = TQApplication::palette().active();
192 
193  // from kmail::headerstyle.cpp
194  m_normalModeCSS = TQString(
195  "@media screen, print {"
196  "body {\n"
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(
206  "a {\n"
207  + TQString(" color: %1 ! important;\n")
208  + TQString(!Settings::underlineLinks() ? " text-decoration: none ! important;\n" : "")
209  + "}\n\n"
210  +".headerbox {\n"
211  +" background: %2 ! important;\n"
212  +" color: %3 ! important;\n"
213  +" border:1px solid #000;\n"
214  +" margin-bottom: 10pt;\n"
215 // +" width: 99%;\n"
216  + "}\n\n")
217  .arg(cg.link().name())
218  .arg(cg.background().name())
219  .arg(cg.text().name());
220 
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());
229 
230  m_normalModeCSS += TQString(
231  ".headertitle {\n"
232  " background: %1 ! important;\n"
233  " padding:2px;\n"
234  " color: %2 ! important;\n"
235  " font-weight: bold;\n"
236  "}\n\n"
237  ".header {\n"
238  " font-weight: bold;\n"
239  " padding:2px;\n"
240  " margin-right: 5px;\n"
241  "}\n\n"
242  ".headertext {\n"
243  "}\n\n"
244  ".headimage {\n"
245  " float: right;\n"
246  " margin-left: 5px;\n"
247  "}\n\n").arg(cg.highlight().name())
248  .arg(cg.highlightedText().name());
249 
250  m_normalModeCSS += TQString(
251  "body { clear: none; }\n\n"
252  ".content {\n"
253  " display: block;\n"
254  " margin-bottom: 6px;\n"
255  "}\n\n"
256  // these rules make sure that there is no leading space between the header and the first of the text
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"
265  "}\n\n"); // @media screen, print
266 }
267 
269 {
270  const TQColorGroup & cg = TQApplication::palette().active();
271 
272  // from kmail::headerstyle.cpp
273  m_combinedModeCSS = TQString (
274 // "<style type=\"text/css\">\n"
275  "@media screen, print {"
276  "body {\n"
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 += (
286  "a {\n"
287  + TQString(" color: %1 ! important;\n")
288  + TQString(!Settings::underlineLinks() ? " text-decoration: none ! important;\n" : "")
289  + "}\n\n"
290  +".headerbox {\n"
291  +" background: %2 ! important;\n"
292  +" color: %3 ! important;\n"
293  +" border:1px solid #000;\n"
294  +" margin-bottom: 10pt;\n"
295 // +" width: 99%;\n"
296  + "}\n\n")
297  .arg(cg.link().name())
298  .arg(cg.background().name())
299  .arg(cg.text().name());
300 
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(
310  ".headertitle {\n"
311  " background: %1 ! important;\n"
312  " padding:2px;\n"
313  " color: %2 ! important;\n"
314  " font-weight: bold;\n"
315  "}\n\n"
316  ".header {\n"
317  " font-weight: bold;\n"
318  " padding:2px;\n"
319  " margin-right: 5px;\n"
320  "}\n\n"
321  ".headertext {\n"
322  "}\n\n"
323  ".headimage {\n"
324  " float: right;\n"
325  " margin-left: 5px;\n"
326  "}\n\n").arg(cg.highlight().name())
327  .arg(cg.highlightedText().name());
328 
329  m_combinedModeCSS += TQString(
330  "body { clear: none; }\n\n"
331  ".content {\n"
332  " display: block;\n"
333  " margin-bottom: 6px;\n"
334  "}\n\n"
335  // these rules make sure that there is no leading space between the header and the first of the text
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"
344  "}\n\n"); // @media screen, print
345 }
346 
348 {
349  beginWriting();
350  write(m_currentText);
351  endWriting();
352 }
353 
354 bool ArticleViewer::openURL(const KURL& url)
355 {
356  if (!m_article.isNull() && m_article.feed()->loadLinkedWebsite())
357  {
358  return Viewer::openURL(url);
359  }
360  else
361  {
362  reload();
363  return true;
364  }
365 }
366 
367 void ArticleViewer::displayAboutPage()
368 {
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" ) );
374  else
375  content = content.arg( "" );
376 
377  begin(KURL( location ));
378  TQString info =
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'>&nbsp; &nbsp; The Akregator Team</p>\n")
392  .arg(AKREGATOR_VERSION) // Akregator version
393  .arg("http://akregator.kde.org/"); // Akregator homepage URL
394 
395  TQString fontSize = TQString::number( pointsToPixel( Settings::mediumFontSize() ));
396  TQString appTitle = i18n("Akregator");
397  TQString catchPhrase = ""; //not enough space for a catch phrase at default window size i18n("Part of the Kontact Suite");
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));
400  end();
401 }
402 
403 TQString ArticleViewer::formatArticleNormalMode(Feed* feed, const Article& article)
404 {
405  TQString text;
406  text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ? "rtl" : "ltr");
407 
408  if (!article.title().isEmpty())
409  {
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("<", "&lt;").replace(">", "&gt;"); // TODO: better leave things escaped in the parser
414  if (article.link().isValid())
415  text += "</a>";
416  text += "</div>\n";
417  }
418  if (article.pubDate().isValid())
419  {
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"; // TODO: might need RTL?
424  }
425  TQString author = article.author();
426  if (!author.isEmpty())
427  {
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"; // TODO: might need RTL?
432  }
433  text += "</div>\n"; // end headerbox
434 
435  if (feed && !feed->image().isNull())
436  {
437  TQString file = Utils::fileNameForUrl(feed->xmlUrl());
438  KURL u(m_imageDir);
439  u.setFileName(file);
440  text += TQString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->htmlUrl()).arg(u.url());
441  }
442 
443 
444 
445  if (!article.description().isEmpty())
446  {
447  text += TQString("<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) );
448  text += "<span class=\"content\">"+article.description()+"</span>";
449  text += "</div>";
450  }
451 
452  text += "<div class=\"body\">";
453 
454  if (article.commentsLink().isValid())
455  {
456  text += "<a class=\"contentlink\" href=\"";
457  text += article.commentsLink().url();
458  text += "\">" + i18n( "Comments");
459  if (article.comments())
460  {
461  text += " ("+ TQString::number(article.comments()) +")";
462  }
463  text += "</a>";
464  }
465 
466  if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
467  {
468  text += "<p><a class=\"contentlink\" href=\"";
469  // in case link isn't valid, fall back to the guid permaLink.
470  if (article.link().isValid())
471  {
472  text += article.link().url();
473  }
474  else
475  {
476  text += article.guid();
477  }
478  text += "\">" + i18n( "Complete Story" ) + "</a></p>";
479  }
480  text += "</div>";
481 
482  if (!article.enclosure().isNull())
483  {
484  //TQString url = article.enclosure().url();
485  //TQString type = article.enclosure().type();
486  //int length = article.enclosure().length();
487  //TQString lengthStr = KIO::convertSize(length);
488 
489  //text += TQString("<hr><div><a href=\"%1\">%2</a> (%3, %4)</div>").arg(url).arg(url).arg(lengthStr).arg(type);
490  }
491  //kdDebug() << text << endl;
492  return text;
493 
494 }
495 
497 {
498  TQString text;
499  text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ? "rtl" : "ltr");
500 
501  KURL link = article.link();
502 
503  if (!article.title().isEmpty())
504  {
505  text += TQString("<div class=\"headertitle\" dir=\"%1\">\n").arg(directionOf(Utils::stripTags(article.title())));
506  if (link.isValid())
507  text += "<a href=\""+link.url()+"\">";
508  text += article.title().replace("<", "&lt;").replace(">", "&gt;"); // TODO: better leave things escaped in the parser
509  if (link.isValid())
510  text += "</a>";
511  text += "</div>\n";
512  }
513  if (article.pubDate().isValid())
514  {
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"; // TODO: might need RTL?
519  }
520 
521  TQString author = article.author();
522  if (!author.isEmpty())
523  {
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"; // TODO: might need RTL?
528  }
529 
530  text += "</div>\n"; // end headerbox
531 
532  if (feed && !feed->image().isNull())
533  {
534  TQString file = Utils::fileNameForUrl(feed->xmlUrl());
535  KURL u(m_imageDir);
536  u.setFileName(file);
537  text += TQString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->htmlUrl()).arg(u.url());
538  }
539 
540 
541 
542  if (!article.description().isEmpty())
543  {
544  text += TQString("<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) );
545  text += "<span class=\"content\">"+article.description()+"</span>";
546  text += "</div>";
547  }
548 
549  text += "<div class=\"body\">";
550 
551  if (article.commentsLink().isValid())
552  {
553  text += "<a class=\"contentlink\" href=\"";
554  text += article.commentsLink().url();
555  text += "\">" + i18n( "Comments");
556  if (article.comments())
557  {
558  text += " ("+ TQString::number(article.comments()) +")";
559  }
560  text += "</a>";
561  }
562 
563  if (link.isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
564  {
565  text += "<p><a class=\"contentlink\" href=\"";
566  // in case link isn't valid, fall back to the guid permaLink.
567  if (link.isValid())
568  {
569  text += link.url();
570  }
571  else
572  {
573  text += article.guid();
574  }
575  text += "\">" + i18n( "Complete Story" ) + "</a></p>";
576  }
577  text += "</div>";
578  //kdDebug() << text << endl;
579  return text;
580 
581 }
582 
583 void ArticleViewer::renderContent(const TQString& text)
584 {
585  closeURL();
586  m_currentText = text;
587  beginWriting();
588  //kdDebug() << text << endl;
589  write(text);
590  endWriting();
591 }
592 
594 {
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);
597  begin(m_link);
598  setUserStyleSheet(m_viewMode == CombinedView ? m_combinedModeCSS : m_normalModeCSS);
599  write(head);
600 }
601 
603 {
604  write(m_htmlFooter);
605  //kdDebug() << m_htmlFooter << endl;
606  end();
607 }
608 
609 void ArticleViewer::slotShowSummary(TreeNode* node)
610 {
611  m_viewMode = SummaryView;
612 
613  if (!node)
614  {
615  slotClear();
616  return;
617  }
618 
619  if (node != m_node)
620  {
621  disconnectFromNode(m_node);
622  connectToNode(node);
623  m_node = node;
624  }
625  m_showSummaryVisitor->visit(node);
626 }
627 
628 
630 {
631  m_viewMode = NormalView;
632  disconnectFromNode(m_node);
633  m_article = article;
634  m_node = 0;
635  m_link = article.link();
636  if (article.feed()->loadLinkedWebsite())
637  openURL(article.link());
638  else
639  renderContent( formatArticleNormalMode(article.feed(), article) );
640 }
641 
643 {
644  if (m_statusFilter == statusFilter && m_textFilter == textFilter)
645  return;
646 
647  m_textFilter = textFilter;
648  m_statusFilter = statusFilter;
649 
651 }
652 
654 {
655  if (m_viewMode != CombinedView)
656  return;
657 
658  if (!m_node)
659  return slotClear();
660 
661  TQValueList<Article> articles = m_node->articles();
662  qHeapSort(articles);
663  TQValueList<Article>::ConstIterator end = articles.end();
664  TQValueList<Article>::ConstIterator it = articles.begin();
665 
666  TQString text;
667 
668  int num = 0;
669  TQTime spent;
670  spent.start();
671 
672  for ( ; it != end; ++it)
673  {
674  if ( !(*it).isDeleted() && m_textFilter.matches(*it) && m_statusFilter.matches(*it) )
675  {
676  text += "<p><div class=\"article\">"+formatArticleCombinedMode(0, *it)+"</div><p>";
677  ++num;
678  }
679  }
680  //kdDebug() << "Combined view rendering: (" << num << " articles):\n" << "generating HTML: " << spent.elapsed() << "ms " << endl;
681  renderContent(text);
682  //kdDebug() << "HTML rendering: " << spent.elapsed() << "ms" << endl;
683 
684 
685 }
686 
687 void ArticleViewer::slotArticlesUpdated(TreeNode* /*node*/, const TQValueList<Article>& /*list*/)
688 {
689  if (m_viewMode == CombinedView)
691 }
692 
693 void ArticleViewer::slotArticlesAdded(TreeNode* /*node*/, const TQValueList<Article>& /*list*/)
694 {
695 }
696 
697 void ArticleViewer::slotArticlesRemoved(TreeNode* /*node*/, const TQValueList<Article>& /*list*/)
698 {
699 }
700 
701 /* testingtesting :)
702 void ArticleViewer::slotPopupMenu(KXMLGUIClient*, const TQPoint& p, const KURL& kurl, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags, mode_t)
703 {
704  kdDebug() << m_link << endl;
705  kdDebug() << kurl.url() << endl;
706 }*/
707 
708 
710 {
711  disconnectFromNode(m_node);
712  m_node = 0;
713  m_article = Article();
714 
715  renderContent(TQString());
716 }
717 
719 {
720  m_viewMode = CombinedView;
721 
722  if (node != m_node)
723  disconnectFromNode(m_node);
724 
725  connectToNode(node);
726 
727  m_article = Article();
728  m_node = node;
729 
730  if (node && !node->articles().isEmpty())
731  m_link = node->articles().first().link();
732  else
733  m_link = KURL();
734 
736 }
737 
738 void ArticleViewer::keyPressEvent(TQKeyEvent* e)
739 {
740  e->ignore();
741 }
742 
743 void ArticleViewer::urlSelected(const TQString &url, int button, int state, const TQString& _target, KParts::URLArgs args)
744 {
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");
750  }
751  }
752  else
753  Viewer::urlSelected(url, button, state, _target, args);
754 }
755 
756 void ArticleViewer::slotPaletteOrFontChanged()
757 {
760  reload();
761 }
762 
763 void ArticleViewer::connectToNode(TreeNode* node)
764 {
765  if (node)
766  {
767  if (m_viewMode == CombinedView)
768  {
769 // connect( node, TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotUpdateCombinedView() ) );
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>&)));
773  }
774  if (m_viewMode == SummaryView)
775  connect( node, TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotShowSummary(TreeNode*) ) );
776 
777  connect( node, TQT_SIGNAL(signalDestroyed(TreeNode*)), this, TQT_SLOT(slotClear() ) );
778  }
779 }
780 
781 void ArticleViewer::disconnectFromNode(TreeNode* node)
782 {
783  if (node)
784  {
785 // disconnect( node, TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotUpdateCombinedView() ) );
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>&)));
791 
792  }
793 }
794 
795 }
796 #include "articleviewer.moc"
797