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