akregator/src

akregator_view.cpp

00001 /*
00002     This file is part of Akregator.
00003 
00004     Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net>
00005                   2004 Sashmit Bhaduri <smt@vfemail.net>
00006                   2005 Frank Osterfeld <frank.osterfeld at kdemail.net>
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00021 
00022     As a special exception, permission is given to link this program
00023     with any edition of Qt, and distribute the resulting executable,
00024     without including the source code for Qt in the source distribution.
00025 */
00026 
00027 #include "actionmanagerimpl.h"
00028 #include "akregator_part.h"
00029 #include "akregator_run.h"
00030 #include "akregator_view.h"
00031 #include "listtabwidget.h"
00032 #include "addfeeddialog.h"
00033 #include "propertiesdialog.h"
00034 #include "frame.h"
00035 #include "fetchqueue.h"
00036 #include "feedlistview.h"
00037 #include "articlelistview.h"
00038 #include "articleviewer.h"
00039 #include "viewer.h"
00040 #include "feed.h"
00041 #include "tagfolder.h"
00042 #include "folder.h"
00043 #include "feedlist.h"
00044 #include "akregatorconfig.h"
00045 #include "kernel.h"
00046 #include "pageviewer.h"
00047 #include "searchbar.h"
00048 #include "speechclient.h"
00049 #include "storage.h"
00050 #include "tabwidget.h"
00051 #include "tag.h"
00052 #include "tagset.h"
00053 #include "tagnode.h"
00054 #include "tagnodelist.h"
00055 #include "tagpropertiesdialog.h"
00056 #include "treenode.h"
00057 #include "progressmanager.h"
00058 #include "treenodevisitor.h"
00059 #include "notificationmanager.h"
00060 
00061 #include <kaction.h>
00062 #include <kapplication.h>
00063 #include <kcharsets.h>
00064 #include <kcombobox.h>
00065 #include <kconfig.h>
00066 #include <kdebug.h>
00067 #include <kdialog.h>
00068 #include <kfiledialog.h>
00069 #include <kfileitem.h>
00070 #include <khtml_part.h>
00071 #include <khtmlview.h>
00072 #include <kiconloader.h>
00073 #include <kinputdialog.h>
00074 #include <klineedit.h>
00075 #include <klistview.h>
00076 #include <klocale.h>
00077 #include <kmessagebox.h>
00078 #include <kpassdlg.h>
00079 #include <kprocess.h>
00080 #include <krun.h>
00081 #include <kshell.h>
00082 #include <kstandarddirs.h>
00083 #include <kurl.h>
00084 #include <kxmlguifactory.h>
00085 #include <kparts/partmanager.h>
00086 
00087 #include <qbuttongroup.h>
00088 #include <qcheckbox.h>
00089 #include <qdatetime.h> // for startup time measure
00090 #include <qfile.h>
00091 #include <qhbox.h>
00092 #include <qlabel.h>
00093 #include <qlayout.h>
00094 #include <qmultilineedit.h>
00095 #include <qpopupmenu.h>
00096 #include <qstylesheet.h>
00097 #include <qtextstream.h>
00098 #include <qtimer.h>
00099 #include <qtoolbutton.h>
00100 #include <qtooltip.h>
00101 #include <qvaluevector.h>
00102 #include <qwhatsthis.h>
00103 #include <qclipboard.h>
00104 
00105 namespace Akregator {
00106 
00107 class View::EditNodePropertiesVisitor : public TreeNodeVisitor
00108 {
00109     public:
00110         EditNodePropertiesVisitor(View* view) : m_view(view) {}
00111 
00112         virtual bool visitTagNode(TagNode* node)
00113         {
00114             TagPropertiesDialog* dlg = new TagPropertiesDialog(m_view);
00115             dlg->setTag(node->tag());
00116             dlg->exec();
00117             delete dlg;
00118             return true;
00119         }
00120 
00121         virtual bool visitFolder(Folder* node)
00122         {
00123             m_view->m_listTabWidget->activeView()->startNodeRenaming(node);
00124             return true;
00125         }
00126 
00127         virtual bool visitFeed(Feed* node)
00128         {
00129             FeedPropertiesDialog *dlg = new FeedPropertiesDialog( m_view, "edit_feed" );
00130             dlg->setFeed(node);
00131             dlg->exec();
00132             delete dlg;
00133             return true;
00134         }
00135     private:
00136 
00137         View* m_view;
00138 };
00139 
00140 class View::DeleteNodeVisitor : public TreeNodeVisitor
00141 {
00142     public:
00143         DeleteNodeVisitor(View* view) : m_view(view) {}
00144 
00145         virtual bool visitTagNode(TagNode* node)
00146         {
00147             QString msg = i18n("<qt>Are you sure you want to delete tag <b>%1</b>? The tag will be removed from all articles.</qt>").arg(node->title());
00148             if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Tag"), KStdGuiItem::del()) == KMessageBox::Continue)
00149             {
00150                 Tag tag = node->tag();
00151                 QValueList<Article> articles = m_view->m_feedList->rootNode()->articles(tag.id());
00152                 node->setNotificationMode(false);
00153                 for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
00154                     (*it).removeTag(tag.id());
00155                 node->setNotificationMode(true);
00156                 Kernel::self()->tagSet()->remove(tag);
00157                 m_view->m_listTabWidget->activeView()->setFocus();
00158             }
00159             return true;
00160         }
00161 
00162         virtual bool visitFolder(Folder* node)
00163         {
00164             QString msg;
00165             if (node->title().isEmpty())
00166                 msg = i18n("<qt>Are you sure you want to delete this folder and its feeds and subfolders?</qt>");
00167             else
00168                 msg = i18n("<qt>Are you sure you want to delete folder <b>%1</b> and its feeds and subfolders?</qt>").arg(node->title());
00169 
00170             if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Folder"), KStdGuiItem::del()) == KMessageBox::Continue)
00171             {
00172                 delete node;
00173                 m_view->m_listTabWidget->activeView()->setFocus();
00174             }
00175             return true;
00176         }
00177 
00178         virtual bool visitFeed(Feed* node)
00179         {
00180             QString msg;
00181             if (node->title().isEmpty())
00182                 msg = i18n("<qt>Are you sure you want to delete this feed?</qt>");
00183             else
00184                 msg = i18n("<qt>Are you sure you want to delete feed <b>%1</b>?</qt>").arg(node->title());
00185 
00186             if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Feed"), KStdGuiItem::del()) == KMessageBox::Continue)
00187             {
00188                 delete node;
00189                 m_view->m_listTabWidget->activeView()->setFocus();
00190             }
00191             return true;
00192         }
00193     private:
00194 
00195         View* m_view;
00196 };
00197 
00198 
00199 View::~View()
00200 {
00201     // if m_shuttingDown is false, slotOnShutdown was not called. That
00202      // means that not the whole app is shutdown, only the part. So it
00203     // should be no risk to do the cleanups now
00204     if (!m_shuttingDown)
00205     {
00206         kdDebug() << "View::~View(): slotOnShutdown() wasn't called. Calling it now." << endl;
00207         slotOnShutdown();
00208     }
00209     kdDebug() << "View::~View(): leaving" << endl;
00210 }
00211 
00212 View::View( Part *part, QWidget *parent, ActionManagerImpl* actionManager, const char *name)
00213  : QWidget(parent, name), m_viewMode(NormalView), m_actionManager(actionManager)
00214 {
00215     m_editNodePropertiesVisitor = new EditNodePropertiesVisitor(this);
00216     m_deleteNodeVisitor = new DeleteNodeVisitor(this);
00217     m_keepFlagIcon = QPixmap(locate("data", "akregator/pics/akregator_flag.png"));
00218     m_part = part;
00219     m_feedList = new FeedList();
00220     m_tagNodeList = new TagNodeList(m_feedList, Kernel::self()->tagSet());
00221     m_shuttingDown = false;
00222     m_displayingAboutPage = false;
00223     m_currentFrame = 0L;
00224     setFocusPolicy(QWidget::StrongFocus);
00225 
00226     QVBoxLayout *lt = new QVBoxLayout( this );
00227     
00228     m_horizontalSplitter = new QSplitter(QSplitter::Horizontal, this);
00229 
00230     m_horizontalSplitter->setOpaqueResize(true);
00231     lt->addWidget(m_horizontalSplitter);
00232 
00233     connect (Kernel::self()->fetchQueue(), SIGNAL(fetched(Feed*)), this, SLOT(slotFeedFetched(Feed*)));
00234     connect (Kernel::self()->fetchQueue(), SIGNAL(signalStarted()), this, SLOT(slotFetchingStarted()));
00235     connect (Kernel::self()->fetchQueue(), SIGNAL(signalStopped()), this, SLOT(slotFetchingStopped()));
00236 
00237     connect(Kernel::self()->tagSet(), SIGNAL(signalTagAdded(const Tag&)), this, SLOT(slotTagCreated(const Tag&)));
00238     connect(Kernel::self()->tagSet(), SIGNAL(signalTagRemoved(const Tag&)), this, SLOT(slotTagRemoved(const Tag&)));
00239 
00240     m_listTabWidget = new ListTabWidget(m_horizontalSplitter);
00241     m_actionManager->initListTabWidget(m_listTabWidget);
00242 
00243     connect(m_listTabWidget, SIGNAL(signalNodeSelected(TreeNode*)), this, SLOT(slotNodeSelected(TreeNode*)));
00244 
00245     if (!Settings::showTaggingGUI())
00246         m_listTabWidget->setViewMode(ListTabWidget::single);
00247 
00248     m_feedListView = new NodeListView( this, "feedtree" );
00249     m_listTabWidget->addView(m_feedListView, i18n("Feeds"), KGlobal::iconLoader()->loadIcon("folder", KIcon::Small));
00250 
00251     connect(m_feedListView, SIGNAL(signalContextMenu(KListView*, TreeNode*, const QPoint&)), this, SLOT(slotFeedTreeContextMenu(KListView*, TreeNode*, const QPoint&)));
00252 
00253     connect(m_feedListView, SIGNAL(signalDropped (KURL::List &, TreeNode*,
00254             Folder*)), this, SLOT(slotFeedURLDropped (KURL::List &,
00255             TreeNode*, Folder*)));
00256 
00257     m_tagNodeListView = new NodeListView(this);
00258     m_listTabWidget->addView(m_tagNodeListView, i18n("Tags"), KGlobal::iconLoader()->loadIcon("rss_tag", KIcon::Small));
00259 
00260     connect(m_tagNodeListView, SIGNAL(signalContextMenu(KListView*, TreeNode*, const QPoint&)), this, SLOT(slotFeedTreeContextMenu(KListView*, TreeNode*, const QPoint&)));
00261 
00262     
00263     ProgressManager::self()->setFeedList(m_feedList);
00264 
00265     m_tabs = new TabWidget(m_horizontalSplitter);
00266     m_actionManager->initTabWidget(m_tabs);
00267 
00268     connect( m_part, SIGNAL(signalSettingsChanged()), m_tabs, SLOT(slotSettingsChanged()));
00269 
00270     connect( m_tabs, SIGNAL( currentFrameChanged(Frame *) ), this,
00271             SLOT( slotFrameChanged(Frame *) ) );
00272 
00273     QWhatsThis::add(m_tabs, i18n("You can view multiple articles in several open tabs."));
00274 
00275     m_mainTab = new QWidget(this, "Article Tab");
00276     QVBoxLayout *mainTabLayout = new QVBoxLayout( m_mainTab, 0, 2, "mainTabLayout");
00277 
00278     QWhatsThis::add(m_mainTab, i18n("Articles list."));
00279 
00280     m_searchBar = new SearchBar(m_mainTab);
00281 
00282     if ( !Settings::showQuickFilter() )
00283         m_searchBar->hide();
00284 
00285     mainTabLayout->addWidget(m_searchBar);
00286 
00287     m_articleSplitter = new QSplitter(QSplitter::Vertical, m_mainTab, "panner2");
00288 
00289     m_articleList = new ArticleListView( m_articleSplitter, "articles" );
00290     m_actionManager->initArticleListView(m_articleList);
00291 
00292     connect( m_articleList, SIGNAL(signalMouseButtonPressed(int, const Article&, const QPoint &, int)), this, SLOT(slotMouseButtonPressed(int, const Article&, const QPoint &, int)));
00293 
00294     // use selectionChanged instead of clicked
00295     connect( m_articleList, SIGNAL(signalArticleChosen(const Article&)),
00296                 this, SLOT( slotArticleSelected(const Article&)) );
00297     connect( m_articleList, SIGNAL(signalDoubleClicked(const Article&, const QPoint&, int)),
00298                 this, SLOT( slotOpenArticleExternal(const Article&, const QPoint&, int)) );
00299 
00300     m_articleViewer = new ArticleViewer(m_articleSplitter, "article_viewer");
00301     m_articleViewer->setSafeMode();  // disable JS, Java, etc...
00302 
00303     m_actionManager->initArticleViewer(m_articleViewer);
00304 
00305     connect(m_searchBar, SIGNAL(signalSearch(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&)), m_articleList, SLOT(slotSetFilter(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&)));
00306 
00307     connect(m_searchBar, SIGNAL(signalSearch(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&)), m_articleViewer, SLOT(slotSetFilter(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&)));
00308 
00309     connect( m_articleViewer, SIGNAL(urlClicked(const KURL&, Viewer*, bool, bool)),
00310              this, SLOT(slotUrlClickedInViewer(const KURL&, Viewer*, bool, bool)) );
00311 
00312     connect( m_articleViewer->browserExtension(), SIGNAL(mouseOverInfo(const KFileItem *)),
00313                                             this, SLOT(slotMouseOverInfo(const KFileItem *)) );
00314 
00315     connect( m_part, SIGNAL(signalSettingsChanged()), m_articleViewer, SLOT(slotPaletteOrFontChanged()));
00316     QWhatsThis::add(m_articleViewer->widget(), i18n("Browsing area."));
00317     mainTabLayout->addWidget( m_articleSplitter );
00318 
00319     m_mainFrame=new Frame(this, m_part, m_mainTab, i18n("Articles"), false);
00320     connectFrame(m_mainFrame);
00321     m_tabs->addFrame(m_mainFrame);
00322 
00323     m_horizontalSplitter->setSizes( Settings::splitter1Sizes() );
00324     m_articleSplitter->setSizes( Settings::splitter2Sizes() );
00325 
00326     KConfig *conf = Settings::self()->config();
00327     conf->setGroup("General");
00328     if(!conf->readBoolEntry("Disable Introduction", false))
00329     {
00330         m_articleList->hide();
00331         m_searchBar->hide();
00332         m_articleViewer->displayAboutPage();
00333         m_mainFrame->setTitle(i18n("About"));
00334         m_displayingAboutPage = true;
00335     }
00336 
00337     m_fetchTimer = new QTimer(this);
00338     connect( m_fetchTimer, SIGNAL(timeout()), this, SLOT(slotDoIntervalFetches()) );
00339     m_fetchTimer->start(1000*60);
00340 
00341     // delete expired articles once per hour
00342     m_expiryTimer = new QTimer(this);
00343     connect(m_expiryTimer, SIGNAL(timeout()), this,
00344             SLOT(slotDeleteExpiredArticles()) );
00345     m_expiryTimer->start(3600*1000);
00346 
00347     m_markReadTimer = new QTimer(this);
00348     connect(m_markReadTimer, SIGNAL(timeout()), this, SLOT(slotSetCurrentArticleReadDelayed()) );
00349 
00350     switch (Settings::viewMode())
00351     {
00352         case CombinedView:
00353             slotCombinedView();
00354             break;
00355         case WidescreenView:
00356             slotWidescreenView();
00357             break;
00358         default:
00359             slotNormalView();
00360     }
00361 
00362     if (!Settings::resetQuickFilterOnNodeChange())
00363     {
00364         m_searchBar->slotSetStatus(Settings::statusFilter());
00365         m_searchBar->slotSetText(Settings::textFilter());
00366     }
00367 
00368     QTimer::singleShot(1000, this, SLOT(slotDeleteExpiredArticles()) );
00369     QTimer::singleShot(0, this, SLOT(delayedInit()));
00370 }
00371 
00372 void View::delayedInit()
00373 {
00374     // HACK, FIXME:
00375     // for some reason, m_part->factory() is NULL at startup of kontact,
00376     // and thus the article viewer GUI can't be merged when creating the view.
00377     // Even the delayed init didn't help. Well, we retry every half a second until
00378     // it works. This is kind of creative, but a dirty hack nevertheless.
00379     if ( !m_part->mergePart(m_articleViewer) )
00380         QTimer::singleShot(500, this, SLOT(delayedInit()));
00381 }
00382 
00383 void View::slotSettingsChanged()
00384 {
00385     // if tagging is hidden, show only feed list
00386     m_listTabWidget->setViewMode(Settings::showTaggingGUI() ? ListTabWidget::verticalTabs : ListTabWidget::single);
00387     
00388 }
00389 
00390 void View::slotOnShutdown()
00391 {
00392     m_shuttingDown = true; // prevents slotFrameChanged from crashing
00393 
00394     m_articleList->slotShowNode(0);
00395     m_articleViewer->slotShowNode(0);
00396 
00397     Kernel::self()->fetchQueue()->slotAbort();
00398 
00399     m_feedListView->setNodeList(0);
00400     ProgressManager::self()->setFeedList(0);
00401 
00402     delete m_feedList;
00403     delete m_tagNodeList;
00404 
00405     // close all pageviewers in a controlled way
00406     // fixes bug 91660, at least when no part loading data
00407     m_tabs->setCurrentPage(m_tabs->count()-1); // select last page
00408     while (m_tabs->count() > 1) // remove frames until only the main frame remains
00409         m_tabs->slotRemoveCurrentFrame();
00410 
00411     delete m_mainTab;
00412     delete m_mainFrame;
00413     delete m_editNodePropertiesVisitor;
00414     delete m_deleteNodeVisitor;
00415 }
00416 
00417 void View::saveSettings()
00418 {
00419     Settings::setSplitter1Sizes( m_horizontalSplitter->sizes() );
00420     Settings::setSplitter2Sizes( m_articleSplitter->sizes() );
00421     Settings::setViewMode( m_viewMode );
00422     Settings::writeConfig();
00423 }
00424 
00425 void View::slotOpenNewTab(const KURL& url, bool background)
00426 {
00427     PageViewer* page = new PageViewer(this, "page");
00428     
00429     connect( m_part, SIGNAL(signalSettingsChanged()), page, SLOT(slotPaletteOrFontChanged()));
00430 
00431     connect( page, SIGNAL(setTabIcon(const QPixmap&)),
00432             this, SLOT(setTabIcon(const QPixmap&)));
00433     connect( page, SIGNAL(urlClicked(const KURL &, Viewer*, bool, bool)),
00434             this, SLOT(slotUrlClickedInViewer(const KURL &, Viewer*, bool, bool)) );
00435 
00436     Frame* frame = new Frame(this, page, page->widget(), i18n("Untitled"));
00437     frame->setAutoDeletePart(true); // delete page viewer when removing the tab
00438 
00439     connect(page, SIGNAL(setWindowCaption (const QString &)), frame, SLOT(setTitle (const QString &)));
00440     connectFrame(frame);
00441     m_tabs->addFrame(frame);
00442 
00443     if(!background)
00444         m_tabs->showPage(page->widget());
00445     else
00446         setFocus();
00447 
00448     page->openURL(url);
00449 }
00450 
00451 
00452 void View::setTabIcon(const QPixmap& icon)
00453 {
00454     const PageViewer *s = dynamic_cast<const PageViewer*>(sender());
00455     if (s) {
00456         m_tabs->setTabIconSet(const_cast<PageViewer*>(s)->widget(), icon);
00457     }
00458 }
00459 
00460 void View::connectFrame(Frame *f)
00461 {
00462     connect(f, SIGNAL(statusText(const QString &)), this, SLOT(slotStatusText(const QString&)));
00463     connect(f, SIGNAL(captionChanged (const QString &)), this, SLOT(slotCaptionChanged (const QString &)));
00464     connect(f, SIGNAL(loadingProgress(int)), this, SLOT(slotLoadingProgress(int)) );
00465     connect(f, SIGNAL(started()), this, SLOT(slotStarted()));
00466     connect(f, SIGNAL(completed()), this, SLOT(slotCompleted()));
00467     connect(f, SIGNAL(canceled(const QString &)), this, SLOT(slotCanceled(const QString&)));
00468 }
00469 
00470 void View::slotStatusText(const QString &c)
00471 {
00472     if (sender() == m_currentFrame)
00473         emit setStatusBarText(c);
00474 }
00475 
00476 void View::slotCaptionChanged(const QString &c)
00477 {
00478     if (sender() == m_currentFrame)
00479         emit setWindowCaption(c);
00480 }
00481 
00482 void View::slotStarted()
00483 {
00484     if (sender() == m_currentFrame)
00485         emit signalStarted(0);
00486 }
00487 
00488 void View::slotCanceled(const QString &s)
00489 {
00490     if (sender() == m_currentFrame)
00491         emit signalCanceled(s);
00492 }
00493 
00494 void View::slotCompleted()
00495 {
00496     if (sender() == m_currentFrame)
00497         emit signalCompleted();
00498 }
00499 
00500 void View::slotLoadingProgress(int percent)
00501 {
00502     if (sender() == m_currentFrame)
00503         emit setProgress(percent);
00504 }
00505 
00506 bool View::importFeeds(const QDomDocument& doc)
00507 {
00508     FeedList* feedList = new FeedList();
00509     bool parsed = feedList->readFromXML(doc);
00510 
00511     // FIXME: parsing error, print some message
00512     if (!parsed)
00513     {
00514         delete feedList;
00515         return false;
00516     }
00517     QString title = feedList->title();
00518 
00519     if (title.isEmpty())
00520         title = i18n("Imported Folder");
00521 
00522     bool ok;
00523     title = KInputDialog::getText(i18n("Add Imported Folder"), i18n("Imported folder name:"), title, &ok);
00524 
00525     if (!ok)
00526     {
00527         delete feedList;
00528         return false;
00529     }
00530 
00531     Folder* fg = new Folder(title);
00532     m_feedList->rootNode()->appendChild(fg);
00533     m_feedList->append(feedList, fg);
00534 
00535     return true;
00536 }
00537 
00538 bool View::loadFeeds(const QDomDocument& doc, Folder* parent)
00539 {
00540     FeedList* feedList = new FeedList();
00541     bool parsed = feedList->readFromXML(doc);
00542 
00543     // parsing went wrong
00544     if (!parsed)
00545     {
00546         delete feedList;
00547         return false;
00548     }
00549     m_feedListView->setUpdatesEnabled(false);
00550     m_tagNodeListView->setUpdatesEnabled(false);
00551     if (!parent)
00552     {
00553         TagSet* tagSet = Kernel::self()->tagSet();
00554 
00555         Kernel::self()->setFeedList(feedList);
00556         ProgressManager::self()->setFeedList(feedList);
00557         disconnectFromFeedList(m_feedList);
00558         delete m_feedList;
00559         delete m_tagNodeList;
00560         m_feedList = feedList;
00561         connectToFeedList(m_feedList);
00562 
00563         m_tagNodeList = new TagNodeList(m_feedList, tagSet);
00564         m_feedListView->setNodeList(m_feedList);
00565         m_tagNodeListView->setNodeList(m_tagNodeList);
00566 
00567         QStringList tagIDs = m_feedList->rootNode()->tags();
00568         QStringList::ConstIterator end = tagIDs.end();
00569         for (QStringList::ConstIterator it = tagIDs.begin(); it != end; ++it)
00570         {
00571             kdDebug() << *it << endl;
00572             // create a tag for every tag ID in the archive that is not part of the tagset
00573             // this is a fallback in case the tagset was corrupted,
00574             // so the tagging information from archive does not get lost.
00575             if (!tagSet->containsID(*it))
00576             {
00577                 Tag tag(*it, *it);
00578                 tagSet->insert(tag);
00579             }
00580         }
00581     }
00582     else
00583         m_feedList->append(feedList, parent);
00584 
00585     m_feedListView->setUpdatesEnabled(true);
00586     m_feedListView->triggerUpdate();
00587     m_tagNodeListView->setUpdatesEnabled(true);
00588     m_tagNodeListView->triggerUpdate();
00589     return true;
00590 }
00591 
00592 void View::slotDeleteExpiredArticles()
00593 {
00594     TreeNode* rootNode = m_feedList->rootNode();
00595     if (rootNode)
00596         rootNode->slotDeleteExpiredArticles();
00597 }
00598 
00599 QDomDocument View::feedListToOPML()
00600 {
00601     return m_feedList->toXML();
00602 }
00603 
00604 void View::addFeedToGroup(const QString& url, const QString& groupName)
00605 {
00606 
00607     // Locate the group.
00608     TreeNode* node = m_feedListView->findNodeByTitle(groupName);
00609 
00610     Folder* group = 0;
00611     if (!node || !node->isGroup())
00612     {
00613         Folder* g = new Folder( groupName );
00614         m_feedList->rootNode()->appendChild(g);
00615         group = g;
00616     }
00617     else
00618         group = static_cast<Folder*>(node);
00619 
00620     // Invoke the Add Feed dialog with url filled in.
00621     if (group)
00622         addFeed(url, 0, group, true);
00623 }
00624 
00625 void View::slotNormalView()
00626 {
00627     if (m_viewMode == NormalView)
00628     return;
00629 
00630     if (m_viewMode == CombinedView)
00631     {
00632         m_articleList->slotShowNode(m_listTabWidget->activeView()->selectedNode());
00633         m_articleList->show();
00634 
00635         Article article = m_articleList->currentArticle();
00636 
00637         if (!article.isNull())
00638             m_articleViewer->slotShowArticle(article);
00639         else
00640             m_articleViewer->slotShowSummary(m_listTabWidget->activeView()->selectedNode());
00641     }
00642 
00643     m_articleSplitter->setOrientation(QSplitter::Vertical);
00644     m_viewMode = NormalView;
00645 
00646     Settings::setViewMode( m_viewMode );
00647 }
00648 
00649 void View::slotWidescreenView()
00650 {
00651     if (m_viewMode == WidescreenView)
00652     return;
00653 
00654     if (m_viewMode == CombinedView)
00655     {
00656         m_articleList->slotShowNode(m_listTabWidget->activeView()->selectedNode());
00657         m_articleList->show();
00658 
00659         Article article = m_articleList->currentArticle();
00660 
00661         if (!article.isNull())
00662             m_articleViewer->slotShowArticle(article);
00663         else
00664             m_articleViewer->slotShowSummary(m_listTabWidget->activeView()->selectedNode());
00665     }
00666 
00667     m_articleSplitter->setOrientation(QSplitter::Horizontal);
00668     m_viewMode = WidescreenView;
00669 
00670     Settings::setViewMode( m_viewMode );
00671 }
00672 
00673 void View::slotCombinedView()
00674 {
00675     if (m_viewMode == CombinedView)
00676         return;
00677 
00678     m_articleList->slotClear();
00679     m_articleList->hide();
00680     m_viewMode = CombinedView;
00681 
00682     slotNodeSelected(m_listTabWidget->activeView()->selectedNode());
00683     Settings::setViewMode( m_viewMode );
00684 }
00685 
00686 void View::slotFrameChanged(Frame *f)
00687 {
00688     if (m_shuttingDown)
00689         return;
00690 
00691     m_currentFrame=f;
00692 
00693     emit setWindowCaption(f->caption());
00694     emit setProgress(f->progress());
00695     emit setStatusBarText(f->statusText());
00696 
00697     m_part->mergePart(m_articleViewer);
00698 
00699     if (f->part() == m_part)
00700         m_part->mergePart(m_articleViewer);
00701     else
00702         m_part->mergePart(f->part());
00703 
00704     f->widget()->setFocus();
00705 
00706     switch (f->state())
00707     {
00708         case Frame::Started:
00709             emit signalStarted(0);
00710             break;
00711         case Frame::Canceled:
00712             emit signalCanceled(QString::null);
00713             break;
00714         case Frame::Idle:
00715         case Frame::Completed:
00716         default:
00717             emit signalCompleted();
00718     }
00719 }
00720 
00721 void View::slotFeedTreeContextMenu(KListView*, TreeNode* /*node*/, const QPoint& /*p*/)
00722 {
00723     m_tabs->showPage(m_mainTab);
00724 }
00725 
00726 void View::slotMoveCurrentNodeUp()
00727 {
00728     TreeNode* current = m_listTabWidget->activeView()->selectedNode();
00729     if (!current)
00730         return;
00731     TreeNode* prev = current->prevSibling();
00732     Folder* parent = current->parent();
00733 
00734     if (!prev || !parent)
00735         return;
00736 
00737     parent->removeChild(prev);
00738     parent->insertChild(prev, current);
00739     m_listTabWidget->activeView()->ensureNodeVisible(current);
00740 }
00741 
00742 void View::slotMoveCurrentNodeDown()
00743 {
00744     TreeNode* current = m_listTabWidget->activeView()->selectedNode();
00745     if (!current)
00746         return;
00747     TreeNode* next = current->nextSibling();
00748     Folder* parent = current->parent();
00749 
00750     if (!next || !parent)
00751         return;
00752 
00753     parent->removeChild(current);
00754     parent->insertChild(current, next);
00755     m_listTabWidget->activeView()->ensureNodeVisible(current);
00756 }
00757 
00758 void View::slotMoveCurrentNodeLeft()
00759 {
00760     TreeNode* current = m_listTabWidget->activeView()->selectedNode();
00761     if (!current || !current->parent() || !current->parent()->parent())
00762         return;
00763 
00764     Folder* parent = current->parent();
00765     Folder* grandparent = current->parent()->parent();
00766 
00767     parent->removeChild(current);
00768     grandparent->insertChild(current, parent);
00769     m_listTabWidget->activeView()->ensureNodeVisible(current);
00770 }
00771 
00772 void View::slotMoveCurrentNodeRight()
00773 {
00774     TreeNode* current = m_listTabWidget->activeView()->selectedNode();
00775     if (!current || !current->parent())
00776         return;
00777     TreeNode* prev = current->prevSibling();
00778 
00779     if ( prev && prev->isGroup() )
00780     {
00781         Folder* fg = static_cast<Folder*>(prev);
00782         current->parent()->removeChild(current);
00783         fg->appendChild(current);
00784         m_listTabWidget->activeView()->ensureNodeVisible(current);
00785     }
00786 }
00787 
00788 void View::slotNodeSelected(TreeNode* node)
00789 {
00790     m_markReadTimer->stop();
00791 
00792     if (node)
00793     {
00794         kdDebug() << "node selected: " << node->title() << endl;
00795         kdDebug() << "unread: " << node->unread() << endl;
00796         kdDebug() << "total: " << node->totalCount() << endl;
00797     }
00798 
00799     if (m_displayingAboutPage)
00800     {
00801         m_mainFrame->setTitle(i18n("Articles"));
00802         if (m_viewMode != CombinedView)
00803             m_articleList->show();
00804         if (Settings::showQuickFilter())
00805             m_searchBar->show();
00806         m_displayingAboutPage = false;
00807     }
00808 
00809     m_tabs->showPage(m_mainTab);
00810 
00811     if (Settings::resetQuickFilterOnNodeChange())
00812         m_searchBar->slotClearSearch();
00813 
00814     if (m_viewMode == CombinedView)
00815         m_articleViewer->slotShowNode(node);
00816     else
00817     {
00818         m_articleList->slotShowNode(node);
00819         m_articleViewer->slotShowSummary(node);
00820     }
00821 
00822     m_actionManager->slotNodeSelected(node);
00823 
00824     updateTagActions();
00825 }
00826 
00827 void View::slotOpenURL(const KURL& url, Viewer* currentViewer, BrowserRun::OpeningMode mode)
00828 {
00829     if (mode == BrowserRun::EXTERNAL)
00830         Viewer::displayInExternalBrowser(url);
00831     else
00832     {
00833          KParts::URLArgs args = currentViewer ? currentViewer->browserExtension()->urlArgs() : KParts::URLArgs();
00834             
00835         BrowserRun* r = new BrowserRun(this, currentViewer, url, args, mode);
00836         connect(r, SIGNAL(signalOpenInViewer(const KURL&, Akregator::Viewer*, Akregator::BrowserRun::OpeningMode)),
00837             this, SLOT(slotOpenURLReply(const KURL&, Akregator::Viewer*, Akregator::BrowserRun::OpeningMode)));
00838     }
00839 }
00840 
00841 //TODO: KDE4 remove this ugly ugly hack
00842 void View::slotUrlClickedInViewer(const KURL& url, Viewer* viewer, bool newTab, bool background)
00843 {
00844     
00845     if (!newTab)
00846     {
00847         slotOpenURL(url, viewer, BrowserRun::CURRENT_TAB);
00848     }
00849     else
00850     {
00851         slotOpenURL(url, 0L, background ? BrowserRun::NEW_TAB_BACKGROUND : BrowserRun::NEW_TAB_FOREGROUND);
00852     }
00853 }
00854 
00855 //TODO: KDE4 remove this ugly ugly hack
00856 void View::slotOpenURLReply(const KURL& url, Viewer* currentViewer, BrowserRun::OpeningMode mode)
00857 {
00858     switch (mode)
00859     {
00860         case BrowserRun::CURRENT_TAB:
00861             currentViewer->openURL(url);
00862             break;
00863         case BrowserRun::NEW_TAB_FOREGROUND:
00864         case BrowserRun::NEW_TAB_BACKGROUND:
00865             slotOpenNewTab(url, mode == BrowserRun::NEW_TAB_BACKGROUND);
00866             break;
00867         case BrowserRun::EXTERNAL:
00868             Viewer::displayInExternalBrowser(url);
00869             break;
00870     }
00871 }
00872 
00873 void View::slotFeedAdd()
00874 {
00875     Folder* group = 0;
00876     if (!m_feedListView->selectedNode())
00877         group = m_feedList->rootNode(); // all feeds
00878     else
00879     {
00880         //TODO: tag nodes need rework
00881         if ( m_feedListView->selectedNode()->isGroup())
00882             group = static_cast<Folder*>(m_feedListView->selectedNode());
00883         else
00884             group= m_feedListView->selectedNode()->parent();
00885 
00886     }
00887 
00888     TreeNode* lastChild = group->children().last();
00889 
00890     addFeed(QString::null, lastChild, group, false);
00891 }
00892 
00893 void View::addFeed(const QString& url, TreeNode *after, Folder* parent, bool autoExec)
00894 {
00895 
00896     AddFeedDialog *afd = new AddFeedDialog( 0, "add_feed" );
00897 
00898     afd->setURL(KURL::decode_string(url));
00899 
00900     if (autoExec)
00901         afd->slotOk();
00902     else
00903     {
00904         if (afd->exec() != QDialog::Accepted)
00905         {
00906             delete afd;
00907             return;
00908         }
00909     }
00910 
00911     Feed* feed = afd->feed;
00912     delete afd;
00913 
00914     FeedPropertiesDialog *dlg = new FeedPropertiesDialog( 0, "edit_feed" );
00915     dlg->setFeed(feed);
00916 
00917     dlg->selectFeedName();
00918 
00919     if (!autoExec)
00920         if (dlg->exec() != QDialog::Accepted)
00921         {
00922             delete feed;
00923             delete dlg;
00924             return;
00925         }
00926 
00927     if (!parent)
00928         parent = m_feedList->rootNode();
00929 
00930     parent->insertChild(feed, after);
00931 
00932     m_feedListView->ensureNodeVisible(feed);
00933 
00934 
00935     delete dlg;
00936 }
00937 
00938 void View::slotFeedAddGroup()
00939 {
00940     TreeNode* node = m_feedListView->selectedNode();
00941     TreeNode* after = 0;
00942 
00943     if (!node)
00944         node = m_feedListView->rootNode();
00945 
00946     // if a feed is selected, add group next to it
00947     //TODO: tag nodes need rework
00948     if (!node->isGroup())
00949     {
00950         after = node;
00951         node = node->parent();
00952     }
00953 
00954     Folder* currentGroup = static_cast<Folder*> (node);
00955 
00956     bool Ok;
00957 
00958     QString text = KInputDialog::getText(i18n("Add Folder"), i18n("Folder name:"), "", &Ok);
00959 
00960     if (Ok)
00961     {
00962         Folder* newGroup = new Folder(text);
00963         if (!after)
00964             currentGroup->appendChild(newGroup);
00965         else
00966             currentGroup->insertChild(newGroup, after);
00967 
00968         m_feedListView->ensureNodeVisible(newGroup);
00969     }
00970 }
00971 
00972 void View::slotFeedRemove()
00973 {
00974     TreeNode* selectedNode = m_listTabWidget->activeView()->selectedNode();
00975 
00976     // don't delete root element! (safety valve)
00977     if (!selectedNode || selectedNode == m_feedList->rootNode())
00978         return;
00979 
00980     m_deleteNodeVisitor->visit(selectedNode);
00981 }
00982 
00983 void View::slotFeedModify()
00984 {
00985     TreeNode* node = m_listTabWidget->activeView()->selectedNode();
00986     if (node)
00987         m_editNodePropertiesVisitor->visit(node);
00988 
00989 }
00990 
00991 void View::slotNextUnreadArticle()
00992 {
00993     if (m_viewMode == CombinedView)
00994         m_listTabWidget->activeView()->slotNextUnreadFeed();
00995     
00996     TreeNode* sel = m_listTabWidget->activeView()->selectedNode();
00997     if (sel && sel->unread() > 0)
00998         m_articleList->slotNextUnreadArticle();
00999     else
01000         m_listTabWidget->activeView()->slotNextUnreadFeed();
01001 }
01002 
01003 void View::slotPrevUnreadArticle()
01004 {
01005     if (m_viewMode == CombinedView)
01006         m_listTabWidget->activeView()->slotPrevUnreadFeed();
01007     
01008     TreeNode* sel = m_listTabWidget->activeView()->selectedNode();
01009     if (sel && sel->unread() > 0)
01010         m_articleList->slotPreviousUnreadArticle();
01011     else
01012         m_listTabWidget->activeView()->slotPrevUnreadFeed();
01013 }
01014 
01015 void View::slotMarkAllFeedsRead()
01016 {
01017     m_feedList->rootNode()->slotMarkAllArticlesAsRead();
01018 }
01019 
01020 void View::slotMarkAllRead()
01021 {
01022     if(!m_listTabWidget->activeView()->selectedNode()) return;
01023     m_listTabWidget->activeView()->selectedNode()->slotMarkAllArticlesAsRead();
01024 }
01025 
01026 void View::slotOpenHomepage()
01027 {
01028     Feed* feed = dynamic_cast<Feed *>(m_listTabWidget->activeView()->selectedNode());
01029 
01030     if (!feed)
01031         return;
01032 
01033     KURL url = KURL(feed->htmlUrl())
01034 ;
01035     switch (Settings::lMBBehaviour())
01036     {
01037         case Settings::EnumLMBBehaviour::OpenInExternalBrowser:
01038             slotOpenURL(url, 0, BrowserRun::EXTERNAL);
01039             break;
01040         case Settings::EnumLMBBehaviour::OpenInBackground:
01041             slotOpenURL(url, 0, BrowserRun::NEW_TAB_BACKGROUND);
01042             break;
01043         default:
01044             slotOpenURL(url, 0, BrowserRun::NEW_TAB_FOREGROUND);
01045     }
01046 }
01047 
01048 void View::slotSetTotalUnread()
01049 {
01050     emit signalUnreadCountChanged( m_feedList->rootNode()->unread() );
01051 }
01052 
01053 void View::slotDoIntervalFetches()
01054 {
01055     m_feedList->rootNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue(), true);
01056 }
01057 
01058 void View::slotFetchCurrentFeed()
01059 {
01060     if ( !m_listTabWidget->activeView()->selectedNode() )
01061         return;
01062     m_listTabWidget->activeView()->selectedNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue());
01063 }
01064 
01065 void View::slotFetchAllFeeds()
01066 {
01067     m_feedList->rootNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue());
01068 }
01069 
01070 void View::slotFetchingStarted()
01071 {
01072     m_mainFrame->setState(Frame::Started);
01073     m_actionManager->action("feed_stop")->setEnabled(true);
01074     m_mainFrame->setStatusText(i18n("Fetching Feeds..."));
01075 }
01076 
01077 void View::slotFetchingStopped()
01078 {
01079     m_mainFrame->setState(Frame::Completed);
01080     m_actionManager->action("feed_stop")->setEnabled(false);
01081     m_mainFrame->setStatusText(QString::null);
01082 }
01083 
01084 void View::slotFeedFetched(Feed *feed)
01085 {
01086     // iterate through the articles (once again) to do notifications properly
01087     if (feed->articles().count() > 0)
01088     {
01089         QValueList<Article> articles = feed->articles();
01090         QValueList<Article>::ConstIterator it;
01091         QValueList<Article>::ConstIterator end = articles.end();
01092         for (it = articles.begin(); it != end; ++it)
01093         {
01094             if ((*it).status()==Article::New && ((*it).feed()->useNotification() || Settings::useNotifications()))
01095             {
01096                 NotificationManager::self()->slotNotifyArticle(*it);
01097             }
01098         }
01099     }
01100 }
01101 
01102 void View::slotMouseButtonPressed(int button, const Article& article, const QPoint &, int)
01103 {
01104     if (button == Qt::MidButton)
01105     {
01106         KURL link = article.link();
01107         switch (Settings::mMBBehaviour())
01108         {
01109             case Settings::EnumMMBBehaviour::OpenInExternalBrowser:
01110                 slotOpenURL(link, 0L, BrowserRun::EXTERNAL);
01111                 break;
01112             case Settings::EnumMMBBehaviour::OpenInBackground:
01113                 slotOpenURL(link, 0L, BrowserRun::NEW_TAB_BACKGROUND);
01114                 break;
01115             default:
01116                 slotOpenURL(link, 0L, BrowserRun::NEW_TAB_FOREGROUND);
01117         }
01118     }
01119 }
01120 
01121 void View::slotAssignTag(const Tag& tag, bool assign)
01122 {
01123     kdDebug() << (assign ? "assigned" : "removed") << " tag \"" << tag.id() << "\"" << endl;
01124     QValueList<Article> selectedArticles = m_articleList->selectedArticles();
01125     for (QValueList<Article>::Iterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it)
01126     {
01127         if (assign)
01128             (*it).addTag(tag.id());
01129         else
01130             (*it).removeTag(tag.id());
01131     }
01132     updateTagActions();
01133 }
01134 /*
01135 void View::slotRemoveTag(const Tag& tag)
01136 {
01137     kdDebug() << "remove tag \"" << tag.id() << "\" from selected articles" << endl;
01138     QValueList<Article> selectedArticles = m_articleList->selectedArticles();
01139     for (QValueList<Article>::Iterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it)
01140         (*it).removeTag(tag.id());
01141 
01142     updateTagActions();
01143 }
01144 */
01145 void View::slotNewTag()
01146 {
01147     Tag tag(KApplication::randomString(8), "New Tag");
01148     Kernel::self()->tagSet()->insert(tag);
01149     TagNode* node = m_tagNodeList->findByTagID(tag.id());
01150     if (node)
01151         m_tagNodeListView->startNodeRenaming(node);
01152 }
01153 
01154 void View::slotTagCreated(const Tag& tag)
01155 {
01156     if (m_tagNodeList && !m_tagNodeList->containsTagId(tag.id()))
01157     {
01158         TagNode* tagNode = new TagNode(tag, m_feedList->rootNode());
01159         m_tagNodeList->rootNode()->appendChild(tagNode);
01160     }
01161 }
01162 
01163 void View::slotTagRemoved(const Tag& /*tag*/)
01164 {
01165 }
01166 
01167 void View::slotArticleSelected(const Article& article)
01168 {
01169     if (m_viewMode == CombinedView)
01170         return;
01171 
01172     m_markReadTimer->stop();
01173 
01174     Feed *feed = article.feed();
01175     if (!feed)
01176         return;
01177 
01178     Article a(article);
01179     if (a.status() != Article::Read)
01180     {
01181         int delay;
01182 
01183         if ( Settings::useMarkReadDelay() )
01184         {
01185             delay = Settings::markReadDelay();
01186 
01187             if (delay > 0)
01188                 m_markReadTimer->start( delay*1000, true );
01189             else
01190                 a.setStatus(Article::Read);
01191         }
01192     }
01193 
01194     KToggleAction*  maai = dynamic_cast<KToggleAction*>(m_actionManager->action("article_set_status_important"));
01195     maai->setChecked(a.keep());
01196 
01197     kdDebug() << "selected: " << a.guid() << endl;
01198 
01199     updateTagActions();
01200 
01201     m_articleViewer->slotShowArticle(a);
01202 }
01203 
01204 void View::slotOpenArticleExternal(const Article& article, const QPoint&, int)
01205 {
01206     if (!article.isNull())
01207         Viewer::displayInExternalBrowser(article.link());
01208 }
01209 
01210 
01211 void View::slotOpenCurrentArticle()
01212 {
01213     Article article = m_articleList->currentArticle();
01214 
01215     if (article.isNull())
01216         return;
01217 
01218     KURL link;
01219     if (article.link().isValid())
01220         link = article.link();
01221     else if (article.guidIsPermaLink())
01222         link = KURL(article.guid());
01223     
01224     if (link.isValid()) 
01225     {
01226         slotOpenURL(link, 0L, BrowserRun::NEW_TAB_FOREGROUND);
01227     }
01228 }
01229 
01230 void View::slotOpenCurrentArticleExternal()
01231 {
01232     slotOpenArticleExternal(m_articleList->currentArticle(), QPoint(), 0);
01233 }
01234 
01235 void View::slotOpenCurrentArticleBackgroundTab()
01236 {
01237     Article article = m_articleList->currentArticle();
01238 
01239     if (article.isNull())
01240         return;
01241 
01242     KURL link;
01243 
01244     if (article.link().isValid())
01245         link = article.link();
01246     else if (article.guidIsPermaLink())
01247         link = KURL(article.guid());
01248     
01249     if (link.isValid()) 
01250     {
01251         slotOpenURL(link, 0L, BrowserRun::NEW_TAB_BACKGROUND);
01252     }
01253 }
01254 
01255 void View::slotCopyLinkAddress()
01256 {
01257     Article article = m_articleList->currentArticle();
01258 
01259     if(article.isNull())
01260        return;
01261 
01262     QString link;
01263     if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
01264     {
01265         // in case link isn't valid, fall back to the guid permaLink.
01266         if (article.link().isValid())
01267             link = article.link().url();
01268         else
01269             link = article.guid();
01270         QClipboard *cb = QApplication::clipboard();
01271         cb->setText(link, QClipboard::Clipboard);
01272         cb->setText(link, QClipboard::Selection);
01273     }
01274 }
01275 
01276 void View::slotFeedURLDropped(KURL::List &urls, TreeNode* after, Folder* parent)
01277 {
01278     KURL::List::iterator it;
01279     for ( it = urls.begin(); it != urls.end(); ++it )
01280     {
01281         addFeed((*it).prettyURL(), after, parent, false);
01282     }
01283 }
01284 
01285 void View::slotToggleShowQuickFilter()
01286 {
01287     if ( Settings::showQuickFilter() )
01288     {
01289         Settings::setShowQuickFilter(false);
01290         m_searchBar->slotClearSearch();
01291         m_searchBar->hide();
01292     }
01293     else
01294     {
01295         Settings::setShowQuickFilter(true);
01296         if (!m_displayingAboutPage)
01297             m_searchBar->show();
01298     }
01299 
01300 }
01301 
01302 void View::slotArticleDelete()
01303 {
01304 
01305     if ( m_viewMode == CombinedView )
01306         return;
01307 
01308     QValueList<Article> articles = m_articleList->selectedArticles();
01309 
01310     QString msg;
01311     switch (articles.count())
01312     {
01313         case 0:
01314             return;
01315         case 1:
01316             msg = i18n("<qt>Are you sure you want to delete article <b>%1</b>?</qt>").arg(QStyleSheet::escape(articles.first().title()));
01317             break;
01318         default:
01319             msg = i18n("<qt>Are you sure you want to delete the selected article?</qt>", 
01320         "<qt>Are you sure you want to delete the %n selected articles?</qt>",
01321         articles.count());
01322     }
01323 
01324     if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Article"), KStdGuiItem::del()) == KMessageBox::Continue)
01325     {
01326         if (m_listTabWidget->activeView()->selectedNode())
01327             m_listTabWidget->activeView()->selectedNode()->setNotificationMode(false);
01328 
01329         QValueList<Feed*> feeds;
01330         for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
01331         {
01332             Feed* feed = (*it).feed();
01333             if (!feeds.contains(feed))
01334                 feeds.append(feed);
01335             feed->setNotificationMode(false);
01336             (*it).setDeleted();
01337         }
01338 
01339         for (QValueList<Feed*>::Iterator it = feeds.begin(); it != feeds.end(); ++it)
01340         {
01341             (*it)->setNotificationMode(true);
01342         }
01343 
01344         if (m_listTabWidget->activeView()->selectedNode())
01345             m_listTabWidget->activeView()->selectedNode()->setNotificationMode(true);
01346     }
01347 }
01348 
01349 
01350 void View::slotArticleToggleKeepFlag(bool /*enabled*/)
01351 {
01352     QValueList<Article> articles = m_articleList->selectedArticles();
01353 
01354     if (articles.isEmpty())
01355         return;
01356 
01357     bool allFlagsSet = true;
01358     for (QValueList<Article>::Iterator it = articles.begin(); allFlagsSet && it != articles.end(); ++it)
01359         if (!(*it).keep())
01360             allFlagsSet = false;
01361 
01362     for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
01363         (*it).setKeep(!allFlagsSet);
01364 }
01365 
01366 void View::slotSetSelectedArticleRead()
01367 {
01368     QValueList<Article> articles = m_articleList->selectedArticles();
01369 
01370     if (articles.isEmpty())
01371         return;
01372 
01373     for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
01374         (*it).setStatus(Article::Read);
01375 }
01376 
01377 void View::slotTextToSpeechRequest()
01378 {
01379     if (m_currentFrame == m_mainFrame)
01380     {
01381         if (m_viewMode != CombinedView)
01382         {
01383             // in non-combined view, read selected articles
01384             SpeechClient::self()->slotSpeak(m_articleList->selectedArticles());
01385             // TODO: if article viewer has a selection, read only the selected text?
01386         }
01387         else
01388         {
01389             if (m_listTabWidget->activeView()->selectedNode())
01390             {
01391                 //TODO: read articles in current node, respecting quick filter!
01392             }
01393         }
01394     }
01395     else
01396     {
01397         // TODO: read selected page viewer
01398     }
01399 }
01400 
01401 void View::slotSetSelectedArticleUnread()
01402 {
01403     QValueList<Article> articles = m_articleList->selectedArticles();
01404 
01405     if (articles.isEmpty())
01406         return;
01407 
01408     for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
01409         (*it).setStatus(Article::Unread);
01410 }
01411 
01412 void View::slotSetSelectedArticleNew()
01413 {
01414     QValueList<Article> articles = m_articleList->selectedArticles();
01415 
01416     if (articles.isEmpty())
01417         return;
01418 
01419     for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
01420         (*it).setStatus(Article::New);
01421 }
01422 
01423 void View::slotSetCurrentArticleReadDelayed()
01424 {
01425     Article article = m_articleList->currentArticle();
01426 
01427     if (article.isNull())
01428         return;
01429 
01430     article.setStatus(Article::Read);
01431 }
01432 
01433 void View::slotMouseOverInfo(const KFileItem *kifi)
01434 {
01435     if (kifi)
01436     {
01437         KFileItem *k=(KFileItem*)kifi;
01438         m_mainFrame->setStatusText(k->url().prettyURL());//getStatusBarInfo());
01439     }
01440     else
01441     {
01442         m_mainFrame->setStatusText(QString::null);
01443     }
01444 }
01445 
01446 void View::readProperties(KConfig* config)
01447 {
01448     
01449     if (!Settings::resetQuickFilterOnNodeChange())
01450     {
01451         m_searchBar->slotSetText(config->readEntry("searchLine"));
01452         int statusfilter = config->readNumEntry("searchCombo", -1);
01453         if (statusfilter != -1)
01454             m_searchBar->slotSetStatus(statusfilter);
01455     }
01456     
01457     int selectedID = config->readNumEntry("selectedNodeID", -1);
01458     if (selectedID != -1)
01459     {
01460         TreeNode* selNode = m_feedList->findByID(selectedID);
01461         if (selNode)
01462             m_listTabWidget->activeView()->setSelectedNode(selNode);
01463     }
01464 }
01465 
01466 void View::saveProperties(KConfig* config)
01467 {
01468     // save filter settings
01469     config->writeEntry("searchLine", m_searchBar->text());
01470     config->writeEntry("searchCombo", m_searchBar->status());
01471     
01472     TreeNode* sel = m_listTabWidget->activeView()->selectedNode();
01473 
01474     if (sel)
01475     {
01476         config->writeEntry("selectedNodeID", sel->id() );
01477     }
01478 }
01479 
01480 void View::connectToFeedList(FeedList* feedList)
01481 {
01482     connect(feedList->rootNode(), SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotSetTotalUnread()));
01483     slotSetTotalUnread();
01484 }
01485 
01486 void View::disconnectFromFeedList(FeedList* feedList)
01487 {
01488     disconnect(feedList->rootNode(), SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotSetTotalUnread()));
01489 }
01490 
01491 void View::updateTagActions()
01492 {
01493     QStringList tags;
01494 
01495     QValueList<Article> selectedArticles = m_articleList->selectedArticles();
01496 
01497     for (QValueList<Article>::ConstIterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it)
01498     {
01499         QStringList atags = (*it).tags();
01500         for (QStringList::ConstIterator it2 = atags.begin(); it2 != atags.end(); ++it2)
01501         {
01502             if (!tags.contains(*it2))
01503                 tags += *it2;
01504         }
01505     }
01506     m_actionManager->slotUpdateTagActions(!selectedArticles.isEmpty(), tags);
01507 }
01508 
01509 } // namespace Akregator
01510 
01511 #include "akregator_view.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys