articleviewer.cpp
00001 /* 00002 This file is part of Akregator. 00003 00004 Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> 00005 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> 00006 This program is free software; you can redistribute it and/or modify 00007 it under the terms of the GNU General Public License as published by 00008 the Free Software Foundation; either version 2 of the License, or 00009 (at your option) any later version. 00010 00011 This program is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 GNU General Public License for more details. 00015 00016 You should have received a copy of the GNU General Public License 00017 along with this program; if not, write to the Free Software 00018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 00020 As a special exception, permission is given to link this program 00021 with any edition of TQt, and distribute the resulting executable, 00022 without including the source code for TQt in the source distribution. 00023 */ 00024 00025 #include <tqdatetime.h> 00026 #include <tqevent.h> 00027 #include <tqscrollview.h> 00028 #include <tqvaluelist.h> 00029 00030 #include <kaction.h> 00031 #include <kapplication.h> 00032 #include <kdebug.h> 00033 #include <kglobalsettings.h> 00034 #include <khtmlview.h> 00035 #include <klocale.h> 00036 #include <kprocess.h> 00037 #include <krun.h> 00038 #include <kstandarddirs.h> 00039 #include <kshell.h> 00040 #include <kmessagebox.h> 00041 #include <kio/netaccess.h> 00042 #include <libkdepim/kfileio.h> 00043 00044 #include "aboutdata.h" 00045 #include "akregator_run.h" 00046 #include "akregatorconfig.h" 00047 #include "articleviewer.h" 00048 #include "feed.h" 00049 #include "folder.h" 00050 #include "article.h" 00051 #include "treenode.h" 00052 #include "treenodevisitor.h" 00053 #include "tagnode.h" 00054 #include "utils.h" 00055 00056 namespace Akregator { 00057 00058 // from kmail::headerstyle.cpp 00059 static inline TQString directionOf(const TQString &str) 00060 { 00061 return str.isRightToLeft() ? "rtl" : "ltr" ; 00062 } 00063 00064 class ArticleViewer::ShowSummaryVisitor : public TreeNodeVisitor 00065 { 00066 public: 00067 00068 ShowSummaryVisitor(ArticleViewer* view) : m_view(view) {} 00069 00070 virtual bool visitFeed(Feed* node) 00071 { 00072 m_view->m_link = TQString(); 00073 00074 TQString text; 00075 text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ? "rtl" : "ltr"); 00076 00077 text += TQString("<div class=\"headertitle\" dir=\"%1\">").arg(directionOf(Utils::stripTags(node->title()))); 00078 text += node->title(); 00079 if(node->unread() == 0) 00080 text += i18n(" (no unread articles)"); 00081 else 00082 text += i18n(" (1 unread article)", " (%n unread articles)", node->unread()); 00083 text += "</div>\n"; // headertitle 00084 text += "</div>\n"; // /headerbox 00085 00086 if (!node->image().isNull()) // image 00087 { 00088 text += TQString("<div class=\"body\">"); 00089 TQString url=node->xmlUrl(); 00090 TQString file = url.replace("/", "_").replace(":", "_"); 00091 KURL u(m_view->m_imageDir); 00092 u.setFileName(file); 00093 text += TQString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(node->htmlUrl()).arg(u.url()); 00094 } 00095 else text += "<div class=\"body\">"; 00096 00097 00098 if( !node->description().isEmpty() ) 00099 { 00100 text += TQString("<div dir=\"%1\">").arg(Utils::stripTags(directionOf(node->description()))); 00101 text += i18n("<b>Description:</b> %1<br><br>").arg(node->description()); 00102 text += "</div>\n"; // /description 00103 } 00104 00105 if ( !node->htmlUrl().isEmpty() ) 00106 { 00107 text += TQString("<div dir=\"%1\">").arg(directionOf(node->htmlUrl())); 00108 text += i18n("<b>Homepage:</b> <a href=\"%1\">%2</a>").arg(node->htmlUrl()).arg(node->htmlUrl()); 00109 text += "</div>\n"; // / link 00110 } 00111 00112 //text += i18n("<b>Unread articles:</b> %1").arg(node->unread()); 00113 text += "</div>"; // /body 00114 00115 m_view->renderContent(text); 00116 return true; 00117 } 00118 00119 virtual bool visitFolder(Folder* node) 00120 { 00121 m_view->m_link = TQString(); 00122 00123 TQString text; 00124 text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ? "rtl" : "ltr"); 00125 text += TQString("<div class=\"headertitle\" dir=\"%1\">%2").arg(directionOf(Utils::stripTags(node->title()))).arg(node->title()); 00126 if(node->unread() == 0) 00127 text += i18n(" (no unread articles)"); 00128 else 00129 text += i18n(" (1 unread article)", " (%n unread articles)", node->unread()); 00130 text += TQString("</div>\n"); 00131 text += "</div>\n"; // /headerbox 00132 00133 m_view->renderContent(text); 00134 return true; 00135 } 00136 00137 virtual bool visitTagNode(TagNode* node) 00138 { 00139 m_view->m_link = TQString(); 00140 00141 TQString text; 00142 text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ? "rtl" : "ltr"); 00143 text += TQString("<div class=\"headertitle\" dir=\"%1\">%2").arg(directionOf(Utils::stripTags(node->title()))).arg(node->title()); 00144 if(node->unread() == 0) 00145 text += i18n(" (no unread articles)"); 00146 else 00147 text += i18n(" (1 unread article)", " (%n unread articles)", node->unread()); 00148 text += TQString("</div>\n"); 00149 text += "</div>\n"; // /headerbox 00150 00151 m_view->renderContent(text); 00152 return true; 00153 } 00154 00155 private: 00156 00157 ArticleViewer* m_view; 00158 }; 00159 00160 ArticleViewer::ArticleViewer(TQWidget *parent, const char *name) 00161 : Viewer(parent, name), m_htmlFooter(), m_currentText(), m_node(0), m_viewMode(NormalView) 00162 { 00163 setJScriptEnabled(false); 00164 setJavaEnabled(false); 00165 setPluginsEnabled(false); 00166 00167 m_showSummaryVisitor = new ShowSummaryVisitor(this); 00168 setXMLFile(locate("data", "akregator/articleviewer.rc"), true); 00169 00170 generateNormalModeCSS(); 00171 generateCombinedModeCSS(); 00172 new KAction( i18n("&Scroll Up"), TQString(), "Up", this, TQT_SLOT(slotScrollUp()), actionCollection(), "articleviewer_scroll_up" ); 00173 new KAction( i18n("&Scroll Down"), TQString(), "Down", this, TQT_SLOT(slotScrollDown()), actionCollection(), "articleviewer_scroll_down" ); 00174 00175 connect(this, TQT_SIGNAL(selectionChanged()), this, TQT_SLOT(slotSelectionChanged())); 00176 00177 connect(kapp, TQT_SIGNAL(kdisplayPaletteChanged()), this, TQT_SLOT(slotPaletteOrFontChanged()) ); 00178 connect(kapp, TQT_SIGNAL(kdisplayFontChanged()), this, TQT_SLOT(slotPaletteOrFontChanged()) ); 00179 00180 m_imageDir.setPath(KGlobal::dirs()->saveLocation("cache", "akregator/Media/")); 00181 m_htmlFooter = "</body></html>"; 00182 } 00183 00184 ArticleViewer::~ArticleViewer() 00185 { 00186 delete m_showSummaryVisitor; 00187 } 00188 00189 void ArticleViewer::generateNormalModeCSS() 00190 { 00191 const TQColorGroup & cg = TQApplication::palette().active(); 00192 00193 // from kmail::headerstyle.cpp 00194 m_normalModeCSS = TQString( 00195 "@media screen, print {" 00196 "body {\n" 00197 " font-family: \"%1\" ! important;\n" 00198 " font-size: %2 ! important;\n" 00199 " color: %3 ! important;\n" 00200 " background: %4 ! important;\n" 00201 "}\n\n").arg(Settings::standardFont()) 00202 .arg(TQString::number(pointsToPixel(Settings::mediumFontSize()))+"px") 00203 .arg(cg.text().name()) 00204 .arg(cg.base().name()); 00205 m_normalModeCSS += TQString( 00206 "a {\n" 00207 + TQString(" color: %1 ! important;\n") 00208 + TQString(!Settings::underlineLinks() ? " text-decoration: none ! important;\n" : "") 00209 + "}\n\n" 00210 +".headerbox {\n" 00211 +" background: %2 ! important;\n" 00212 +" color: %3 ! important;\n" 00213 +" border:1px solid #000;\n" 00214 +" margin-bottom: 10pt;\n" 00215 // +" width: 99%;\n" 00216 + "}\n\n") 00217 .arg(cg.link().name()) 00218 .arg(cg.background().name()) 00219 .arg(cg.text().name()); 00220 00221 m_normalModeCSS += TQString(".headertitle a:link { color: %1 ! important; }\n" 00222 ".headertitle a:visited { color: %2 ! important; }\n" 00223 ".headertitle a:hover{ color: %3 ! important; }\n" 00224 ".headertitle a:active { color: %4 ! important; }\n") 00225 .arg(cg.highlightedText().name()) 00226 .arg(cg.highlightedText().name()) 00227 .arg(cg.highlightedText().name()) 00228 .arg(cg.highlightedText().name()); 00229 00230 m_normalModeCSS += TQString( 00231 ".headertitle {\n" 00232 " background: %1 ! important;\n" 00233 " padding:2px;\n" 00234 " color: %2 ! important;\n" 00235 " font-weight: bold;\n" 00236 "}\n\n" 00237 ".header {\n" 00238 " font-weight: bold;\n" 00239 " padding:2px;\n" 00240 " margin-right: 5px;\n" 00241 "}\n\n" 00242 ".headertext {\n" 00243 "}\n\n" 00244 ".headimage {\n" 00245 " float: right;\n" 00246 " margin-left: 5px;\n" 00247 "}\n\n").arg(cg.highlight().name()) 00248 .arg(cg.highlightedText().name()); 00249 00250 m_normalModeCSS += TQString( 00251 "body { clear: none; }\n\n" 00252 ".content {\n" 00253 " display: block;\n" 00254 " margin-bottom: 6px;\n" 00255 "}\n\n" 00256 // these rules make sure that there is no leading space between the header and the first of the text 00257 ".content > P:first-child {\n margin-top: 1px; }\n" 00258 ".content > DIV:first-child {\n margin-top: 1px; }\n" 00259 ".content > BR:first-child {\n display: none; }\n" 00260 "iframe {display: none !important; }\n" 00261 "frame {display: none !important; }\n" 00262 "frameset {display: none !important; }\n" 00263 "object {display: none !important; }\n" 00264 "applet {display: none !important; }\n" 00265 "}\n\n"); // @media screen, print 00266 } 00267 00268 void ArticleViewer::generateCombinedModeCSS() 00269 { 00270 const TQColorGroup & cg = TQApplication::palette().active(); 00271 00272 // from kmail::headerstyle.cpp 00273 m_combinedModeCSS = TQString ( 00274 // "<style type=\"text/css\">\n" 00275 "@media screen, print {" 00276 "body {\n" 00277 " font-family: \"%1\" ! important;\n" 00278 " font-size: %2 ! important;\n" 00279 " color: %3 ! important;\n" 00280 " background: %4 ! important;\n" 00281 "}\n\n").arg(Settings::standardFont()) 00282 .arg(TQString::number(pointsToPixel(Settings::mediumFontSize()))+"px") 00283 .arg(cg.text().name()) 00284 .arg(cg.base().name()); 00285 m_combinedModeCSS += ( 00286 "a {\n" 00287 + TQString(" color: %1 ! important;\n") 00288 + TQString(!Settings::underlineLinks() ? " text-decoration: none ! important;\n" : "") 00289 + "}\n\n" 00290 +".headerbox {\n" 00291 +" background: %2 ! important;\n" 00292 +" color: %3 ! important;\n" 00293 +" border:1px solid #000;\n" 00294 +" margin-bottom: 10pt;\n" 00295 // +" width: 99%;\n" 00296 + "}\n\n") 00297 .arg(cg.link().name()) 00298 .arg(cg.background().name()) 00299 .arg(cg.text().name()); 00300 00301 m_combinedModeCSS += TQString(".headertitle a:link { color: %1 ! important; }\n" 00302 ".headertitle a:visited { color: %2 ! important; }\n" 00303 ".headertitle a:hover{ color: %3 ! important; }\n" 00304 ".headertitle a:active { color: %4 ! important; }\n") 00305 .arg(cg.highlightedText().name()) 00306 .arg(cg.highlightedText().name()) 00307 .arg(cg.highlightedText().name()) 00308 .arg(cg.highlightedText().name()); 00309 m_combinedModeCSS += TQString( 00310 ".headertitle {\n" 00311 " background: %1 ! important;\n" 00312 " padding:2px;\n" 00313 " color: %2 ! important;\n" 00314 " font-weight: bold;\n" 00315 "}\n\n" 00316 ".header {\n" 00317 " font-weight: bold;\n" 00318 " padding:2px;\n" 00319 " margin-right: 5px;\n" 00320 "}\n\n" 00321 ".headertext {\n" 00322 "}\n\n" 00323 ".headimage {\n" 00324 " float: right;\n" 00325 " margin-left: 5px;\n" 00326 "}\n\n").arg(cg.highlight().name()) 00327 .arg(cg.highlightedText().name()); 00328 00329 m_combinedModeCSS += TQString( 00330 "body { clear: none; }\n\n" 00331 ".content {\n" 00332 " display: block;\n" 00333 " margin-bottom: 6px;\n" 00334 "}\n\n" 00335 // these rules make sure that there is no leading space between the header and the first of the text 00336 ".content > P:first-child {\n margin-top: 1px; }\n" 00337 ".content > DIV:first-child {\n margin-top: 1px; }\n" 00338 ".content > BR:first-child {\n display: none; }\n" 00339 "iframe {display: none !important; }\n" 00340 "frame {display: none !important; }\n" 00341 "frameset {display: none !important; }\n" 00342 "object {display: none !important; }\n" 00343 "applet {display: none !important; }\n" 00344 "}\n\n"); // @media screen, print 00345 } 00346 00347 void ArticleViewer::reload() 00348 { 00349 beginWriting(); 00350 write(m_currentText); 00351 endWriting(); 00352 } 00353 00354 bool ArticleViewer::openURL(const KURL& url) 00355 { 00356 if (!m_article.isNull() && m_article.feed()->loadLinkedWebsite()) 00357 { 00358 return Viewer::openURL(url); 00359 } 00360 else 00361 { 00362 reload(); 00363 return true; 00364 } 00365 } 00366 00367 void ArticleViewer::displayAboutPage() 00368 { 00369 TQString location = locate("data", "akregator/about/main.html"); 00370 TQString content = KPIM::kFileToString(location); 00371 content = content.arg( locate( "data", "libkdepim/about/kde_infopage.css" ) ); 00372 if ( kapp->reverseLayout() ) 00373 content = content.arg( "@import \"%1\";" ).arg( locate( "data", "libkdepim/about/kde_infopage_rtl.css" ) ); 00374 else 00375 content = content.arg( "" ); 00376 00377 begin(KURL( location )); 00378 TQString info = 00379 i18n("%1: Akregator version; %2: help:// URL; %3: homepage URL; " 00380 "--- end of comment ---", 00381 "<h2 style='margin-top: 0px;'>Welcome to Akregator %1</h2>" 00382 "<p>Akregator is an RSS feed aggregator for the K Desktop Environment. " 00383 "Feed aggregators provide a convenient way to browse different kinds of " 00384 "content, including news, blogs, and other content from online sites. " 00385 "Instead of checking all your favorite web sites manually for updates, " 00386 "Akregator collects the content for you.</p>" 00387 "<p>For more information about using Akregator, check the " 00388 "<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>" 00389 "<p>We hope that you will enjoy Akregator.</p>\n" 00390 "<p>Thank you,</p>\n" 00391 "<p style='margin-bottom: 0px'> The Akregator Team</p>\n") 00392 .arg(AKREGATOR_VERSION) // Akregator version 00393 .arg("http://akregator.kde.org/"); // Akregator homepage URL 00394 00395 TQString fontSize = TQString::number( pointsToPixel( Settings::mediumFontSize() )); 00396 TQString appTitle = i18n("Akregator"); 00397 TQString catchPhrase = ""; //not enough space for a catch phrase at default window size i18n("Part of the Kontact Suite"); 00398 TQString quickDescription = i18n("An RSS feed reader for the K Desktop Environment."); 00399 write(content.arg(fontSize).arg(appTitle).arg(catchPhrase).arg(quickDescription).arg(info)); 00400 end(); 00401 } 00402 00403 TQString ArticleViewer::formatArticleNormalMode(Feed* feed, const Article& article) 00404 { 00405 TQString text; 00406 text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ? "rtl" : "ltr"); 00407 00408 if (!article.title().isEmpty()) 00409 { 00410 text += TQString("<div class=\"headertitle\" dir=\"%1\">\n").arg(directionOf(Utils::stripTags(article.title()))); 00411 if (article.link().isValid()) 00412 text += "<a href=\""+article.link().url()+"\">"; 00413 text += article.title().replace("<", "<").replace(">", ">"); // TODO: better leave things escaped in the parser 00414 if (article.link().isValid()) 00415 text += "</a>"; 00416 text += "</div>\n"; 00417 } 00418 if (article.pubDate().isValid()) 00419 { 00420 text += TQString("<span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Date"))); 00421 text += TQString ("%1:").arg(i18n("Date")); 00422 text += "</span><span class=\"headertext\">"; 00423 text += KGlobal::locale()->formatDateTime(article.pubDate(), false, false)+"</span>\n"; // TODO: might need RTL? 00424 } 00425 TQString author = article.author(); 00426 if (!author.isEmpty()) 00427 { 00428 text += TQString("<br/><span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Author"))); 00429 text += TQString ("%1:").arg(i18n("Author")); 00430 text += "</span><span class=\"headertext\">"; 00431 text += author+"</span>\n"; // TODO: might need RTL? 00432 } 00433 text += "</div>\n"; // end headerbox 00434 00435 if (feed && !feed->image().isNull()) 00436 { 00437 TQString file = Utils::fileNameForUrl(feed->xmlUrl()); 00438 KURL u(m_imageDir); 00439 u.setFileName(file); 00440 text += TQString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->htmlUrl()).arg(u.url()); 00441 } 00442 00443 00444 00445 if (!article.description().isEmpty()) 00446 { 00447 text += TQString("<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) ); 00448 text += "<span class=\"content\">"+article.description()+"</span>"; 00449 text += "</div>"; 00450 } 00451 00452 text += "<div class=\"body\">"; 00453 00454 if (article.commentsLink().isValid()) 00455 { 00456 text += "<a class=\"contentlink\" href=\""; 00457 text += article.commentsLink().url(); 00458 text += "\">" + i18n( "Comments"); 00459 if (article.comments()) 00460 { 00461 text += " ("+ TQString::number(article.comments()) +")"; 00462 } 00463 text += "</a>"; 00464 } 00465 00466 if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid())) 00467 { 00468 text += "<p><a class=\"contentlink\" href=\""; 00469 // in case link isn't valid, fall back to the guid permaLink. 00470 if (article.link().isValid()) 00471 { 00472 text += article.link().url(); 00473 } 00474 else 00475 { 00476 text += article.guid(); 00477 } 00478 text += "\">" + i18n( "Complete Story" ) + "</a></p>"; 00479 } 00480 text += "</div>"; 00481 00482 if (!article.enclosure().isNull()) 00483 { 00484 //TQString url = article.enclosure().url(); 00485 //TQString type = article.enclosure().type(); 00486 //int length = article.enclosure().length(); 00487 //TQString lengthStr = KIO::convertSize(length); 00488 00489 //text += TQString("<hr><div><a href=\"%1\">%2</a> (%3, %4)</div>").arg(url).arg(url).arg(lengthStr).arg(type); 00490 } 00491 //kdDebug() << text << endl; 00492 return text; 00493 00494 } 00495 00496 TQString ArticleViewer::formatArticleCombinedMode(Feed* feed, const Article& article) 00497 { 00498 TQString text; 00499 text = TQString("<div class=\"headerbox\" dir=\"%1\">\n").arg(TQApplication::reverseLayout() ? "rtl" : "ltr"); 00500 00501 KURL link = article.link(); 00502 00503 if (!article.title().isEmpty()) 00504 { 00505 text += TQString("<div class=\"headertitle\" dir=\"%1\">\n").arg(directionOf(Utils::stripTags(article.title()))); 00506 if (link.isValid()) 00507 text += "<a href=\""+link.url()+"\">"; 00508 text += article.title().replace("<", "<").replace(">", ">"); // TODO: better leave things escaped in the parser 00509 if (link.isValid()) 00510 text += "</a>"; 00511 text += "</div>\n"; 00512 } 00513 if (article.pubDate().isValid()) 00514 { 00515 text += TQString("<span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Date"))); 00516 text += TQString ("%1:").arg(i18n("Date")); 00517 text += "</span><span class=\"headertext\">"; 00518 text += KGlobal::locale()->formatDateTime(article.pubDate(), false, false)+"</span>\n"; // TODO: might need RTL? 00519 } 00520 00521 TQString author = article.author(); 00522 if (!author.isEmpty()) 00523 { 00524 text += TQString("<br/><span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Author"))); 00525 text += TQString ("%1:").arg(i18n("Author")); 00526 text += "</span><span class=\"headertext\">"; 00527 text += author+"</span>\n"; // TODO: might need RTL? 00528 } 00529 00530 text += "</div>\n"; // end headerbox 00531 00532 if (feed && !feed->image().isNull()) 00533 { 00534 TQString file = Utils::fileNameForUrl(feed->xmlUrl()); 00535 KURL u(m_imageDir); 00536 u.setFileName(file); 00537 text += TQString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->htmlUrl()).arg(u.url()); 00538 } 00539 00540 00541 00542 if (!article.description().isEmpty()) 00543 { 00544 text += TQString("<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) ); 00545 text += "<span class=\"content\">"+article.description()+"</span>"; 00546 text += "</div>"; 00547 } 00548 00549 text += "<div class=\"body\">"; 00550 00551 if (article.commentsLink().isValid()) 00552 { 00553 text += "<a class=\"contentlink\" href=\""; 00554 text += article.commentsLink().url(); 00555 text += "\">" + i18n( "Comments"); 00556 if (article.comments()) 00557 { 00558 text += " ("+ TQString::number(article.comments()) +")"; 00559 } 00560 text += "</a>"; 00561 } 00562 00563 if (link.isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid())) 00564 { 00565 text += "<p><a class=\"contentlink\" href=\""; 00566 // in case link isn't valid, fall back to the guid permaLink. 00567 if (link.isValid()) 00568 { 00569 text += link.url(); 00570 } 00571 else 00572 { 00573 text += article.guid(); 00574 } 00575 text += "\">" + i18n( "Complete Story" ) + "</a></p>"; 00576 } 00577 text += "</div>"; 00578 //kdDebug() << text << endl; 00579 return text; 00580 00581 } 00582 00583 void ArticleViewer::renderContent(const TQString& text) 00584 { 00585 closeURL(); 00586 m_currentText = text; 00587 beginWriting(); 00588 //kdDebug() << text << endl; 00589 write(text); 00590 endWriting(); 00591 } 00592 00593 void ArticleViewer::beginWriting() 00594 { 00595 TQString head = TQString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n <html><head><title>.</title></head>"); 00596 view()->setContentsPos(0,0); 00597 begin(m_link); 00598 setUserStyleSheet(m_viewMode == CombinedView ? m_combinedModeCSS : m_normalModeCSS); 00599 write(head); 00600 } 00601 00602 void ArticleViewer::endWriting() 00603 { 00604 write(m_htmlFooter); 00605 //kdDebug() << m_htmlFooter << endl; 00606 end(); 00607 } 00608 00609 void ArticleViewer::slotShowSummary(TreeNode* node) 00610 { 00611 m_viewMode = SummaryView; 00612 00613 if (!node) 00614 { 00615 slotClear(); 00616 return; 00617 } 00618 00619 if (node != m_node) 00620 { 00621 disconnectFromNode(m_node); 00622 connectToNode(node); 00623 m_node = node; 00624 } 00625 m_showSummaryVisitor->visit(node); 00626 } 00627 00628 00629 void ArticleViewer::slotShowArticle(const Article& article) 00630 { 00631 m_viewMode = NormalView; 00632 disconnectFromNode(m_node); 00633 m_article = article; 00634 m_node = 0; 00635 m_link = article.link(); 00636 if (article.feed()->loadLinkedWebsite()) 00637 openURL(article.link()); 00638 else 00639 renderContent( formatArticleNormalMode(article.feed(), article) ); 00640 } 00641 00642 void ArticleViewer::slotSetFilter(const Akregator::Filters::ArticleMatcher& textFilter, const Akregator::Filters::ArticleMatcher& statusFilter) 00643 { 00644 if (m_statusFilter == statusFilter && m_textFilter == textFilter) 00645 return; 00646 00647 m_textFilter = textFilter; 00648 m_statusFilter = statusFilter; 00649 00650 slotUpdateCombinedView(); 00651 } 00652 00653 void ArticleViewer::slotUpdateCombinedView() 00654 { 00655 if (m_viewMode != CombinedView) 00656 return; 00657 00658 if (!m_node) 00659 return slotClear(); 00660 00661 TQValueList<Article> articles = m_node->articles(); 00662 qHeapSort(articles); 00663 TQValueList<Article>::ConstIterator end = articles.end(); 00664 TQValueList<Article>::ConstIterator it = articles.begin(); 00665 00666 TQString text; 00667 00668 int num = 0; 00669 TQTime spent; 00670 spent.start(); 00671 00672 for ( ; it != end; ++it) 00673 { 00674 if ( !(*it).isDeleted() && m_textFilter.matches(*it) && m_statusFilter.matches(*it) ) 00675 { 00676 text += "<p><div class=\"article\">"+formatArticleCombinedMode(0, *it)+"</div><p>"; 00677 ++num; 00678 } 00679 } 00680 //kdDebug() << "Combined view rendering: (" << num << " articles):\n" << "generating HTML: " << spent.elapsed() << "ms " << endl; 00681 renderContent(text); 00682 //kdDebug() << "HTML rendering: " << spent.elapsed() << "ms" << endl; 00683 00684 00685 } 00686 00687 void ArticleViewer::slotArticlesUpdated(TreeNode* /*node*/, const TQValueList<Article>& /*list*/) 00688 { 00689 if (m_viewMode == CombinedView) 00690 slotUpdateCombinedView(); 00691 } 00692 00693 void ArticleViewer::slotArticlesAdded(TreeNode* /*node*/, const TQValueList<Article>& /*list*/) 00694 { 00695 } 00696 00697 void ArticleViewer::slotArticlesRemoved(TreeNode* /*node*/, const TQValueList<Article>& /*list*/) 00698 { 00699 } 00700 00701 /* testingtesting :) 00702 void ArticleViewer::slotPopupMenu(KXMLGUIClient*, const TQPoint& p, const KURL& kurl, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags, mode_t) 00703 { 00704 kdDebug() << m_link << endl; 00705 kdDebug() << kurl.url() << endl; 00706 }*/ 00707 00708 00709 void ArticleViewer::slotClear() 00710 { 00711 disconnectFromNode(m_node); 00712 m_node = 0; 00713 m_article = Article(); 00714 00715 renderContent(TQString()); 00716 } 00717 00718 void ArticleViewer::slotShowNode(TreeNode* node) 00719 { 00720 m_viewMode = CombinedView; 00721 00722 if (node != m_node) 00723 disconnectFromNode(m_node); 00724 00725 connectToNode(node); 00726 00727 m_article = Article(); 00728 m_node = node; 00729 00730 if (node && !node->articles().isEmpty()) 00731 m_link = node->articles().first().link(); 00732 else 00733 m_link = KURL(); 00734 00735 slotUpdateCombinedView(); 00736 } 00737 00738 void ArticleViewer::keyPressEvent(TQKeyEvent* e) 00739 { 00740 e->ignore(); 00741 } 00742 00743 void ArticleViewer::urlSelected(const TQString &url, int button, int state, const TQString& _target, KParts::URLArgs args) 00744 { 00745 if(url == "config:/disable_introduction") { 00746 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) { 00747 KConfig *conf = Settings::self()->config(); 00748 conf->setGroup("General"); 00749 conf->writeEntry("Disable Introduction", "true"); 00750 } 00751 } 00752 else 00753 Viewer::urlSelected(url, button, state, _target, args); 00754 } 00755 00756 void ArticleViewer::slotPaletteOrFontChanged() 00757 { 00758 generateNormalModeCSS(); 00759 generateCombinedModeCSS(); 00760 reload(); 00761 } 00762 00763 void ArticleViewer::connectToNode(TreeNode* node) 00764 { 00765 if (node) 00766 { 00767 if (m_viewMode == CombinedView) 00768 { 00769 // connect( node, TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotUpdateCombinedView() ) ); 00770 connect( node, TQT_SIGNAL(signalArticlesAdded(TreeNode*, const TQValueList<Article>&)), this, TQT_SLOT(slotArticlesAdded(TreeNode*, const TQValueList<Article>&))); 00771 connect( node, TQT_SIGNAL(signalArticlesRemoved(TreeNode*, const TQValueList<Article>&)), this, TQT_SLOT(slotArticlesRemoved(TreeNode*, const TQValueList<Article>&))); 00772 connect( node, TQT_SIGNAL(signalArticlesUpdated(TreeNode*, const TQValueList<Article>&)), this, TQT_SLOT(slotArticlesUpdated(TreeNode*, const TQValueList<Article>&))); 00773 } 00774 if (m_viewMode == SummaryView) 00775 connect( node, TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotShowSummary(TreeNode*) ) ); 00776 00777 connect( node, TQT_SIGNAL(signalDestroyed(TreeNode*)), this, TQT_SLOT(slotClear() ) ); 00778 } 00779 } 00780 00781 void ArticleViewer::disconnectFromNode(TreeNode* node) 00782 { 00783 if (node) 00784 { 00785 // disconnect( node, TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotUpdateCombinedView() ) ); 00786 disconnect( node, TQT_SIGNAL(signalDestroyed(TreeNode*)), this, TQT_SLOT(slotClear() ) ); 00787 disconnect( node, TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotShowSummary(TreeNode*) ) ); 00788 disconnect( node, TQT_SIGNAL(signalArticlesAdded(TreeNode*, const TQValueList<Article>&)), this, TQT_SLOT(slotArticlesAdded(TreeNode*, const TQValueList<Article>&))); 00789 disconnect( node, TQT_SIGNAL(signalArticlesRemoved(TreeNode*, const TQValueList<Article>&)), this, TQT_SLOT(slotArticlesRemoved(TreeNode*, const TQValueList<Article>&))); 00790 disconnect( node, TQT_SIGNAL(signalArticlesUpdated(TreeNode*, const TQValueList<Article>&)), this, TQT_SLOT(slotArticlesUpdated(TreeNode*, const TQValueList<Article>&))); 00791 00792 } 00793 } 00794 00795 } 00796 #include "articleviewer.moc" 00797