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