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             "@media screen, print {"
00192             "body {\n"
00193             "  font-family: \"%1\" ! important;\n"
00194             "  font-size: %2 ! important;\n"
00195             "  color: %3 ! important;\n"
00196             "  background: %4 ! important;\n"
00197             "}\n\n").arg(Settings::standardFont())
00198             .arg(QString::number(pointsToPixel(Settings::mediumFontSize()))+"px")
00199             .arg(cg.text().name())
00200             .arg(cg.base().name());
00201     m_normalModeCSS += QString(
00202     "a {\n"
00203     + QString("  color: %1 ! important;\n")
00204     + QString(!Settings::underlineLinks() ? " text-decoration: none ! important;\n" : "")
00205     +       "}\n\n"
00206     +".headerbox {\n"
00207     +"  background: %2 ! important;\n"
00208     +"  color: %3 ! important;\n"
00209     +"  border:1px solid #000;\n"
00210     +"  margin-bottom: 10pt;\n"
00211 //    +"  width: 99%;\n"
00212     +        "}\n\n")
00213             .arg(cg.link().name())
00214             .arg(cg.background().name())
00215             .arg(cg.text().name());
00216 
00217     m_normalModeCSS += QString(".headertitle a:link { color: %1  ! important; }\n"
00218     ".headertitle a:visited { color: %2 ! important; }\n"
00219     ".headertitle a:hover{ color: %3 ! important; }\n"
00220             ".headertitle a:active { color: %4 ! important; }\n")
00221             .arg(cg.highlightedText().name())
00222             .arg(cg.highlightedText().name())
00223             .arg(cg.highlightedText().name())
00224             .arg(cg.highlightedText().name());
00225     
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     "iframe {display: none !important; }\n"
00257     "frame {display: none !important; }\n"
00258     "frameset {display: none !important; }\n"
00259     "object {display: none !important; }\n"
00260     "applet {display: none !important; }\n"
00261     "}\n\n"); // @media screen, print    
00262 }
00263 
00264 void ArticleViewer::generateCombinedModeCSS()
00265 {
00266     const QColorGroup & cg = QApplication::palette().active();
00267     
00268     // from kmail::headerstyle.cpp
00269     m_combinedModeCSS = QString (
00270 //            "<style type=\"text/css\">\n"
00271             "@media screen, print {"
00272             "body {\n"
00273             "  font-family: \"%1\" ! important;\n"
00274             "  font-size: %2 ! important;\n"
00275             "  color: %3 ! important;\n"
00276             "  background: %4 ! important;\n"
00277             "}\n\n").arg(Settings::standardFont())
00278             .arg(QString::number(pointsToPixel(Settings::mediumFontSize()))+"px")
00279             .arg(cg.text().name())
00280             .arg(cg.base().name());
00281     m_combinedModeCSS += (
00282     "a {\n"
00283     + QString("  color: %1 ! important;\n")
00284     + QString(!Settings::underlineLinks() ? " text-decoration: none ! important;\n" : "")
00285     +       "}\n\n"
00286     +".headerbox {\n"
00287     +"  background: %2 ! important;\n"
00288     +"  color: %3 ! important;\n"
00289     +"  border:1px solid #000;\n"
00290     +"  margin-bottom: 10pt;\n"
00291 //    +"  width: 99%;\n"
00292     +        "}\n\n")
00293             .arg(cg.link().name())
00294             .arg(cg.background().name())
00295             .arg(cg.text().name());
00296 
00297     m_combinedModeCSS += QString(".headertitle a:link { color: %1  ! important; }\n"
00298     ".headertitle a:visited { color: %2 ! important; }\n"
00299     ".headertitle a:hover{ color: %3 ! important; }\n"
00300             ".headertitle a:active { color: %4 ! important; }\n")
00301             .arg(cg.highlightedText().name())
00302             .arg(cg.highlightedText().name())
00303             .arg(cg.highlightedText().name())
00304             .arg(cg.highlightedText().name());
00305     m_combinedModeCSS += QString(
00306     ".headertitle {\n"
00307     "  background: %1 ! important;\n"
00308     "  padding:2px;\n"
00309     "  color: %2 ! important;\n"
00310     "  font-weight: bold;\n"
00311     "}\n\n"
00312     ".header {\n"
00313     "  font-weight: bold;\n"
00314     "  padding:2px;\n"
00315     "  margin-right: 5px;\n"
00316     "}\n\n"
00317     ".headertext {\n"
00318     "}\n\n"
00319     ".headimage {\n"
00320     "  float: right;\n"
00321     "  margin-left: 5px;\n"
00322             "}\n\n").arg(cg.highlight().name())
00323             .arg(cg.highlightedText().name());
00324     
00325     m_combinedModeCSS += QString(
00326     "body { clear: none; }\n\n"
00327     ".content {\n"
00328     "  display: block;\n"
00329     "  margin-bottom: 6px;\n"
00330             "}\n\n"
00331     // these rules make sure that there is no leading space between the header and the first of the text
00332     ".content > P:first-child {\n margin-top: 1px; }\n"
00333     ".content > DIV:first-child {\n margin-top: 1px; }\n"
00334     ".content > BR:first-child {\n display: none;  }\n"
00335     "iframe {display: none !important; }\n"
00336     "frame {display: none !important; }\n"
00337     "frameset {display: none !important; }\n"
00338     "object {display: none !important; }\n"
00339     "applet {display: none !important; }\n"
00340     "}\n\n"); // @media screen, print
00341 }
00342 
00343 void ArticleViewer::reload()
00344 {
00345     beginWriting();
00346     write(m_currentText);
00347     endWriting();
00348 }
00349 
00350 bool ArticleViewer::openURL(const KURL& url)
00351 {
00352     if (!m_article.isNull() && m_article.feed()->loadLinkedWebsite())
00353     {
00354         return Viewer::openURL(url);
00355     }
00356     else
00357     {
00358         reload();
00359         return true;
00360     }
00361 }
00362 
00363 void ArticleViewer::displayAboutPage()
00364 {
00365     QString location = locate("data", "akregator/about/main.html");
00366     QString content = KPIM::kFileToString(location);
00367     content = content.arg( locate( "data", "libkdepim/about/kde_infopage.css" ) );
00368     if ( kapp->reverseLayout() )
00369         content = content.arg( "@import \"%1\";" ).arg( locate( "data", "libkdepim/about/kde_infopage_rtl.css" ) );
00370     else
00371         content = content.arg( "" );
00372 
00373     begin(KURL( location ));
00374     QString info =
00375             i18n("%1: Akregator version; %2: help:// URL; %3: homepage URL; "
00376             "--- end of comment ---",
00377     "<h2 style='margin-top: 0px;'>Welcome to Akregator %1</h2>"
00378             "<p>Akregator is an RSS feed aggregator for the K Desktop Environment. "
00379             "Feed aggregators provide a convenient way to browse different kinds of "
00380             "content, including news, blogs, and other content from online sites. "
00381             "Instead of checking all your favorite web sites manually for updates, "
00382             "Akregator collects the content for you.</p>"
00383             "<p>For more information about using Akregator, check the "
00384             "<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>"
00385             "<p>We hope that you will enjoy Akregator.</p>\n"
00386             "<p>Thank you,</p>\n"
00387             "<p style='margin-bottom: 0px'>&nbsp; &nbsp; The Akregator Team</p>\n")
00388             .arg(AKREGATOR_VERSION) // Akregator version
00389             .arg("http://akregator.kde.org/"); // Akregator homepage URL
00390 
00391     QString fontSize = QString::number( pointsToPixel( Settings::mediumFontSize() ));
00392     QString appTitle = i18n("Akregator");
00393     QString catchPhrase = ""; //not enough space for a catch phrase at default window size i18n("Part of the Kontact Suite");
00394     QString quickDescription = i18n("An RSS feed reader for the K Desktop Environment.");
00395     write(content.arg(fontSize).arg(appTitle).arg(catchPhrase).arg(quickDescription).arg(info));
00396     end();
00397 }
00398 
00399 QString ArticleViewer::formatArticleNormalMode(Feed* feed, const Article& article)
00400 {
00401     QString text;
00402     text = QString("<div class=\"headerbox\" dir=\"%1\">\n").arg(QApplication::reverseLayout() ? "rtl" : "ltr");
00403 
00404     if (!article.title().isEmpty())
00405     {
00406         text += QString("<div class=\"headertitle\" dir=\"%1\">\n").arg(directionOf(Utils::stripTags(article.title())));
00407         if (article.link().isValid())
00408             text += "<a href=\""+article.link().url()+"\">";
00409         text += article.title().replace("<", "&lt;").replace(">", "&gt;"); // TODO: better leave things escaped in the parser
00410         if (article.link().isValid())
00411             text += "</a>";
00412         text += "</div>\n";
00413     }
00414     if (article.pubDate().isValid())
00415     {
00416         text += QString("<span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Date")));
00417         text += QString ("%1:").arg(i18n("Date"));
00418         text += "</span><span class=\"headertext\">";
00419         text += KGlobal::locale()->formatDateTime(article.pubDate(), false, false)+"</span>\n"; // TODO: might need RTL?
00420     }
00421     QString author = article.author();
00422     if (!author.isEmpty())
00423     {
00424         text += QString("<br/><span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Author")));
00425         text += QString ("%1:").arg(i18n("Author"));
00426         text += "</span><span class=\"headertext\">";
00427         text += author+"</span>\n"; // TODO: might need RTL?
00428     }
00429     text += "</div>\n"; // end headerbox
00430 
00431     if (feed && !feed->image().isNull())
00432     {
00433         QString file = Utils::fileNameForUrl(feed->xmlUrl());
00434         KURL u(m_imageDir);
00435         u.setFileName(file);
00436         text += QString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->htmlUrl()).arg(u.url());
00437     }
00438 
00439     
00440 
00441     if (!article.description().isEmpty())
00442     {
00443         text += QString("<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) );
00444         text += "<span class=\"content\">"+article.description()+"</span>";
00445         text += "</div>";
00446     }
00447     
00448     text += "<div class=\"body\">";
00449     
00450     if (article.commentsLink().isValid())
00451     {
00452         text += "<a class=\"contentlink\" href=\"";
00453         text += article.commentsLink().url();
00454         text += "\">" + i18n( "Comments");
00455         if (article.comments())
00456         {
00457             text += " ("+ QString::number(article.comments()) +")";
00458         }
00459         text += "</a>";
00460     }
00461 
00462     if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
00463     {
00464         text += "<p><a class=\"contentlink\" href=\"";
00465         // in case link isn't valid, fall back to the guid permaLink.
00466         if (article.link().isValid())
00467         {
00468             text += article.link().url();
00469         }
00470         else
00471          {
00472             text += article.guid();
00473         }
00474         text += "\">" + i18n( "Complete Story" ) + "</a></p>";
00475     }
00476     text += "</div>";
00477     
00478     if (!article.enclosure().isNull())
00479     {
00480         //QString url = article.enclosure().url();
00481         //QString type = article.enclosure().type();
00482         //int length = article.enclosure().length();
00483         //QString lengthStr = KIO::convertSize(length);
00484 
00485         //text += QString("<hr><div><a href=\"%1\">%2</a> (%3, %4)</div>").arg(url).arg(url).arg(lengthStr).arg(type);
00486     }
00487     //kdDebug() << text << endl;
00488     return text;
00489 
00490 }
00491 
00492 QString ArticleViewer::formatArticleCombinedMode(Feed* feed, const Article& article)
00493 {
00494     QString text;
00495     text = QString("<div class=\"headerbox\" dir=\"%1\">\n").arg(QApplication::reverseLayout() ? "rtl" : "ltr");
00496 
00497     KURL link = article.link();
00498     
00499     if (!article.title().isEmpty())
00500     {
00501         text += QString("<div class=\"headertitle\" dir=\"%1\">\n").arg(directionOf(Utils::stripTags(article.title())));
00502         if (link.isValid())
00503             text += "<a href=\""+link.url()+"\">";
00504         text += article.title().replace("<", "&lt;").replace(">", "&gt;"); // TODO: better leave things escaped in the parser
00505         if (link.isValid())
00506             text += "</a>";
00507         text += "</div>\n";
00508     }
00509     if (article.pubDate().isValid())
00510     {
00511         text += QString("<span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Date")));
00512         text += QString ("%1:").arg(i18n("Date"));
00513         text += "</span><span class=\"headertext\">";
00514         text += KGlobal::locale()->formatDateTime(article.pubDate(), false, false)+"</span>\n"; // TODO: might need RTL?
00515     }
00516     
00517     QString author = article.author();
00518     if (!author.isEmpty())
00519     {
00520         text += QString("<br/><span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Author")));
00521         text += QString ("%1:").arg(i18n("Author"));
00522         text += "</span><span class=\"headertext\">";
00523         text += author+"</span>\n"; // TODO: might need RTL?
00524     }
00525     
00526     text += "</div>\n"; // end headerbox
00527 
00528     if (feed && !feed->image().isNull())
00529     {
00530         QString file = Utils::fileNameForUrl(feed->xmlUrl());
00531         KURL u(m_imageDir);
00532         u.setFileName(file);
00533         text += QString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->htmlUrl()).arg(u.url());
00534     }
00535 
00536     
00537 
00538     if (!article.description().isEmpty())
00539     {
00540         text += QString("<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) );
00541         text += "<span class=\"content\">"+article.description()+"</span>";
00542         text += "</div>";
00543     }
00544     
00545     text += "<div class=\"body\">";
00546     
00547     if (article.commentsLink().isValid())
00548     {
00549         text += "<a class=\"contentlink\" href=\"";
00550         text += article.commentsLink().url();
00551         text += "\">" + i18n( "Comments");
00552         if (article.comments())
00553         {
00554             text += " ("+ QString::number(article.comments()) +")";
00555         }
00556         text += "</a>";
00557     }
00558 
00559     if (link.isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
00560     {
00561         text += "<p><a class=\"contentlink\" href=\"";
00562         // in case link isn't valid, fall back to the guid permaLink.
00563         if (link.isValid())
00564         {
00565             text += link.url();
00566         }
00567         else
00568          {
00569             text += article.guid();
00570         }
00571         text += "\">" + i18n( "Complete Story" ) + "</a></p>";
00572     }
00573     text += "</div>";
00574     //kdDebug() << text << endl;
00575     return text;
00576 
00577 }
00578 
00579 void ArticleViewer::renderContent(const QString& text)
00580 {
00581     closeURL();
00582     m_currentText = text;
00583     beginWriting();
00584     //kdDebug() << text << endl;
00585     write(text);
00586     endWriting();
00587 }
00588 
00589 void ArticleViewer::beginWriting()
00590 {
00591     QString head = QString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n <html><head><title>.</title></head>");
00592     view()->setContentsPos(0,0);
00593     begin(m_link);
00594     setUserStyleSheet(m_viewMode == CombinedView ? m_combinedModeCSS : m_normalModeCSS);
00595     write(head);
00596 }
00597 
00598 void ArticleViewer::endWriting()
00599 {
00600     write(m_htmlFooter);
00601     //kdDebug() << m_htmlFooter << endl;
00602     end();
00603 }
00604 
00605 void ArticleViewer::slotShowSummary(TreeNode* node)
00606 {
00607     m_viewMode = SummaryView;
00608 
00609     if (!node)
00610     {
00611         slotClear();
00612         return;
00613     }
00614 
00615     if (node != m_node)
00616     {
00617         disconnectFromNode(m_node);
00618         connectToNode(node);
00619         m_node = node;
00620     }
00621     m_showSummaryVisitor->visit(node);
00622 }
00623 
00624 
00625 void ArticleViewer::slotShowArticle(const Article& article)
00626 {
00627     m_viewMode = NormalView;
00628     disconnectFromNode(m_node);
00629     m_article = article;
00630     m_node = 0;
00631     m_link = article.link();
00632     if (article.feed()->loadLinkedWebsite())
00633         openURL(article.link());
00634     else
00635         renderContent( formatArticleNormalMode(article.feed(), article) );
00636 }
00637 
00638 void ArticleViewer::slotSetFilter(const Akregator::Filters::ArticleMatcher& textFilter, const Akregator::Filters::ArticleMatcher& statusFilter)
00639 {
00640     if (m_statusFilter == statusFilter && m_textFilter == textFilter)
00641         return;
00642 
00643     m_textFilter = textFilter;
00644     m_statusFilter = statusFilter;
00645 
00646     slotUpdateCombinedView();
00647 }
00648 
00649 void ArticleViewer::slotUpdateCombinedView()
00650 {
00651     if (m_viewMode != CombinedView)
00652         return;
00653 
00654     if (!m_node)
00655         return slotClear();
00656 
00657     QValueList<Article> articles = m_node->articles();
00658     qHeapSort(articles);
00659     QValueList<Article>::ConstIterator end = articles.end();
00660     QValueList<Article>::ConstIterator it = articles.begin();
00661 
00662     QString text;
00663 
00664     int num = 0;
00665     QTime spent;
00666     spent.start();
00667     
00668     for ( ; it != end; ++it)
00669     {
00670         if ( !(*it).isDeleted() && m_textFilter.matches(*it) && m_statusFilter.matches(*it) )
00671         {
00672             text += "<p><div class=\"article\">"+formatArticleCombinedMode(0, *it)+"</div><p>";
00673             ++num;
00674         }
00675     }
00676     //kdDebug() << "Combined view rendering: (" << num << " articles):\n" << "generating HTML: " << spent.elapsed() << "ms " << endl;
00677     renderContent(text);
00678     //kdDebug() << "HTML rendering: " << spent.elapsed() << "ms" << endl;
00679 
00680 
00681 }
00682 
00683 void ArticleViewer::slotArticlesUpdated(TreeNode* /*node*/, const QValueList<Article>& /*list*/)
00684 {
00685     if (m_viewMode == CombinedView)
00686         slotUpdateCombinedView();
00687 }
00688 
00689 void ArticleViewer::slotArticlesAdded(TreeNode* /*node*/, const QValueList<Article>& /*list*/)
00690 {
00691 }
00692 
00693 void ArticleViewer::slotArticlesRemoved(TreeNode* /*node*/, const QValueList<Article>& /*list*/)
00694 {
00695 }
00696 
00697 /* testingtesting :)
00698 void ArticleViewer::slotPopupMenu(KXMLGUIClient*, const QPoint& p, const KURL& kurl, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags, mode_t)
00699 {
00700     kdDebug() << m_link << endl;
00701     kdDebug() << kurl.url() << endl;
00702 }*/
00703 
00704 
00705 void ArticleViewer::slotClear()
00706 {
00707     disconnectFromNode(m_node);
00708     m_node = 0;
00709     m_article = Article();
00710 
00711     renderContent(QString());
00712 }
00713 
00714 void ArticleViewer::slotShowNode(TreeNode* node)
00715 {
00716     m_viewMode = CombinedView;
00717 
00718     if (node != m_node)
00719         disconnectFromNode(m_node);
00720 
00721     connectToNode(node);
00722     
00723     m_article = Article();
00724     m_node = node;
00725 
00726     if (node && !node->articles().isEmpty())
00727         m_link = node->articles().first().link();
00728     else
00729         m_link = KURL();
00730 
00731     slotUpdateCombinedView();
00732 }
00733 
00734 void ArticleViewer::keyPressEvent(QKeyEvent* e)
00735 {
00736     e->ignore();
00737 }
00738 
00739 void ArticleViewer::urlSelected(const QString &url, int button, int state, const QString& _target, KParts::URLArgs args)
00740 {
00741     if(url == "config:/disable_introduction") {
00742         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) {
00743             KConfig *conf = Settings::self()->config();
00744             conf->setGroup("General");
00745             conf->writeEntry("Disable Introduction", "true");
00746         }
00747     }
00748     else
00749         Viewer::urlSelected(url, button, state, _target, args);
00750 }
00751 
00752 void ArticleViewer::slotPaletteOrFontChanged()
00753 {
00754     generateNormalModeCSS();
00755     generateCombinedModeCSS();
00756     reload();
00757 }
00758 
00759 void ArticleViewer::connectToNode(TreeNode* node)
00760 {
00761     if (node)
00762     {
00763         if (m_viewMode == CombinedView)
00764         {
00765 //            connect( node, SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotUpdateCombinedView() ) );
00766             connect( node, SIGNAL(signalArticlesAdded(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesAdded(TreeNode*, const QValueList<Article>&)));
00767             connect( node, SIGNAL(signalArticlesRemoved(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesRemoved(TreeNode*, const QValueList<Article>&)));
00768             connect( node, SIGNAL(signalArticlesUpdated(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesUpdated(TreeNode*, const QValueList<Article>&)));
00769         }
00770         if (m_viewMode == SummaryView)
00771             connect( node, SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotShowSummary(TreeNode*) ) );
00772 
00773         connect( node, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotClear() ) );
00774     }   
00775 }
00776 
00777 void ArticleViewer::disconnectFromNode(TreeNode* node)
00778 {
00779     if (node)
00780     {
00781 //        disconnect( node, SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotUpdateCombinedView() ) );
00782         disconnect( node, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotClear() ) );
00783         disconnect( node, SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotShowSummary(TreeNode*) ) );
00784         disconnect( node, SIGNAL(signalArticlesAdded(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesAdded(TreeNode*, const QValueList<Article>&)));
00785         disconnect( node, SIGNAL(signalArticlesRemoved(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesRemoved(TreeNode*, const QValueList<Article>&)));
00786         disconnect( node, SIGNAL(signalArticlesUpdated(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesUpdated(TreeNode*, const QValueList<Article>&)));
00787 
00788     }
00789 }
00790 
00791 }
00792 #include "articleviewer.moc"
00793 
KDE Home | KDE Accessibility Home | Description of Access Keys