TDE personal information management applications
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

798 lines
27KB

  1. /*
  2. This file is part of Akregator.
  3. Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net>
  4. 2005 Frank Osterfeld <frank.osterfeld at kdemail.net>
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. As a special exception, permission is given to link this program
  17. with any edition of TQt, and distribute the resulting executable,
  18. without including the source code for TQt in the source distribution.
  19. */
  20. #include <tqdatetime.h>
  21. #include <tqevent.h>
  22. #include <tqscrollview.h>
  23. #include <tqvaluelist.h>
  24. #include <kaction.h>
  25. #include <kapplication.h>
  26. #include <kdebug.h>
  27. #include <kglobalsettings.h>
  28. #include <khtmlview.h>
  29. #include <klocale.h>
  30. #include <kprocess.h>
  31. #include <krun.h>
  32. #include <kstandarddirs.h>
  33. #include <kshell.h>
  34. #include <kmessagebox.h>
  35. #include <kio/netaccess.h>
  36. #include <libtdepim/kfileio.h>
  37. #include "aboutdata.h"
  38. #include "akregator_run.h"
  39. #include "akregatorconfig.h"
  40. #include "articleviewer.h"
  41. #include "feed.h"
  42. #include "folder.h"
  43. #include "article.h"
  44. #include "treenode.h"
  45. #include "treenodevisitor.h"
  46. #include "tagnode.h"
  47. #include "utils.h"
  48. namespace Akregator {
  49. // from kmail::headerstyle.cpp
  50. static inline TQString directionOf(const TQString &str)
  51. {
  52. return str.isRightToLeft() ? "rtl" : "ltr" ;
  53. }
  54. class ArticleViewer::ShowSummaryVisitor : public TreeNodeVisitor
  55. {
  56. public:
  57. ShowSummaryVisitor(ArticleViewer* view) : m_view(view) {}
  58. virtual bool visitFeed(Feed* node)
  59. {
  60. m_view->m_link = TQString();
  61. TQString text;
  62. text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").tqarg(TQApplication::reverseLayout() ? "rtl" : "ltr");
  63. text += TQString("<div class=\"headertitle\" dir=\"%1\">").tqarg(directionOf(Utils::stripTags(node->title())));
  64. text += node->title();
  65. if(node->unread() == 0)
  66. text += i18n(" (no unread articles)");
  67. else
  68. text += i18n(" (1 unread article)", " (%n unread articles)", node->unread());
  69. text += "</div>\n"; // headertitle
  70. text += "</div>\n"; // /headerbox
  71. if (!node->image().isNull()) // image
  72. {
  73. text += TQString("<div class=\"body\">");
  74. TQString url=node->xmlUrl();
  75. TQString file = url.replace("/", "_").replace(":", "_");
  76. KURL u(m_view->m_imageDir);
  77. u.setFileName(file);
  78. text += TQString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").tqarg(node->htmlUrl()).tqarg(u.url());
  79. }
  80. else text += "<div class=\"body\">";
  81. if( !node->description().isEmpty() )
  82. {
  83. text += TQString("<div dir=\"%1\">").tqarg(Utils::stripTags(directionOf(node->description())));
  84. text += i18n("<b>Description:</b> %1<br><br>").tqarg(node->description());
  85. text += "</div>\n"; // /description
  86. }
  87. if ( !node->htmlUrl().isEmpty() )
  88. {
  89. text += TQString("<div dir=\"%1\">").tqarg(directionOf(node->htmlUrl()));
  90. text += i18n("<b>Homepage:</b> <a href=\"%1\">%2</a>").tqarg(node->htmlUrl()).tqarg(node->htmlUrl());
  91. text += "</div>\n"; // / link
  92. }
  93. //text += i18n("<b>Unread articles:</b> %1").tqarg(node->unread());
  94. text += "</div>"; // /body
  95. m_view->renderContent(text);
  96. return true;
  97. }
  98. virtual bool visitFolder(Folder* node)
  99. {
  100. m_view->m_link = TQString();
  101. TQString text;
  102. text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").tqarg(TQApplication::reverseLayout() ? "rtl" : "ltr");
  103. text += TQString("<div class=\"headertitle\" dir=\"%1\">%2").tqarg(directionOf(Utils::stripTags(node->title()))).tqarg(node->title());
  104. if(node->unread() == 0)
  105. text += i18n(" (no unread articles)");
  106. else
  107. text += i18n(" (1 unread article)", " (%n unread articles)", node->unread());
  108. text += TQString("</div>\n");
  109. text += "</div>\n"; // /headerbox
  110. m_view->renderContent(text);
  111. return true;
  112. }
  113. virtual bool visitTagNode(TagNode* node)
  114. {
  115. m_view->m_link = TQString();
  116. TQString text;
  117. text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").tqarg(TQApplication::reverseLayout() ? "rtl" : "ltr");
  118. text += TQString("<div class=\"headertitle\" dir=\"%1\">%2").tqarg(directionOf(Utils::stripTags(node->title()))).tqarg(node->title());
  119. if(node->unread() == 0)
  120. text += i18n(" (no unread articles)");
  121. else
  122. text += i18n(" (1 unread article)", " (%n unread articles)", node->unread());
  123. text += TQString("</div>\n");
  124. text += "</div>\n"; // /headerbox
  125. m_view->renderContent(text);
  126. return true;
  127. }
  128. private:
  129. ArticleViewer* m_view;
  130. };
  131. ArticleViewer::ArticleViewer(TQWidget *parent, const char *name)
  132. : Viewer(parent, name), m_htmlFooter(), m_currentText(), m_node(0), m_viewMode(NormalView)
  133. {
  134. setJScriptEnabled(false);
  135. setJavaEnabled(false);
  136. setPluginsEnabled(false);
  137. m_showSummaryVisitor = new ShowSummaryVisitor(this);
  138. setXMLFile(locate("data", "akregator/articleviewer.rc"), true);
  139. generateNormalModeCSS();
  140. generateCombinedModeCSS();
  141. new KAction( i18n("&Scroll Up"), TQString(), "Up", this, TQT_SLOT(slotScrollUp()), actionCollection(), "articleviewer_scroll_up" );
  142. new KAction( i18n("&Scroll Down"), TQString(), "Down", this, TQT_SLOT(slotScrollDown()), actionCollection(), "articleviewer_scroll_down" );
  143. connect(this, TQT_SIGNAL(selectionChanged()), this, TQT_SLOT(slotSelectionChanged()));
  144. connect(kapp, TQT_SIGNAL(kdisplayPaletteChanged()), this, TQT_SLOT(slotPaletteOrFontChanged()) );
  145. connect(kapp, TQT_SIGNAL(kdisplayFontChanged()), this, TQT_SLOT(slotPaletteOrFontChanged()) );
  146. m_imageDir.setPath(KGlobal::dirs()->saveLocation("cache", "akregator/Media/"));
  147. m_htmlFooter = "</body></html>";
  148. }
  149. ArticleViewer::~ArticleViewer()
  150. {
  151. delete m_showSummaryVisitor;
  152. }
  153. void ArticleViewer::generateNormalModeCSS()
  154. {
  155. const TQColorGroup & cg = TQApplication::tqpalette().active();
  156. // from kmail::headerstyle.cpp
  157. m_normalModeCSS = TQString(
  158. "@media screen, print {"
  159. "body {\n"
  160. " font-family: \"%1\" ! important;\n"
  161. " font-size: %2 ! important;\n"
  162. " color: %3 ! important;\n"
  163. " background: %4 ! important;\n"
  164. "}\n\n").tqarg(Settings::standardFont())
  165. .tqarg(TQString::number(pointsToPixel(Settings::mediumFontSize()))+"px")
  166. .tqarg(cg.text().name())
  167. .tqarg(cg.base().name());
  168. m_normalModeCSS += TQString(
  169. "a {\n"
  170. + TQString(" color: %1 ! important;\n")
  171. + TQString(!Settings::underlineLinks() ? " text-decoration: none ! important;\n" : "")
  172. + "}\n\n"
  173. +".headerbox {\n"
  174. +" background: %2 ! important;\n"
  175. +" color: %3 ! important;\n"
  176. +" border:1px solid #000;\n"
  177. +" margin-bottom: 10pt;\n"
  178. // +" width: 99%;\n"
  179. + "}\n\n")
  180. .tqarg(cg.link().name())
  181. .tqarg(cg.background().name())
  182. .tqarg(cg.text().name());
  183. m_normalModeCSS += TQString(".headertitle a:link { color: %1 ! important; }\n"
  184. ".headertitle a:visited { color: %2 ! important; }\n"
  185. ".headertitle a:hover{ color: %3 ! important; }\n"
  186. ".headertitle a:active { color: %4 ! important; }\n")
  187. .tqarg(cg.highlightedText().name())
  188. .tqarg(cg.highlightedText().name())
  189. .tqarg(cg.highlightedText().name())
  190. .tqarg(cg.highlightedText().name());
  191. m_normalModeCSS += TQString(
  192. ".headertitle {\n"
  193. " background: %1 ! important;\n"
  194. " padding:2px;\n"
  195. " color: %2 ! important;\n"
  196. " font-weight: bold;\n"
  197. "}\n\n"
  198. ".header {\n"
  199. " font-weight: bold;\n"
  200. " padding:2px;\n"
  201. " margin-right: 5px;\n"
  202. "}\n\n"
  203. ".headertext {\n"
  204. "}\n\n"
  205. ".headimage {\n"
  206. " float: right;\n"
  207. " margin-left: 5px;\n"
  208. "}\n\n").tqarg(cg.highlight().name())
  209. .tqarg(cg.highlightedText().name());
  210. m_normalModeCSS += TQString(
  211. "body { clear: none; }\n\n"
  212. ".content {\n"
  213. " display: block;\n"
  214. " margin-bottom: 6px;\n"
  215. "}\n\n"
  216. // these rules make sure that there is no leading space between the header and the first of the text
  217. ".content > P:first-child {\n margin-top: 1px; }\n"
  218. ".content > DIV:first-child {\n margin-top: 1px; }\n"
  219. ".content > BR:first-child {\n display: none; }\n"
  220. "iframe {display: none !important; }\n"
  221. "frame {display: none !important; }\n"
  222. "frameset {display: none !important; }\n"
  223. "object {display: none !important; }\n"
  224. "applet {display: none !important; }\n"
  225. "}\n\n"); // @media screen, print
  226. }
  227. void ArticleViewer::generateCombinedModeCSS()
  228. {
  229. const TQColorGroup & cg = TQApplication::tqpalette().active();
  230. // from kmail::headerstyle.cpp
  231. m_combinedModeCSS = TQString (
  232. // "<style type=\"text/css\">\n"
  233. "@media screen, print {"
  234. "body {\n"
  235. " font-family: \"%1\" ! important;\n"
  236. " font-size: %2 ! important;\n"
  237. " color: %3 ! important;\n"
  238. " background: %4 ! important;\n"
  239. "}\n\n").tqarg(Settings::standardFont())
  240. .tqarg(TQString::number(pointsToPixel(Settings::mediumFontSize()))+"px")
  241. .tqarg(cg.text().name())
  242. .tqarg(cg.base().name());
  243. m_combinedModeCSS += (
  244. "a {\n"
  245. + TQString(" color: %1 ! important;\n")
  246. + TQString(!Settings::underlineLinks() ? " text-decoration: none ! important;\n" : "")
  247. + "}\n\n"
  248. +".headerbox {\n"
  249. +" background: %2 ! important;\n"
  250. +" color: %3 ! important;\n"
  251. +" border:1px solid #000;\n"
  252. +" margin-bottom: 10pt;\n"
  253. // +" width: 99%;\n"
  254. + "}\n\n")
  255. .tqarg(cg.link().name())
  256. .tqarg(cg.background().name())
  257. .tqarg(cg.text().name());
  258. m_combinedModeCSS += TQString(".headertitle a:link { color: %1 ! important; }\n"
  259. ".headertitle a:visited { color: %2 ! important; }\n"
  260. ".headertitle a:hover{ color: %3 ! important; }\n"
  261. ".headertitle a:active { color: %4 ! important; }\n")
  262. .tqarg(cg.highlightedText().name())
  263. .tqarg(cg.highlightedText().name())
  264. .tqarg(cg.highlightedText().name())
  265. .tqarg(cg.highlightedText().name());
  266. m_combinedModeCSS += TQString(
  267. ".headertitle {\n"
  268. " background: %1 ! important;\n"
  269. " padding:2px;\n"
  270. " color: %2 ! important;\n"
  271. " font-weight: bold;\n"
  272. "}\n\n"
  273. ".header {\n"
  274. " font-weight: bold;\n"
  275. " padding:2px;\n"
  276. " margin-right: 5px;\n"
  277. "}\n\n"
  278. ".headertext {\n"
  279. "}\n\n"
  280. ".headimage {\n"
  281. " float: right;\n"
  282. " margin-left: 5px;\n"
  283. "}\n\n").tqarg(cg.highlight().name())
  284. .tqarg(cg.highlightedText().name());
  285. m_combinedModeCSS += TQString(
  286. "body { clear: none; }\n\n"
  287. ".content {\n"
  288. " display: block;\n"
  289. " margin-bottom: 6px;\n"
  290. "}\n\n"
  291. // these rules make sure that there is no leading space between the header and the first of the text
  292. ".content > P:first-child {\n margin-top: 1px; }\n"
  293. ".content > DIV:first-child {\n margin-top: 1px; }\n"
  294. ".content > BR:first-child {\n display: none; }\n"
  295. "iframe {display: none !important; }\n"
  296. "frame {display: none !important; }\n"
  297. "frameset {display: none !important; }\n"
  298. "object {display: none !important; }\n"
  299. "applet {display: none !important; }\n"
  300. "}\n\n"); // @media screen, print
  301. }
  302. void ArticleViewer::reload()
  303. {
  304. beginWriting();
  305. write(m_currentText);
  306. endWriting();
  307. }
  308. bool ArticleViewer::openURL(const KURL& url)
  309. {
  310. if (!m_article.isNull() && m_article.feed()->loadLinkedWebsite())
  311. {
  312. return Viewer::openURL(url);
  313. }
  314. else
  315. {
  316. reload();
  317. return true;
  318. }
  319. }
  320. void ArticleViewer::displayAboutPage()
  321. {
  322. TQString location = locate("data", "akregator/about/main.html");
  323. TQString content = KPIM::kFileToString(location);
  324. content = content.tqarg( locate( "data", "libtdepim/about/kde_infopage.css" ) );
  325. if ( kapp->reverseLayout() )
  326. content = content.tqarg( "@import \"%1\";" ).tqarg( locate( "data", "libtdepim/about/kde_infopage_rtl.css" ) );
  327. else
  328. content = content.tqarg( "" );
  329. begin(KURL( location ));
  330. TQString info =
  331. i18n("%1: Akregator version; %2: help:// URL; %3: homepage URL; "
  332. "--- end of comment ---",
  333. "<h2 style='margin-top: 0px;'>Welcome to Akregator %1</h2>"
  334. "<p>Akregator is an RSS feed aggregator for the K Desktop Environment. "
  335. "Feed aggregators provide a convenient way to browse different kinds of "
  336. "content, including news, blogs, and other content from online sites. "
  337. "Instead of checking all your favorite web sites manually for updates, "
  338. "Akregator collects the content for you.</p>"
  339. "<p>For more information about using Akregator, check the "
  340. "<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>"
  341. "<p>We hope that you will enjoy Akregator.</p>\n"
  342. "<p>Thank you,</p>\n"
  343. "<p style='margin-bottom: 0px'>&nbsp; &nbsp; The Akregator Team</p>\n")
  344. .tqarg(AKREGATOR_VERSION) // Akregator version
  345. .tqarg("http://akregator.kde.org/"); // Akregator homepage URL
  346. TQString fontSize = TQString::number( pointsToPixel( Settings::mediumFontSize() ));
  347. TQString appTitle = i18n("Akregator");
  348. TQString catchPhrase = ""; //not enough space for a catch phrase at default window size i18n("Part of the Kontact Suite");
  349. TQString quickDescription = i18n("An RSS feed reader for the K Desktop Environment.");
  350. write(content.tqarg(fontSize).tqarg(appTitle).tqarg(catchPhrase).tqarg(quickDescription).tqarg(info));
  351. end();
  352. }
  353. TQString ArticleViewer::formatArticleNormalMode(Feed* feed, const Article& article)
  354. {
  355. TQString text;
  356. text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").tqarg(TQApplication::reverseLayout() ? "rtl" : "ltr");
  357. if (!article.title().isEmpty())
  358. {
  359. text += TQString("<div class=\"headertitle\" dir=\"%1\">\n").tqarg(directionOf(Utils::stripTags(article.title())));
  360. if (article.link().isValid())
  361. text += "<a href=\""+article.link().url()+"\">";
  362. text += article.title().replace("<", "&lt;").replace(">", "&gt;"); // TODO: better leave things escaped in the parser
  363. if (article.link().isValid())
  364. text += "</a>";
  365. text += "</div>\n";
  366. }
  367. if (article.pubDate().isValid())
  368. {
  369. text += TQString("<span class=\"header\" dir=\"%1\">").tqarg(directionOf(i18n("Date")));
  370. text += TQString ("%1:").tqarg(i18n("Date"));
  371. text += "</span><span class=\"headertext\">";
  372. text += KGlobal::locale()->formatDateTime(article.pubDate(), false, false)+"</span>\n"; // TODO: might need RTL?
  373. }
  374. TQString author = article.author();
  375. if (!author.isEmpty())
  376. {
  377. text += TQString("<br/><span class=\"header\" dir=\"%1\">").tqarg(directionOf(i18n("Author")));
  378. text += TQString ("%1:").tqarg(i18n("Author"));
  379. text += "</span><span class=\"headertext\">";
  380. text += author+"</span>\n"; // TODO: might need RTL?
  381. }
  382. text += "</div>\n"; // end headerbox
  383. if (feed && !feed->image().isNull())
  384. {
  385. TQString file = Utils::fileNameForUrl(feed->xmlUrl());
  386. KURL u(m_imageDir);
  387. u.setFileName(file);
  388. text += TQString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").tqarg(feed->htmlUrl()).tqarg(u.url());
  389. }
  390. if (!article.description().isEmpty())
  391. {
  392. text += TQString("<div dir=\"%1\">").tqarg(directionOf(Utils::stripTags(article.description())) );
  393. text += "<span class=\"content\">"+article.description()+"</span>";
  394. text += "</div>";
  395. }
  396. text += "<div class=\"body\">";
  397. if (article.commentsLink().isValid())
  398. {
  399. text += "<a class=\"contentlink\" href=\"";
  400. text += article.commentsLink().url();
  401. text += "\">" + i18n( "Comments");
  402. if (article.comments())
  403. {
  404. text += " ("+ TQString::number(article.comments()) +")";
  405. }
  406. text += "</a>";
  407. }
  408. if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
  409. {
  410. text += "<p><a class=\"contentlink\" href=\"";
  411. // in case link isn't valid, fall back to the guid permaLink.
  412. if (article.link().isValid())
  413. {
  414. text += article.link().url();
  415. }
  416. else
  417. {
  418. text += article.guid();
  419. }
  420. text += "\">" + i18n( "Complete Story" ) + "</a></p>";
  421. }
  422. text += "</div>";
  423. if (!article.enclosure().isNull())
  424. {
  425. //TQString url = article.enclosure().url();
  426. //TQString type = article.enclosure().type();
  427. //int length = article.enclosure().length();
  428. //TQString lengthStr = KIO::convertSize(length);
  429. //text += TQString("<hr><div><a href=\"%1\">%2</a> (%3, %4)</div>").tqarg(url).tqarg(url).tqarg(lengthStr).tqarg(type);
  430. }
  431. //kdDebug() << text << endl;
  432. return text;
  433. }
  434. TQString ArticleViewer::formatArticleCombinedMode(Feed* feed, const Article& article)
  435. {
  436. TQString text;
  437. text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").tqarg(TQApplication::reverseLayout() ? "rtl" : "ltr");
  438. KURL link = article.link();
  439. if (!article.title().isEmpty())
  440. {
  441. text += TQString("<div class=\"headertitle\" dir=\"%1\">\n").tqarg(directionOf(Utils::stripTags(article.title())));
  442. if (link.isValid())
  443. text += "<a href=\""+link.url()+"\">";
  444. text += article.title().replace("<", "&lt;").replace(">", "&gt;"); // TODO: better leave things escaped in the parser
  445. if (link.isValid())
  446. text += "</a>";
  447. text += "</div>\n";
  448. }
  449. if (article.pubDate().isValid())
  450. {
  451. text += TQString("<span class=\"header\" dir=\"%1\">").tqarg(directionOf(i18n("Date")));
  452. text += TQString ("%1:").tqarg(i18n("Date"));
  453. text += "</span><span class=\"headertext\">";
  454. text += KGlobal::locale()->formatDateTime(article.pubDate(), false, false)+"</span>\n"; // TODO: might need RTL?
  455. }
  456. TQString author = article.author();
  457. if (!author.isEmpty())
  458. {
  459. text += TQString("<br/><span class=\"header\" dir=\"%1\">").tqarg(directionOf(i18n("Author")));
  460. text += TQString ("%1:").tqarg(i18n("Author"));
  461. text += "</span><span class=\"headertext\">";
  462. text += author+"</span>\n"; // TODO: might need RTL?
  463. }
  464. text += "</div>\n"; // end headerbox
  465. if (feed && !feed->image().isNull())
  466. {
  467. TQString file = Utils::fileNameForUrl(feed->xmlUrl());
  468. KURL u(m_imageDir);
  469. u.setFileName(file);
  470. text += TQString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").tqarg(feed->htmlUrl()).tqarg(u.url());
  471. }
  472. if (!article.description().isEmpty())
  473. {
  474. text += TQString("<div dir=\"%1\">").tqarg(directionOf(Utils::stripTags(article.description())) );
  475. text += "<span class=\"content\">"+article.description()+"</span>";
  476. text += "</div>";
  477. }
  478. text += "<div class=\"body\">";
  479. if (article.commentsLink().isValid())
  480. {
  481. text += "<a class=\"contentlink\" href=\"";
  482. text += article.commentsLink().url();
  483. text += "\">" + i18n( "Comments");
  484. if (article.comments())
  485. {
  486. text += " ("+ TQString::number(article.comments()) +")";
  487. }
  488. text += "</a>";
  489. }
  490. if (link.isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
  491. {
  492. text += "<p><a class=\"contentlink\" href=\"";
  493. // in case link isn't valid, fall back to the guid permaLink.
  494. if (link.isValid())
  495. {
  496. text += link.url();
  497. }
  498. else
  499. {
  500. text += article.guid();
  501. }
  502. text += "\">" + i18n( "Complete Story" ) + "</a></p>";
  503. }
  504. text += "</div>";
  505. //kdDebug() << text << endl;
  506. return text;
  507. }
  508. void ArticleViewer::renderContent(const TQString& text)
  509. {
  510. closeURL();
  511. m_currentText = text;
  512. beginWriting();
  513. //kdDebug() << text << endl;
  514. write(text);
  515. endWriting();
  516. }
  517. void ArticleViewer::beginWriting()
  518. {
  519. TQString head = TQString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n <html><head><title>.</title></head>");
  520. view()->setContentsPos(0,0);
  521. begin(m_link);
  522. setUserStyleSheet(m_viewMode == CombinedView ? m_combinedModeCSS : m_normalModeCSS);
  523. write(head);
  524. }
  525. void ArticleViewer::endWriting()
  526. {
  527. write(m_htmlFooter);
  528. //kdDebug() << m_htmlFooter << endl;
  529. end();
  530. }
  531. void ArticleViewer::slotShowSummary(TreeNode* node)
  532. {
  533. m_viewMode = SummaryView;
  534. if (!node)
  535. {
  536. slotClear();
  537. return;
  538. }
  539. if (node != m_node)
  540. {
  541. disconnectFromNode(m_node);
  542. connectToNode(node);
  543. m_node = node;
  544. }
  545. m_showSummaryVisitor->visit(node);
  546. }
  547. void ArticleViewer::slotShowArticle(const Article& article)
  548. {
  549. m_viewMode = NormalView;
  550. disconnectFromNode(m_node);
  551. m_article = article;
  552. m_node = 0;
  553. m_link = article.link();
  554. if (article.feed()->loadLinkedWebsite())
  555. openURL(article.link());
  556. else
  557. renderContent( formatArticleNormalMode(article.feed(), article) );
  558. }
  559. void ArticleViewer::slotSetFilter(const Akregator::Filters::ArticleMatcher& textFilter, const Akregator::Filters::ArticleMatcher& statusFilter)
  560. {
  561. if (m_statusFilter == statusFilter && m_textFilter == textFilter)
  562. return;
  563. m_textFilter = textFilter;
  564. m_statusFilter = statusFilter;
  565. slotUpdateCombinedView();
  566. }
  567. void ArticleViewer::slotUpdateCombinedView()
  568. {
  569. if (m_viewMode != CombinedView)
  570. return;
  571. if (!m_node)
  572. return slotClear();
  573. TQValueList<Article> articles = m_node->articles();
  574. qHeapSort(articles);
  575. TQValueList<Article>::ConstIterator end = articles.end();
  576. TQValueList<Article>::ConstIterator it = articles.begin();
  577. TQString text;
  578. int num = 0;
  579. TQTime spent;
  580. spent.start();
  581. for ( ; it != end; ++it)
  582. {
  583. if ( !(*it).isDeleted() && m_textFilter.matches(*it) && m_statusFilter.matches(*it) )
  584. {
  585. text += "<p><div class=\"article\">"+formatArticleCombinedMode(0, *it)+"</div><p>";
  586. ++num;
  587. }
  588. }
  589. //kdDebug() << "Combined view rendering: (" << num << " articles):\n" << "generating HTML: " << spent.elapsed() << "ms " << endl;
  590. renderContent(text);
  591. //kdDebug() << "HTML rendering: " << spent.elapsed() << "ms" << endl;
  592. }
  593. void ArticleViewer::slotArticlesUpdated(TreeNode* /*node*/, const TQValueList<Article>& /*list*/)
  594. {
  595. if (m_viewMode == CombinedView)
  596. slotUpdateCombinedView();
  597. }
  598. void ArticleViewer::slotArticlesAdded(TreeNode* /*node*/, const TQValueList<Article>& /*list*/)
  599. {
  600. }
  601. void ArticleViewer::slotArticlesRemoved(TreeNode* /*node*/, const TQValueList<Article>& /*list*/)
  602. {
  603. }
  604. /* testingtesting :)
  605. void ArticleViewer::slotPopupMenu(KXMLGUIClient*, const TQPoint& p, const KURL& kurl, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags, mode_t)
  606. {
  607. kdDebug() << m_link << endl;
  608. kdDebug() << kurl.url() << endl;
  609. }*/
  610. void ArticleViewer::slotClear()
  611. {
  612. disconnectFromNode(m_node);
  613. m_node = 0;
  614. m_article = Article();
  615. renderContent(TQString());
  616. }
  617. void ArticleViewer::slotShowNode(TreeNode* node)
  618. {
  619. m_viewMode = CombinedView;
  620. if (node != m_node)
  621. disconnectFromNode(m_node);
  622. connectToNode(node);
  623. m_article = Article();
  624. m_node = node;
  625. if (node && !node->articles().isEmpty())
  626. m_link = node->articles().first().link();
  627. else
  628. m_link = KURL();
  629. slotUpdateCombinedView();
  630. }
  631. void ArticleViewer::keyPressEvent(TQKeyEvent* e)
  632. {
  633. e->ignore();
  634. }
  635. void ArticleViewer::urlSelected(const TQString &url, int button, int state, const TQString& _target, KParts::URLArgs args)
  636. {
  637. if(url == "config:/disable_introduction") {
  638. 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) {
  639. KConfig *conf = Settings::self()->config();
  640. conf->setGroup("General");
  641. conf->writeEntry("Disable Introduction", "true");
  642. }
  643. }
  644. else
  645. Viewer::urlSelected(url, button, state, _target, args);
  646. }
  647. void ArticleViewer::slotPaletteOrFontChanged()
  648. {
  649. generateNormalModeCSS();
  650. generateCombinedModeCSS();
  651. reload();
  652. }
  653. void ArticleViewer::connectToNode(TreeNode* node)
  654. {
  655. if (node)
  656. {
  657. if (m_viewMode == CombinedView)
  658. {
  659. // connect( node, TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotUpdateCombinedView() ) );
  660. connect( node, TQT_SIGNAL(signalArticlesAdded(TreeNode*, const TQValueList<Article>&)), this, TQT_SLOT(slotArticlesAdded(TreeNode*, const TQValueList<Article>&)));
  661. connect( node, TQT_SIGNAL(signalArticlesRemoved(TreeNode*, const TQValueList<Article>&)), this, TQT_SLOT(slotArticlesRemoved(TreeNode*, const TQValueList<Article>&)));
  662. connect( node, TQT_SIGNAL(signalArticlesUpdated(TreeNode*, const TQValueList<Article>&)), this, TQT_SLOT(slotArticlesUpdated(TreeNode*, const TQValueList<Article>&)));
  663. }
  664. if (m_viewMode == SummaryView)
  665. connect( node, TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotShowSummary(TreeNode*) ) );
  666. connect( node, TQT_SIGNAL(signalDestroyed(TreeNode*)), this, TQT_SLOT(slotClear() ) );
  667. }
  668. }
  669. void ArticleViewer::disconnectFromNode(TreeNode* node)
  670. {
  671. if (node)
  672. {
  673. // disconnect( node, TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotUpdateCombinedView() ) );
  674. disconnect( node, TQT_SIGNAL(signalDestroyed(TreeNode*)), this, TQT_SLOT(slotClear() ) );
  675. disconnect( node, TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotShowSummary(TreeNode*) ) );
  676. disconnect( node, TQT_SIGNAL(signalArticlesAdded(TreeNode*, const TQValueList<Article>&)), this, TQT_SLOT(slotArticlesAdded(TreeNode*, const TQValueList<Article>&)));
  677. disconnect( node, TQT_SIGNAL(signalArticlesRemoved(TreeNode*, const TQValueList<Article>&)), this, TQT_SLOT(slotArticlesRemoved(TreeNode*, const TQValueList<Article>&)));
  678. disconnect( node, TQT_SIGNAL(signalArticlesUpdated(TreeNode*, const TQValueList<Article>&)), this, TQT_SLOT(slotArticlesUpdated(TreeNode*, const TQValueList<Article>&)));
  679. }
  680. }
  681. }
  682. #include "articleviewer.moc"