akregator/src

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 Qt, and distribute the resulting executable,
00022     without including the source code for Qt in the source distribution.
00023 */
00024 
00025 #include <qdatetime.h>
00026 #include <qevent.h>
00027 #include <qscrollview.h>
00028 #include <qvaluelist.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 QString directionOf(const QString &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 = QString();
00073         
00074         QString text;
00075         text = QString("<div class=\"headerbox\" dir=\"%1\">\n").arg(QApplication::reverseLayout() ? "rtl" : "ltr");
00076         
00077         text += QString("<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 += QString("<div class=\"body\">");
00089             QString url=node->xmlUrl();
00090             QString file = url.replace("/", "_").replace(":", "_");
00091             KURL u(m_view->m_imageDir);
00092             u.setFileName(file);
00093             text += QString("<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 += QString("<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 += QString("<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 = QString();
00122         
00123         QString text;
00124         text = QString("<div class=\"headerbox\" dir=\"%1\">\n").arg(QApplication::reverseLayout() ? "rtl" : "ltr");
00125         text += QString("<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 += QString("</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 = QString();
00140         
00141         QString text;
00142         text = QString("<div class=\"headerbox\" dir=\"%1\">\n").arg(QApplication::reverseLayout() ? "rtl" : "ltr");
00143         text += QString("<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 += QString("</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(QWidget *parent, const char *name)
00161     : Viewer(parent, name), m_htmlFooter(), m_currentText(), m_node(0), m_viewMode(NormalView)
00162 {
00163     m_showSummaryVisitor = new ShowSummaryVisitor(this);
00164     setXMLFile(locate("data", "akregator/articleviewer.rc"), true);
00165 
00166     generateNormalModeCSS();
00167     generateCombinedModeCSS();
00168     new KAction( i18n("&Scroll Up"), QString::null, "Up", this, SLOT(slotScrollUp()), actionCollection(), "articleviewer_scroll_up" );
00169     new KAction( i18n("&Scroll Down"), QString::null, "Down", this, SLOT(slotScrollDown()), actionCollection(), "articleviewer_scroll_down" );
00170     
00171     connect(this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));
00172 
00173     connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteOrFontChanged()) );
00174     connect(kapp, SIGNAL(kdisplayFontChanged()), this, SLOT(slotPaletteOrFontChanged()) );
00175     
00176     m_imageDir.setPath(KGlobal::dirs()->saveLocation("cache", "akregator/Media/"));
00177     m_htmlFooter = "</body></html>";
00178 }
00179 
00180 ArticleViewer::~ArticleViewer()
00181 {
00182     delete m_showSummaryVisitor;
00183 }
00184 
00185 void ArticleViewer::generateNormalModeCSS()
00186 {
00187     const QColorGroup & cg = QApplication::palette().active();
00188     
00189     // from kmail::headerstyle.cpp
00190     m_normalModeCSS = QString (
00191             "<style type=\"text/css\">\n"
00192             "@media screen, print {"
00193             "body {\n"
00194             "  font-family: \"%1\" ! important;\n"
00195             "  font-size: %2 ! important;\n"
00196             "  color: %3 ! important;\n"
00197             "  background: %4 ! important;\n"
00198             "}\n\n").arg(Settings::standardFont())
00199             .arg(QString::number(pointsToPixel(Settings::mediumFontSize()))+"px")
00200             .arg(cg.text().name())
00201             .arg(cg.base().name());
00202     m_normalModeCSS += (
00203     "a {\n"
00204     + QString("  color: %1 ! important;\n")
00205     + QString(!Settings::underlineLinks() ? " text-decoration: none ! important;\n" : "")
00206     +       "}\n\n"
00207     +".headerbox {\n"
00208     +"  background: %2 ! important;\n"
00209     +"  color: %3 ! important;\n"
00210     +"  border:1px solid #000;\n"
00211     +"  margin-bottom: 10pt;\n"
00212 //    +"  width: 99%;\n"
00213     +        "}\n\n")
00214             .arg(cg.link().name())
00215             .arg(cg.background().name())
00216             .arg(cg.text().name());
00217 
00218     m_normalModeCSS += QString(".headertitle a:link { color: %1  ! important; }\n"
00219     ".headertitle a:visited { color: %2 ! important; }\n"
00220     ".headertitle a:hover{ color: %3 ! important; }\n"
00221             ".headertitle a:active { color: %4 ! important; }\n")
00222             .arg(cg.highlightedText().name())
00223             .arg(cg.highlightedText().name())
00224             .arg(cg.highlightedText().name())
00225             .arg(cg.highlightedText().name());
00226     m_normalModeCSS += QString(
00227     ".headertitle {\n"
00228     "  background: %1 ! important;\n"
00229     "  padding:2px;\n"
00230     "  color: %2 ! important;\n"
00231     "  font-weight: bold;\n"
00232     "}\n\n"
00233     ".header {\n"
00234     "  font-weight: bold;\n"
00235     "  padding:2px;\n"
00236     "  margin-right: 5px;\n"
00237     "}\n\n"
00238     ".headertext {\n"
00239     "}\n\n"
00240     ".headimage {\n"
00241     "  float: right;\n"
00242     "  margin-left: 5px;\n"
00243             "}\n\n").arg(cg.highlight().name())
00244             .arg(cg.highlightedText().name());
00245     
00246     m_normalModeCSS += QString(
00247     "body { clear: none; }\n\n"
00248     ".content {\n"
00249     "  display: block;\n"
00250     "  margin-bottom: 6px;\n"
00251             "}\n\n"
00252     // these rules make sure that there is no leading space between the header and the first of the text
00253     ".content > P:first-child {\n margin-top: 1px; }\n"
00254     ".content > DIV:first-child {\n margin-top: 1px; }\n"
00255     ".content > BR:first-child {\n display: none;  }\n"
00256     //".contentlink {\n display: block; }\n"
00257     "}\n\n" // @media screen, print
00258     // Why did we need that, bug #108187?
00259     //"@media screen { body { overflow: auto; } }\n"
00260     "\n\n");
00261 }
00262 
00263 void ArticleViewer::generateCombinedModeCSS()
00264 {
00265     const QColorGroup & cg = QApplication::palette().active();
00266     
00267     // from kmail::headerstyle.cpp
00268     m_combinedModeCSS = QString (
00269             "<style type=\"text/css\">\n"
00270             "@media screen, print {"
00271             "body {\n"
00272             "  font-family: \"%1\" ! important;\n"
00273             "  font-size: %2 ! important;\n"
00274             "  color: %3 ! important;\n"
00275             "  background: %4 ! important;\n"
00276             "}\n\n").arg(Settings::standardFont())
00277             .arg(QString::number(pointsToPixel(Settings::mediumFontSize()))+"px")
00278             .arg(cg.text().name())
00279             .arg(cg.base().name());
00280     m_combinedModeCSS += (
00281     "a {\n"
00282     + QString("  color: %1 ! important;\n")
00283     + QString(!Settings::underlineLinks() ? " text-decoration: none ! important;\n" : "")
00284     +       "}\n\n"
00285     +".headerbox {\n"
00286     +"  background: %2 ! important;\n"
00287     +"  color: %3 ! important;\n"
00288     +"  border:1px solid #000;\n"
00289     +"  margin-bottom: 10pt;\n"
00290 //    +"  width: 99%;\n"
00291     +        "}\n\n")
00292             .arg(cg.link().name())
00293             .arg(cg.background().name())
00294             .arg(cg.text().name());
00295 
00296     m_combinedModeCSS += QString(".headertitle a:link { color: %1  ! important; }\n"
00297     ".headertitle a:visited { color: %2 ! important; }\n"
00298     ".headertitle a:hover{ color: %3 ! important; }\n"
00299             ".headertitle a:active { color: %4 ! important; }\n")
00300             .arg(cg.highlightedText().name())
00301             .arg(cg.highlightedText().name())
00302             .arg(cg.highlightedText().name())
00303             .arg(cg.highlightedText().name());
00304     m_combinedModeCSS += QString(
00305     ".headertitle {\n"
00306     "  background: %1 ! important;\n"
00307     "  padding:2px;\n"
00308     "  color: %2 ! important;\n"
00309     "  font-weight: bold;\n"
00310     "}\n\n"
00311     ".header {\n"
00312     "  font-weight: bold;\n"
00313     "  padding:2px;\n"
00314     "  margin-right: 5px;\n"
00315     "}\n\n"
00316     ".headertext {\n"
00317     "}\n\n"
00318     ".headimage {\n"
00319     "  float: right;\n"
00320     "  margin-left: 5px;\n"
00321             "}\n\n").arg(cg.highlight().name())
00322             .arg(cg.highlightedText().name());
00323     
00324     m_combinedModeCSS += QString(
00325     "body { clear: none; }\n\n"
00326     ".content {\n"
00327     "  display: block;\n"
00328     "  margin-bottom: 6px;\n"
00329             "}\n\n"
00330     // these rules make sure that there is no leading space between the header and the first of the text
00331     ".content > P:first-child {\n margin-top: 1px; }\n"
00332     ".content > DIV:first-child {\n margin-top: 1px; }\n"
00333     ".content > BR:first-child {\n display: none;  }\n"
00334     //".contentlink {\n display: block; }\n"
00335     "}\n\n" // @media screen, print
00336     // Why did we need that, bug #108187?
00337     //"@media screen { body { overflow: auto; } }\n"
00338     "\n\n");
00339 }
00340 
00341 void ArticleViewer::reload()
00342 {
00343     beginWriting();
00344     write(m_currentText);
00345     endWriting();
00346 }
00347 
00348 bool ArticleViewer::openURL(const KURL& url)
00349 {
00350     if (!m_article.isNull() && m_article.feed()->loadLinkedWebsite())
00351     {
00352         return Viewer::openURL(url);
00353     }
00354     else
00355     {
00356         reload();
00357         return true;
00358     }
00359 }
00360 
00361 void ArticleViewer::displayAboutPage()
00362 {
00363     QString location = locate("data", "akregator/about/main.html");
00364     QString content = KPIM::kFileToString(location);
00365     content = content.arg( locate( "data", "libkdepim/about/kde_infopage.css" ) );
00366     if ( kapp->reverseLayout() )
00367         content = content.arg( "@import \"%1\";" ).arg( locate( "data", "libkdepim/about/kde_infopage_rtl.css" ) );
00368     else
00369         content = content.arg( "" );
00370 
00371     begin(KURL( location ));
00372     QString info =
00373             i18n("%1: Akregator version; %2: help:// URL; %3: homepage URL; "
00374             "--- end of comment ---",
00375     "<h2 style='margin-top: 0px;'>Welcome to Akregator %1</h2>"
00376             "<p>Akregator is an RSS feed aggregator for the K Desktop Environment. "
00377             "Feed aggregators provide a convenient way to browse different kinds of "
00378             "content, including news, blogs, and other content from online sites. "
00379             "Instead of checking all your favorite web sites manually for updates, "
00380             "Akregator collects the content for you.</p>"
00381             "<p>For more information about using Akregator, check the "
00382             "<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>"
00383             "<p>We hope that you will enjoy Akregator.</p>\n"
00384             "<p>Thank you,</p>\n"
00385             "<p style='margin-bottom: 0px'>&nbsp; &nbsp; The Akregator Team</p>\n")
00386             .arg(AKREGATOR_VERSION) // Akregator version
00387             .arg("http://akregator.sourceforge.net/"); // Akregator homepage URL
00388 
00389     QString fontSize = QString::number( pointsToPixel( Settings::mediumFontSize() ));
00390     QString appTitle = i18n("Akregator");
00391     QString catchPhrase = ""; //not enough space for a catch phrase at default window size i18n("Part of the Kontact Suite");
00392     QString quickDescription = i18n("An RSS feed reader for the K Desktop Environment.");
00393     write(content.arg(fontSize).arg(appTitle).arg(catchPhrase).arg(quickDescription).arg(info));
00394     end();
00395 }
00396 
00397 QString ArticleViewer::formatArticleNormalMode(Feed* feed, const Article& article)
00398 {
00399     QString text;
00400     text = QString("<div class=\"headerbox\" dir=\"%1\">\n").arg(QApplication::reverseLayout() ? "rtl" : "ltr");
00401 
00402     if (!article.title().isEmpty())
00403     {
00404         text += QString("<div class=\"headertitle\" dir=\"%1\">\n").arg(directionOf(Utils::stripTags(article.title())));
00405         if (article.link().isValid())
00406             text += "<a href=\""+article.link().url()+"\">";
00407         text += article.title().replace("<", "&lt;").replace(">", "&gt;"); // TODO: better leave things escaped in the parser
00408         if (article.link().isValid())
00409             text += "</a>";
00410         text += "</div>\n";
00411     }
00412     if (article.pubDate().isValid())
00413     {
00414         text += QString("<span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Date")));
00415         text += QString ("%1:").arg(i18n("Date"));
00416         text += "</span><span class=\"headertext\">";
00417         text += KGlobal::locale()->formatDateTime(article.pubDate(), false, false)+"</span>\n"; // TODO: might need RTL?
00418     }
00419     QString author = article.author();
00420     if (!author.isEmpty())
00421     {
00422         text += QString("<br/><span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Author")));
00423         text += QString ("%1:").arg(i18n("Author"));
00424         text += "</span><span class=\"headertext\">";
00425         text += author+"</span>\n"; // TODO: might need RTL?
00426     }
00427     text += "</div>\n"; // end headerbox
00428 
00429     if (feed && !feed->image().isNull())
00430     {
00431         QString url=feed->xmlUrl();
00432         QString file = url.replace("/", "_").replace(":", "_");
00433         KURL u(m_imageDir);
00434         u.setFileName(file);
00435         text += QString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->htmlUrl()).arg(u.url());
00436     }
00437 
00438     
00439 
00440     if (!article.description().isEmpty())
00441     {
00442         text += QString("<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) );
00443         text += "<span class=\"content\">"+article.description()+"</span>";
00444         text += "</div>";
00445     }
00446     
00447     text += "<div class=\"body\">";
00448     
00449     if (article.commentsLink().isValid())
00450     {
00451         text += "<a class=\"contentlink\" href=\"";
00452         text += article.commentsLink().url();
00453         text += "\">" + i18n( "Comments");
00454         if (article.comments())
00455         {
00456             text += " ("+ QString::number(article.comments()) +")";
00457         }
00458         text += "</a>";
00459     }
00460 
00461     if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
00462     {
00463         text += "<p><a class=\"contentlink\" href=\"";
00464         // in case link isn't valid, fall back to the guid permaLink.
00465         if (article.link().isValid())
00466         {
00467             text += article.link().url();
00468         }
00469         else
00470          {
00471             text += article.guid();
00472         }
00473         text += "\">" + i18n( "Complete Story" ) + "</a></p>";
00474     }
00475     text += "</div>";
00476     
00477     if (!article.enclosure().isNull())
00478     {
00479         //QString url = article.enclosure().url();
00480         //QString type = article.enclosure().type();
00481         //int length = article.enclosure().length();
00482         //QString lengthStr = KIO::convertSize(length);
00483 
00484         //text += QString("<hr><div><a href=\"%1\">%2</a> (%3, %4)</div>").arg(url).arg(url).arg(lengthStr).arg(type);
00485     }
00486     //kdDebug() << text << endl;
00487     return text;
00488 
00489 }
00490 
00491 QString ArticleViewer::formatArticleCombinedMode(Feed* feed, const Article& article)
00492 {
00493     QString text;
00494     text = QString("<div class=\"headerbox\" dir=\"%1\">\n").arg(QApplication::reverseLayout() ? "rtl" : "ltr");
00495 
00496     if (!article.title().isEmpty())
00497     {
00498         text += QString("<div class=\"headertitle\" dir=\"%1\">\n").arg(directionOf(Utils::stripTags(article.title())));
00499         if (article.link().isValid())
00500             text += "<a href=\""+article.link().url()+"\">";
00501         text += article.title().replace("<", "&lt;").replace(">", "&gt;"); // TODO: better leave things escaped in the parser
00502         if (article.link().isValid())
00503             text += "</a>";
00504         text += "</div>\n";
00505     }
00506     if (article.pubDate().isValid())
00507     {
00508         text += QString("<span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Date")));
00509         text += QString ("%1:").arg(i18n("Date"));
00510         text += "</span><span class=\"headertext\">";
00511         text += KGlobal::locale()->formatDateTime(article.pubDate(), false, false)+"</span>\n"; // TODO: might need RTL?
00512     }
00513     
00514     QString author = article.author();
00515     if (!author.isEmpty())
00516     {
00517         text += QString("<br/><span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Author")));
00518         text += QString ("%1:").arg(i18n("Author"));
00519         text += "</span><span class=\"headertext\">";
00520         text += author+"</span>\n"; // TODO: might need RTL?
00521     }
00522     
00523     text += "</div>\n"; // end headerbox
00524 
00525     if (feed && !feed->image().isNull())
00526     {
00527         QString url=feed->xmlUrl();
00528         QString file = url.replace("/", "_").replace(":", "_");
00529         KURL u(m_imageDir);
00530         u.setFileName(file);
00531         text += QString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->htmlUrl()).arg(u.url());
00532     }
00533 
00534     
00535 
00536     if (!article.description().isEmpty())
00537     {
00538         text += QString("<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) );
00539         text += "<span class=\"content\">"+article.description()+"</span>";
00540         text += "</div>";
00541     }
00542     
00543     text += "<div class=\"body\">";
00544     
00545     if (article.commentsLink().isValid())
00546     {
00547         text += "<a class=\"contentlink\" href=\"";
00548         text += article.commentsLink().url();
00549         text += "\">" + i18n( "Comments");
00550         if (article.comments())
00551         {
00552             text += " ("+ QString::number(article.comments()) +")";
00553         }
00554         text += "</a>";
00555     }
00556 
00557     if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
00558     {
00559         text += "<p><a class=\"contentlink\" href=\"";
00560         // in case link isn't valid, fall back to the guid permaLink.
00561         if (article.link().isValid())
00562         {
00563             text += article.link().url();
00564         }
00565         else
00566          {
00567             text += article.guid();
00568         }
00569         text += "\">" + i18n( "Complete Story" ) + "</a></p>";
00570     }
00571     text += "</div>";
00572     //kdDebug() << text << endl;
00573     return text;
00574 
00575 }
00576 
00577 void ArticleViewer::renderContent(const QString& text)
00578 {
00579     closeURL();
00580     m_currentText = text;
00581     beginWriting();
00582     //kdDebug() << text << endl;
00583     write(text);
00584     endWriting();
00585 }
00586 
00587 void ArticleViewer::beginWriting()
00588 {
00589     QString head = QString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n <html><head><title>.</title>");
00590     
00591     if (m_viewMode == CombinedView)
00592         head += m_combinedModeCSS;
00593     else
00594         head += m_normalModeCSS; 
00595 
00596     head += "</style></head><body>";
00597     view()->setContentsPos(0,0);
00598     begin(m_link);
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     QValueList<Article> articles = m_node->articles();
00662     qHeapSort(articles);
00663     QValueList<Article>::ConstIterator end = articles.end();
00664     QValueList<Article>::ConstIterator it = articles.begin();
00665 
00666     QString text;
00667 
00668     int num = 0;
00669     QTime 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 QValueList<Article>& /*list*/)
00688 {
00689     if (m_viewMode == CombinedView)
00690         slotUpdateCombinedView();
00691 }
00692 
00693 void ArticleViewer::slotArticlesAdded(TreeNode* /*node*/, const QValueList<Article>& /*list*/)
00694 {
00695 }
00696 
00697 void ArticleViewer::slotArticlesRemoved(TreeNode* /*node*/, const QValueList<Article>& /*list*/)
00698 {
00699 }
00700 
00701 /* testingtesting :)
00702 void ArticleViewer::slotPopupMenu(KXMLGUIClient*, const QPoint& 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(QString());
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(QKeyEvent* e)
00739 {
00740     e->ignore();
00741 }
00742 
00743 void ArticleViewer::urlSelected(const QString &url, int button, int state, const QString& _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, SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotUpdateCombinedView() ) );
00770             connect( node, SIGNAL(signalArticlesAdded(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesAdded(TreeNode*, const QValueList<Article>&)));
00771             connect( node, SIGNAL(signalArticlesRemoved(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesRemoved(TreeNode*, const QValueList<Article>&)));
00772             connect( node, SIGNAL(signalArticlesUpdated(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesUpdated(TreeNode*, const QValueList<Article>&)));
00773         }
00774         if (m_viewMode == SummaryView)
00775             connect( node, SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotShowSummary(TreeNode*) ) );
00776 
00777         connect( node, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotClear() ) );
00778     }   
00779 }
00780 
00781 void ArticleViewer::disconnectFromNode(TreeNode* node)
00782 {
00783     if (node)
00784     {
00785 //        disconnect( node, SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotUpdateCombinedView() ) );
00786         disconnect( node, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotClear() ) );
00787         disconnect( node, SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotShowSummary(TreeNode*) ) );
00788         disconnect( node, SIGNAL(signalArticlesAdded(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesAdded(TreeNode*, const QValueList<Article>&)));
00789         disconnect( node, SIGNAL(signalArticlesRemoved(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesRemoved(TreeNode*, const QValueList<Article>&)));
00790         disconnect( node, SIGNAL(signalArticlesUpdated(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesUpdated(TreeNode*, const QValueList<Article>&)));
00791 
00792     }
00793 }
00794 
00795 }
00796 #include "articleviewer.moc"
00797 
KDE Home | KDE Accessibility Home | Description of Access Keys