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     QTimer::singleShot(0, this, SLOT(delayedInit()));
00371 }
00372 
00373 void View::delayedInit()
00374 {
00375     // HACK, FIXME:
00376     // for some reason, m_part->factory() is NULL at startup of kontact,
00377     // and thus the article viewer GUI can't be merged when creating the view.
00378     // Even the delayed init didn't help. Well, we retry every half a second until
00379     // it works. This is kind of creative, but a dirty hack nevertheless.
00380     if ( !m_part->mergePart(m_articleViewer) )
00381         QTimer::singleShot(500, this, SLOT(delayedInit()));
00382 }
00383 
00384 void View::slotSettingsChanged()
00385 {
00386     // if tagging is hidden, show only feed list
00387     m_listTabWidget->setViewMode(Settings::showTaggingGUI() ? ListTabWidget::verticalTabs : ListTabWidget::single);
00388     
00389 }
00390 
00391 void View::slotOnShutdown()
00392 {
00393     m_shuttingDown = true; // prevents slotFrameChanged from crashing
00394 
00395     m_articleList->slotShowNode(0);
00396     m_articleViewer->slotShowNode(0);
00397 
00398     Kernel::self()->fetchQueue()->slotAbort();
00399 
00400     m_feedListView->setNodeList(0);
00401     ProgressManager::self()->setFeedList(0);
00402 
00403     delete m_feedList;
00404     delete m_tagNodeList;
00405 
00406     // close all pageviewers in a controlled way
00407     // fixes bug 91660, at least when no part loading data
00408     m_tabs->setCurrentPage(m_tabs->count()-1); // select last page
00409     while (m_tabs->count() > 1) // remove frames until only the main frame remains
00410         m_tabs->slotRemoveCurrentFrame();
00411 
00412     delete m_mainTab;
00413     delete m_mainFrame;
00414     delete m_editNodePropertiesVisitor;
00415     delete m_deleteNodeVisitor;
00416 }
00417 
00418 void View::saveSettings()
00419 {
00420     Settings::setSplitter1Sizes( m_horizontalSplitter->sizes() );
00421     Settings::setSplitter2Sizes( m_articleSplitter->sizes() );
00422     Settings::setViewMode( m_viewMode );
00423     Settings::writeConfig();
00424 }
00425 
00426 void View::slotOpenNewTab(const KURL& url, bool background)
00427 {
00428     PageViewer* page = new PageViewer(this, "page");
00429     
00430     connect( m_part, SIGNAL(signalSettingsChanged()), page, SLOT(slotPaletteOrFontChanged()));
00431 
00432     connect( page, SIGNAL(setTabIcon(const QPixmap&)),
00433             this, SLOT(setTabIcon(const QPixmap&)));
00434     connect( page, SIGNAL(urlClicked(const KURL &, Viewer*, bool, bool)),
00435             this, SLOT(slotUrlClickedInViewer(const KURL &, Viewer*, bool, bool)) );
00436 
00437     Frame* frame = new Frame(this, page, page->widget(), i18n("Untitled"));
00438     frame->setAutoDeletePart(true); // delete page viewer when removing the tab
00439 
00440     connect(page, SIGNAL(setWindowCaption (const QString &)), frame, SLOT(setTitle (const QString &)));
00441     connectFrame(frame);
00442     m_tabs->addFrame(frame);
00443 
00444     if(!background)
00445         m_tabs->showPage(page->widget());
00446     else
00447         setFocus();
00448 
00449     page->openURL(url);
00450 }
00451 
00452 
00453 void View::setTabIcon(const QPixmap& icon)
00454 {
00455     const PageViewer *s = dynamic_cast<const PageViewer*>(sender());
00456     if (s) {
00457         m_tabs->setTabIconSet(const_cast<PageViewer*>(s)->widget(), icon);
00458     }
00459 }
00460 
00461 void View::connectFrame(Frame *f)
00462 {
00463     connect(f, SIGNAL(statusText(const QString &)), this, SLOT(slotStatusText(const QString&)));
00464     connect(f, SIGNAL(captionChanged (const QString &)), this, SLOT(slotCaptionChanged (const QString &)));
00465     connect(f, SIGNAL(loadingProgress(int)), this, SLOT(slotLoadingProgress(int)) );
00466     connect(f, SIGNAL(started()), this, SLOT(slotStarted()));
00467     connect(f, SIGNAL(completed()), this, SLOT(slotCompleted()));
00468     connect(f, SIGNAL(canceled(const QString &)), this, SLOT(slotCanceled(const QString&)));
00469 }
00470 
00471 void View::slotStatusText(const QString &c)
00472 {
00473     if (sender() == m_currentFrame)
00474         emit setStatusBarText(c);
00475 }
00476 
00477 void View::slotCaptionChanged(const QString &c)
00478 {
00479     if (sender() == m_currentFrame)
00480         emit setWindowCaption(c);
00481 }
00482 
00483 void View::slotStarted()
00484 {
00485     if (sender() == m_currentFrame)
00486         emit signalStarted(0);
00487 }
00488 
00489 void View::slotCanceled(const QString &s)
00490 {
00491     if (sender() == m_currentFrame)
00492         emit signalCanceled(s);
00493 }
00494 
00495 void View::slotCompleted()
00496 {
00497     if (sender() == m_currentFrame)
00498         emit signalCompleted();
00499 }
00500 
00501 void View::slotLoadingProgress(int percent)
00502 {
00503     if (sender() == m_currentFrame)
00504         emit setProgress(percent);
00505 }
00506 
00507 bool View::importFeeds(const QDomDocument& doc)
00508 {
00509     FeedList* feedList = new FeedList();
00510     bool parsed = feedList->readFromXML(doc);
00511 
00512     // FIXME: parsing error, print some message
00513     if (!parsed)
00514     {
00515         delete feedList;
00516         return false;
00517     }
00518     QString title = feedList->title();
00519 
00520     if (title.isEmpty())
00521         title = i18n("Imported Folder");
00522 
00523     bool ok;
00524     title = KInputDialog::getText(i18n("Add Imported Folder"), i18n("Imported folder name:"), title, &ok);
00525 
00526     if (!ok)
00527     {
00528         delete feedList;
00529         return false;
00530     }
00531 
00532     Folder* fg = new Folder(title);
00533     m_feedList->rootNode()->appendChild(fg);
00534     m_feedList->append(feedList, fg);
00535 
00536     return true;
00537 }
00538 
00539 bool View::loadFeeds(const QDomDocument& doc, Folder* parent)
00540 {
00541     FeedList* feedList = new FeedList();
00542     bool parsed = feedList->readFromXML(doc);
00543 
00544     // parsing went wrong
00545     if (!parsed)
00546     {
00547         delete feedList;
00548         return false;
00549     }
00550     m_feedListView->setUpdatesEnabled(false);
00551     m_tagNodeListView->setUpdatesEnabled(false);
00552     if (!parent)
00553     {
00554         TagSet* tagSet = Kernel::self()->tagSet();
00555 
00556         Kernel::self()->setFeedList(feedList);
00557         ProgressManager::self()->setFeedList(feedList);
00558         disconnectFromFeedList(m_feedList);
00559         delete m_feedList;
00560         delete m_tagNodeList;
00561         m_feedList = feedList;
00562         connectToFeedList(m_feedList);
00563 
00564         m_tagNodeList = new TagNodeList(m_feedList, tagSet);
00565         m_feedListView->setNodeList(m_feedList);
00566         m_tagNodeListView->setNodeList(m_tagNodeList);
00567 
00568         QStringList tagIDs = m_feedList->rootNode()->tags();
00569         QStringList::ConstIterator end = tagIDs.end();
00570         for (QStringList::ConstIterator it = tagIDs.begin(); it != end; ++it)
00571         {
00572             kdDebug() << *it << endl;
00573             // create a tag for every tag ID in the archive that is not part of the tagset
00574             // this is a fallback in case the tagset was corrupted,
00575             // so the tagging information from archive does not get lost.
00576             if (!tagSet->containsID(*it))
00577             {
00578                 Tag tag(*it, *it);
00579                 tagSet->insert(tag);
00580             }
00581         }
00582     }
00583     else
00584         m_feedList->append(feedList, parent);
00585 
00586     m_feedListView->setUpdatesEnabled(true);
00587     m_feedListView->triggerUpdate();
00588     m_tagNodeListView->setUpdatesEnabled(true);
00589     m_tagNodeListView->triggerUpdate();
00590     return true;
00591 }
00592 
00593 void View::slotDeleteExpiredArticles()
00594 {
00595     TreeNode* rootNode = m_feedList->rootNode();
00596     if (rootNode)
00597         rootNode->slotDeleteExpiredArticles();
00598 }
00599 
00600 QDomDocument View::feedListToOPML()
00601 {
00602     return m_feedList->toXML();
00603 }
00604 
00605 void View::addFeedToGroup(const QString& url, const QString& groupName)
00606 {
00607 
00608     // Locate the group.
00609     TreeNode* node = m_feedListView->findNodeByTitle(groupName);
00610 
00611     Folder* group = 0;
00612     if (!node || !node->isGroup())
00613     {
00614         Folder* g = new Folder( groupName );
00615         m_feedList->rootNode()->appendChild(g);
00616         group = g;
00617     }
00618     else
00619         group = static_cast<Folder*>(node);
00620 
00621     // Invoke the Add Feed dialog with url filled in.
00622     if (group)
00623         addFeed(url, 0, group, true);
00624 }
00625 
00626 void View::slotNormalView()
00627 {
00628     if (m_viewMode == NormalView)
00629     return;
00630 
00631     if (m_viewMode == CombinedView)
00632     {
00633         m_articleList->slotShowNode(m_listTabWidget->activeView()->selectedNode());
00634         m_articleList->show();
00635 
00636         Article article = m_articleList->currentArticle();
00637 
00638         if (!article.isNull())
00639             m_articleViewer->slotShowArticle(article);
00640         else
00641             m_articleViewer->slotShowSummary(m_listTabWidget->activeView()->selectedNode());
00642     }
00643 
00644     m_articleSplitter->setOrientation(QSplitter::Vertical);
00645     m_viewMode = NormalView;
00646 
00647     Settings::setViewMode( m_viewMode );
00648 }
00649 
00650 void View::slotWidescreenView()
00651 {
00652     if (m_viewMode == WidescreenView)
00653     return;
00654 
00655     if (m_viewMode == CombinedView)
00656     {
00657         m_articleList->slotShowNode(m_listTabWidget->activeView()->selectedNode());
00658         m_articleList->show();
00659 
00660         Article article = m_articleList->currentArticle();
00661 
00662         if (!article.isNull())
00663             m_articleViewer->slotShowArticle(article);
00664         else
00665             m_articleViewer->slotShowSummary(m_listTabWidget->activeView()->selectedNode());
00666     }
00667 
00668     m_articleSplitter->setOrientation(QSplitter::Horizontal);
00669     m_viewMode = WidescreenView;
00670 
00671     Settings::setViewMode( m_viewMode );
00672 }
00673 
00674 void View::slotCombinedView()
00675 {
00676     if (m_viewMode == CombinedView)
00677         return;
00678 
00679     m_articleList->slotClear();
00680     m_articleList->hide();
00681     m_viewMode = CombinedView;
00682 
00683     slotNodeSelected(m_listTabWidget->activeView()->selectedNode());
00684     Settings::setViewMode( m_viewMode );
00685 }
00686 
00687 void View::slotFrameChanged(Frame *f)
00688 {
00689     if (m_shuttingDown)
00690         return;
00691 
00692     m_currentFrame=f;
00693 
00694     emit setWindowCaption(f->caption());
00695     emit setProgress(f->progress());
00696     emit setStatusBarText(f->statusText());
00697 
00698     m_part->mergePart(m_articleViewer);
00699 
00700     if (f->part() == m_part)
00701         m_part->mergePart(m_articleViewer);
00702     else
00703         m_part->mergePart(f->part());
00704 
00705     f->widget()->setFocus();
00706 
00707     switch (f->state())
00708     {
00709         case Frame::Started:
00710             emit signalStarted(0);
00711             break;
00712         case Frame::Canceled:
00713             emit signalCanceled(QString::null);
00714             break;
00715         case Frame::Idle:
00716         case Frame::Completed:
00717         default:
00718             emit signalCompleted();
00719     }
00720 }
00721 
00722 void View::slotFeedTreeContextMenu(KListView*, TreeNode* /*node*/, const QPoint& /*p*/)
00723 {
00724     m_tabs->showPage(m_mainTab);
00725 }
00726 
00727 void View::slotMoveCurrentNodeUp()
00728 {
00729     TreeNode* current = m_listTabWidget->activeView()->selectedNode();
00730     if (!current)
00731         return;
00732     TreeNode* prev = current->prevSibling();
00733     Folder* parent = current->parent();
00734 
00735     if (!prev || !parent)
00736         return;
00737 
00738     parent->removeChild(prev);
00739     parent->insertChild(prev, current);
00740     m_listTabWidget->activeView()->ensureNodeVisible(current);
00741 }
00742 
00743 void View::slotMoveCurrentNodeDown()
00744 {
00745     TreeNode* current = m_listTabWidget->activeView()->selectedNode();
00746     if (!current)
00747         return;
00748     TreeNode* next = current->nextSibling();
00749     Folder* parent = current->parent();
00750 
00751     if (!next || !parent)
00752         return;
00753 
00754     parent->removeChild(current);
00755     parent->insertChild(current, next);
00756     m_listTabWidget->activeView()->ensureNodeVisible(current);
00757 }
00758 
00759 void View::slotMoveCurrentNodeLeft()
00760 {
00761     TreeNode* current = m_listTabWidget->activeView()->selectedNode();
00762     if (!current || !current->parent() || !current->parent()->parent())
00763         return;
00764 
00765     Folder* parent = current->parent();
00766     Folder* grandparent = current->parent()->parent();
00767 
00768     parent->removeChild(current);
00769     grandparent->insertChild(current, parent);
00770     m_listTabWidget->activeView()->ensureNodeVisible(current);
00771 }
00772 
00773 void View::slotMoveCurrentNodeRight()
00774 {
00775     TreeNode* current = m_listTabWidget->activeView()->selectedNode();
00776     if (!current || !current->parent())
00777         return;
00778     TreeNode* prev = current->prevSibling();
00779 
00780     if ( prev && prev->isGroup() )
00781     {
00782         Folder* fg = static_cast<Folder*>(prev);
00783         current->parent()->removeChild(current);
00784         fg->appendChild(current);
00785         m_listTabWidget->activeView()->ensureNodeVisible(current);
00786     }
00787 }
00788 
00789 void View::slotNodeSelected(TreeNode* node)
00790 {
00791     m_markReadTimer->stop();
00792 
00793     if (node)
00794     {
00795         kdDebug() << "node selected: " << node->title() << endl;
00796         kdDebug() << "unread: " << node->unread() << endl;
00797         kdDebug() << "total: " << node->totalCount() << endl;
00798     }
00799 
00800     if (m_displayingAboutPage)
00801     {
00802         m_mainFrame->setTitle(i18n("Articles"));
00803         if (m_viewMode != CombinedView)
00804             m_articleList->show();
00805         if (Settings::showQuickFilter())
00806             m_searchBar->show();
00807         m_displayingAboutPage = false;
00808     }
00809 
00810     m_tabs->showPage(m_mainTab);
00811 
00812     if (Settings::resetQuickFilterOnNodeChange())
00813         m_searchBar->slotClearSearch();
00814 
00815     if (m_viewMode == CombinedView)
00816         m_articleViewer->slotShowNode(node);
00817     else
00818     {
00819         m_articleList->slotShowNode(node);
00820         m_articleViewer->slotShowSummary(node);
00821     }
00822 
00823     if (node)
00824         m_mainFrame->setCaption(node->title());
00825 
00826     m_actionManager->slotNodeSelected(node);
00827 
00828     updateTagActions();
00829 }
00830 
00831 void View::slotOpenURL(const KURL& url, Viewer* currentViewer, BrowserRun::OpeningMode mode)
00832 {
00833     if (mode == BrowserRun::EXTERNAL)
00834         Viewer::displayInExternalBrowser(url);
00835     else
00836     {
00837          KParts::URLArgs args = currentViewer ? currentViewer->browserExtension()->urlArgs() : KParts::URLArgs();
00838             
00839         BrowserRun* r = new BrowserRun(this, currentViewer, url, args, mode);
00840         connect(r, SIGNAL(signalOpenInViewer(const KURL&, Akregator::Viewer*, Akregator::BrowserRun::OpeningMode)),
00841             this, SLOT(slotOpenURLReply(const KURL&, Akregator::Viewer*, Akregator::BrowserRun::OpeningMode)));
00842     }
00843 }
00844 
00845 //TODO: KDE4 remove this ugly ugly hack
00846 void View::slotUrlClickedInViewer(const KURL& url, Viewer* viewer, bool newTab, bool background)
00847 {
00848     
00849     if (!newTab)
00850     {
00851         slotOpenURL(url, viewer, BrowserRun::CURRENT_TAB);
00852     }
00853     else
00854     {
00855         slotOpenURL(url, 0L, background ? BrowserRun::NEW_TAB_BACKGROUND : BrowserRun::NEW_TAB_FOREGROUND);
00856     }
00857 }
00858 
00859 //TODO: KDE4 remove this ugly ugly hack
00860 void View::slotOpenURLReply(const KURL& url, Viewer* currentViewer, BrowserRun::OpeningMode mode)
00861 {
00862     switch (mode)
00863     {
00864         case BrowserRun::CURRENT_TAB:
00865             currentViewer->openURL(url);
00866             break;
00867         case BrowserRun::NEW_TAB_FOREGROUND:
00868         case BrowserRun::NEW_TAB_BACKGROUND:
00869             slotOpenNewTab(url, mode == BrowserRun::NEW_TAB_BACKGROUND);
00870             break;
00871         case BrowserRun::EXTERNAL:
00872             Viewer::displayInExternalBrowser(url);
00873             break;
00874     }
00875 }
00876 
00877 void View::slotFeedAdd()
00878 {
00879     Folder* group = 0;
00880     if (!m_feedListView->selectedNode())
00881         group = m_feedList->rootNode(); // all feeds
00882     else
00883     {
00884         //TODO: tag nodes need rework
00885         if ( m_feedListView->selectedNode()->isGroup())
00886             group = static_cast<Folder*>(m_feedListView->selectedNode());
00887         else
00888             group= m_feedListView->selectedNode()->parent();
00889 
00890     }
00891 
00892     TreeNode* lastChild = group->children().last();
00893 
00894     addFeed(QString::null, lastChild, group, false);
00895 }
00896 
00897 void View::addFeed(const QString& url, TreeNode *after, Folder* parent, bool autoExec)
00898 {
00899 
00900     AddFeedDialog *afd = new AddFeedDialog( 0, "add_feed" );
00901 
00902     afd->setURL(KURL::decode_string(url));
00903 
00904     if (autoExec)
00905         afd->slotOk();
00906     else
00907     {
00908         if (afd->exec() != QDialog::Accepted)
00909         {
00910             delete afd;
00911             return;
00912         }
00913     }
00914 
00915     Feed* feed = afd->feed;
00916     delete afd;
00917 
00918     FeedPropertiesDialog *dlg = new FeedPropertiesDialog( 0, "edit_feed" );
00919     dlg->setFeed(feed);
00920 
00921     dlg->selectFeedName();
00922 
00923     if (!autoExec)
00924         if (dlg->exec() != QDialog::Accepted)
00925         {
00926             delete feed;
00927             delete dlg;
00928             return;
00929         }
00930 
00931     if (!parent)
00932         parent = m_feedList->rootNode();
00933 
00934     parent->insertChild(feed, after);
00935 
00936     m_feedListView->ensureNodeVisible(feed);
00937 
00938 
00939     delete dlg;
00940 }
00941 
00942 void View::slotFeedAddGroup()
00943 {
00944     TreeNode* node = m_feedListView->selectedNode();
00945     TreeNode* after = 0;
00946 
00947     if (!node)
00948         node = m_feedListView->rootNode();
00949 
00950     // if a feed is selected, add group next to it
00951     //TODO: tag nodes need rework
00952     if (!node->isGroup())
00953     {
00954         after = node;
00955         node = node->parent();
00956     }
00957 
00958     Folder* currentGroup = static_cast<Folder*> (node);
00959 
00960     bool Ok;
00961 
00962     QString text = KInputDialog::getText(i18n("Add Folder"), i18n("Folder name:"), "", &Ok);
00963 
00964     if (Ok)
00965     {
00966         Folder* newGroup = new Folder(text);
00967         if (!after)
00968             currentGroup->appendChild(newGroup);
00969         else
00970             currentGroup->insertChild(newGroup, after);
00971 
00972         m_feedListView->ensureNodeVisible(newGroup);
00973     }
00974 }
00975 
00976 void View::slotFeedRemove()
00977 {
00978     TreeNode* selectedNode = m_listTabWidget->activeView()->selectedNode();
00979 
00980     // don't delete root element! (safety valve)
00981     if (!selectedNode || selectedNode == m_feedList->rootNode())
00982         return;
00983 
00984     m_deleteNodeVisitor->visit(selectedNode);
00985 }
00986 
00987 void View::slotFeedModify()
00988 {
00989     TreeNode* node = m_listTabWidget->activeView()->selectedNode();
00990     if (node)
00991         m_editNodePropertiesVisitor->visit(node);
00992 
00993 }
00994 
00995 void View::slotNextUnreadArticle()
00996 {
00997     if (m_viewMode == CombinedView)
00998         m_listTabWidget->activeView()->slotNextUnreadFeed();
00999     
01000     TreeNode* sel = m_listTabWidget->activeView()->selectedNode();
01001     if (sel && sel->unread() > 0)
01002         m_articleList->slotNextUnreadArticle();
01003     else
01004         m_listTabWidget->activeView()->slotNextUnreadFeed();
01005 }
01006 
01007 void View::slotPrevUnreadArticle()
01008 {
01009     if (m_viewMode == CombinedView)
01010         m_listTabWidget->activeView()->slotPrevUnreadFeed();
01011     
01012     TreeNode* sel = m_listTabWidget->activeView()->selectedNode();
01013     if (sel && sel->unread() > 0)
01014         m_articleList->slotPreviousUnreadArticle();
01015     else
01016         m_listTabWidget->activeView()->slotPrevUnreadFeed();
01017 }
01018 
01019 void View::slotMarkAllFeedsRead()
01020 {
01021     m_feedList->rootNode()->slotMarkAllArticlesAsRead();
01022 }
01023 
01024 void View::slotMarkAllRead()
01025 {
01026     if(!m_listTabWidget->activeView()->selectedNode()) return;
01027     m_listTabWidget->activeView()->selectedNode()->slotMarkAllArticlesAsRead();
01028 }
01029 
01030 void View::slotOpenHomepage()
01031 {
01032     Feed* feed = dynamic_cast<Feed *>(m_listTabWidget->activeView()->selectedNode());
01033 
01034     if (!feed)
01035         return;
01036 
01037     KURL url = KURL(feed->htmlUrl())
01038 ;
01039     switch (Settings::lMBBehaviour())
01040     {
01041         case Settings::EnumLMBBehaviour::OpenInExternalBrowser:
01042             slotOpenURL(url, 0, BrowserRun::EXTERNAL);
01043             break;
01044         case Settings::EnumLMBBehaviour::OpenInBackground:
01045             slotOpenURL(url, 0, BrowserRun::NEW_TAB_BACKGROUND);
01046             break;
01047         default:
01048             slotOpenURL(url, 0, BrowserRun::NEW_TAB_FOREGROUND);
01049     }
01050 }
01051 
01052 void View::slotSetTotalUnread()
01053 {
01054     emit signalUnreadCountChanged( m_feedList->rootNode()->unread() );
01055 }
01056 
01057 void View::slotDoIntervalFetches()
01058 {
01059     m_feedList->rootNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue(), true);
01060 }
01061 
01062 void View::slotFetchCurrentFeed()
01063 {
01064     if ( !m_listTabWidget->activeView()->selectedNode() )
01065         return;
01066     m_listTabWidget->activeView()->selectedNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue());
01067 }
01068 
01069 void View::slotFetchAllFeeds()
01070 {
01071     m_feedList->rootNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue());
01072 }
01073 
01074 void View::slotFetchingStarted()
01075 {
01076     m_mainFrame->setState(Frame::Started);
01077     m_actionManager->action("feed_stop")->setEnabled(true);
01078     m_mainFrame->setStatusText(i18n("Fetching Feeds..."));
01079 }
01080 
01081 void View::slotFetchingStopped()
01082 {
01083     m_mainFrame->setState(Frame::Completed);
01084     m_actionManager->action("feed_stop")->setEnabled(false);
01085     m_mainFrame->setStatusText(QString::null);
01086 }
01087 
01088 void View::slotFeedFetched(Feed *feed)
01089 {
01090     // iterate through the articles (once again) to do notifications properly
01091     if (feed->articles().count() > 0)
01092     {
01093         QValueList<Article> articles = feed->articles();
01094         QValueList<Article>::ConstIterator it;
01095         QValueList<Article>::ConstIterator end = articles.end();
01096         for (it = articles.begin(); it != end; ++it)
01097         {
01098             if ((*it).status()==Article::New && ((*it).feed()->useNotification() || Settings::useNotifications()))
01099             {
01100                 NotificationManager::self()->slotNotifyArticle(*it);
01101             }
01102         }
01103     }
01104 }
01105 
01106 void View::slotMouseButtonPressed(int button, const Article& article, const QPoint &, int)
01107 {
01108     if (button == Qt::MidButton)
01109     {
01110         KURL link = article.link();
01111         switch (Settings::mMBBehaviour())
01112         {
01113             case Settings::EnumMMBBehaviour::OpenInExternalBrowser:
01114                 slotOpenURL(link, 0L, BrowserRun::EXTERNAL);
01115                 break;
01116             case Settings::EnumMMBBehaviour::OpenInBackground:
01117                 slotOpenURL(link, 0L, BrowserRun::NEW_TAB_BACKGROUND);
01118                 break;
01119             default:
01120                 slotOpenURL(link, 0L, BrowserRun::NEW_TAB_FOREGROUND);
01121         }
01122     }
01123 }
01124 
01125 void View::slotAssignTag(const Tag& tag, bool assign)
01126 {
01127     kdDebug() << (assign ? "assigned" : "removed") << " tag \"" << tag.id() << "\"" << endl;
01128     QValueList<Article> selectedArticles = m_articleList->selectedArticles();
01129     for (QValueList<Article>::Iterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it)
01130     {
01131         if (assign)
01132             (*it).addTag(tag.id());
01133         else
01134             (*it).removeTag(tag.id());
01135     }
01136     updateTagActions();
01137 }
01138 /*
01139 void View::slotRemoveTag(const Tag& tag)
01140 {
01141     kdDebug() << "remove tag \"" << tag.id() << "\" from selected articles" << endl;
01142     QValueList<Article> selectedArticles = m_articleList->selectedArticles();
01143     for (QValueList<Article>::Iterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it)
01144         (*it).removeTag(tag.id());
01145 
01146     updateTagActions();
01147 }
01148 */
01149 void View::slotNewTag()
01150 {
01151     Tag tag(KApplication::randomString(8), "New Tag");
01152     Kernel::self()->tagSet()->insert(tag);
01153     TagNode* node = m_tagNodeList->findByTagID(tag.id());
01154     if (node)
01155         m_tagNodeListView->startNodeRenaming(node);
01156 }
01157 
01158 void View::slotTagCreated(const Tag& tag)
01159 {
01160     if (m_tagNodeList && !m_tagNodeList->containsTagId(tag.id()))
01161     {
01162         TagNode* tagNode = new TagNode(tag, m_feedList->rootNode());
01163         m_tagNodeList->rootNode()->appendChild(tagNode);
01164     }
01165 }
01166 
01167 void View::slotTagRemoved(const Tag& /*tag*/)
01168 {
01169 }
01170 
01171 void View::slotArticleSelected(const Article& article)
01172 {
01173     if (m_viewMode == CombinedView)
01174         return;
01175 
01176     m_markReadTimer->stop();
01177 
01178     Feed *feed = article.feed();
01179     if (!feed)
01180         return;
01181 
01182     Article a(article);
01183     if (a.status() != Article::Read)
01184     {
01185         int delay;
01186 
01187         if ( Settings::useMarkReadDelay() )
01188         {
01189             delay = Settings::markReadDelay();
01190 
01191             if (delay > 0)
01192                 m_markReadTimer->start( delay*1000, true );
01193             else
01194                 a.setStatus(Article::Read);
01195         }
01196     }
01197 
01198     KToggleAction*  maai = dynamic_cast<KToggleAction*>(m_actionManager->action("article_set_status_important"));
01199     maai->setChecked(a.keep());
01200 
01201     kdDebug() << "selected: " << a.guid() << endl;
01202 
01203     updateTagActions();
01204 
01205     m_articleViewer->slotShowArticle(a);
01206 }
01207 
01208 void View::slotOpenArticleExternal(const Article& article, const QPoint&, int)
01209 {
01210     if (!article.isNull())
01211         Viewer::displayInExternalBrowser(article.link());
01212 }
01213 
01214 
01215 void View::slotOpenCurrentArticle()
01216 {
01217     Article article = m_articleList->currentArticle();
01218 
01219     if (article.isNull())
01220         return;
01221 
01222     KURL link;
01223     if (article.link().isValid())
01224         link = article.link();
01225     else if (article.guidIsPermaLink())
01226         link = KURL(article.guid());
01227     
01228     if (link.isValid()) 
01229     {
01230         slotOpenURL(link, 0L, BrowserRun::NEW_TAB_FOREGROUND);
01231     }
01232 }
01233 
01234 void View::slotOpenCurrentArticleExternal()
01235 {
01236     slotOpenArticleExternal(m_articleList->currentArticle(), QPoint(), 0);
01237 }
01238 
01239 void View::slotOpenCurrentArticleBackgroundTab()
01240 {
01241     Article article = m_articleList->currentArticle();
01242 
01243     if (article.isNull())
01244         return;
01245 
01246     KURL link;
01247 
01248     if (article.link().isValid())
01249         link = article.link();
01250     else if (article.guidIsPermaLink())
01251         link = KURL(article.guid());
01252     
01253     if (link.isValid()) 
01254     {
01255         slotOpenURL(link, 0L, BrowserRun::NEW_TAB_BACKGROUND);
01256     }
01257 }
01258 
01259 void View::slotCopyLinkAddress()
01260 {
01261     Article article = m_articleList->currentArticle();
01262 
01263     if(article.isNull())
01264        return;
01265 
01266     QString link;
01267     if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
01268     {
01269         // in case link isn't valid, fall back to the guid permaLink.
01270         if (article.link().isValid())
01271             link = article.link().url();
01272         else
01273             link = article.guid();
01274         QClipboard *cb = QApplication::clipboard();
01275         cb->setText(link, QClipboard::Clipboard);
01276         cb->setText(link, QClipboard::Selection);
01277     }
01278 }
01279 
01280 void View::slotFeedURLDropped(KURL::List &urls, TreeNode* after, Folder* parent)
01281 {
01282     KURL::List::iterator it;
01283     for ( it = urls.begin(); it != urls.end(); ++it )
01284     {
01285         addFeed((*it).prettyURL(), after, parent, false);
01286     }
01287 }
01288 
01289 void View::slotToggleShowQuickFilter()
01290 {
01291     if ( Settings::showQuickFilter() )
01292     {
01293         Settings::setShowQuickFilter(false);
01294         m_searchBar->slotClearSearch();
01295         m_searchBar->hide();
01296     }
01297     else
01298     {
01299         Settings::setShowQuickFilter(true);
01300         if (!m_displayingAboutPage)
01301             m_searchBar->show();
01302     }
01303 
01304 }
01305 
01306 void View::slotArticleDelete()
01307 {
01308 
01309     if ( m_viewMode == CombinedView )
01310         return;
01311 
01312     QValueList<Article> articles = m_articleList->selectedArticles();
01313 
01314     QString msg;
01315     switch (articles.count())
01316     {
01317         case 0:
01318             return;
01319         case 1:
01320             msg = i18n("<qt>Are you sure you want to delete article <b>%1</b>?</qt>").arg(QStyleSheet::escape(articles.first().title()));
01321             break;
01322         default:
01323             msg = i18n("<qt>Are you sure you want to delete the selected article?</qt>", 
01324         "<qt>Are you sure you want to delete the %n selected articles?</qt>",
01325         articles.count());
01326     }
01327 
01328     if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Article"), KStdGuiItem::del()) == KMessageBox::Continue)
01329     {
01330         if (m_listTabWidget->activeView()->selectedNode())
01331             m_listTabWidget->activeView()->selectedNode()->setNotificationMode(false);
01332 
01333         QValueList<Feed*> feeds;
01334         for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
01335         {
01336             Feed* feed = (*it).feed();
01337             if (!feeds.contains(feed))
01338                 feeds.append(feed);
01339             feed->setNotificationMode(false);
01340             (*it).setDeleted();
01341         }
01342 
01343         for (QValueList<Feed*>::Iterator it = feeds.begin(); it != feeds.end(); ++it)
01344         {
01345             (*it)->setNotificationMode(true);
01346         }
01347 
01348         if (m_listTabWidget->activeView()->selectedNode())
01349             m_listTabWidget->activeView()->selectedNode()->setNotificationMode(true);
01350     }
01351 }
01352 
01353 
01354 void View::slotArticleToggleKeepFlag(bool /*enabled*/)
01355 {
01356     QValueList<Article> articles = m_articleList->selectedArticles();
01357 
01358     if (articles.isEmpty())
01359         return;
01360 
01361     bool allFlagsSet = true;
01362     for (QValueList<Article>::Iterator it = articles.begin(); allFlagsSet && it != articles.end(); ++it)
01363         if (!(*it).keep())
01364             allFlagsSet = false;
01365 
01366     for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
01367         (*it).setKeep(!allFlagsSet);
01368 }
01369 
01370 void View::slotSetSelectedArticleRead()
01371 {
01372     QValueList<Article> articles = m_articleList->selectedArticles();
01373 
01374     if (articles.isEmpty())
01375         return;
01376 
01377     for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
01378         (*it).setStatus(Article::Read);
01379 }
01380 
01381 void View::slotTextToSpeechRequest()
01382 {
01383     if (m_currentFrame == m_mainFrame)
01384     {
01385         if (m_viewMode != CombinedView)
01386         {
01387             // in non-combined view, read selected articles
01388             SpeechClient::self()->slotSpeak(m_articleList->selectedArticles());
01389             // TODO: if article viewer has a selection, read only the selected text?
01390         }
01391         else
01392         {
01393             if (m_listTabWidget->activeView()->selectedNode())
01394             {
01395                 //TODO: read articles in current node, respecting quick filter!
01396             }
01397         }
01398     }
01399     else
01400     {
01401         QString selectedText = static_cast<PageViewer *>(m_currentFrame->part())->selectedText();
01402         
01403         if (!selectedText.isEmpty())
01404             SpeechClient::self()->slotSpeak(selectedText, "en");
01405     }
01406 }
01407 
01408 void View::slotSetSelectedArticleUnread()
01409 {
01410     QValueList<Article> articles = m_articleList->selectedArticles();
01411 
01412     if (articles.isEmpty())
01413         return;
01414 
01415     for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
01416         (*it).setStatus(Article::Unread);
01417 }
01418 
01419 void View::slotSetSelectedArticleNew()
01420 {
01421     QValueList<Article> articles = m_articleList->selectedArticles();
01422 
01423     if (articles.isEmpty())
01424         return;
01425 
01426     for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
01427         (*it).setStatus(Article::New);
01428 }
01429 
01430 void View::slotSetCurrentArticleReadDelayed()
01431 {
01432     Article article = m_articleList->currentArticle();
01433 
01434     if (article.isNull())
01435         return;
01436 
01437     article.setStatus(Article::Read);
01438 }
01439 
01440 void View::slotMouseOverInfo(const KFileItem *kifi)
01441 {
01442     if (kifi)
01443     {
01444         KFileItem *k=(KFileItem*)kifi;
01445         m_mainFrame->setStatusText(k->url().prettyURL());//getStatusBarInfo());
01446     }
01447     else
01448     {
01449         m_mainFrame->setStatusText(QString::null);
01450     }
01451 }
01452 
01453 void View::readProperties(KConfig* config)
01454 {
01455     
01456     if (!Settings::resetQuickFilterOnNodeChange())
01457     {
01458         m_searchBar->slotSetText(config->readEntry("searchLine"));
01459         int statusfilter = config->readNumEntry("searchCombo", -1);
01460         if (statusfilter != -1)
01461             m_searchBar->slotSetStatus(statusfilter);
01462     }
01463     
01464     int selectedID = config->readNumEntry("selectedNodeID", -1);
01465     if (selectedID != -1)
01466     {
01467         TreeNode* selNode = m_feedList->findByID(selectedID);
01468         if (selNode)
01469             m_listTabWidget->activeView()->setSelectedNode(selNode);
01470     }
01471 
01472     QStringList urls = config->readListEntry("FeedBrowserURLs");
01473     QStringList::ConstIterator it = urls.begin();
01474     for (; it != urls.end(); ++it)
01475     {
01476         KURL url = KURL::fromPathOrURL(*it);
01477         if (url.isValid())
01478             slotOpenNewTab(url, true); // open in background
01479     }
01480 }
01481 
01482 void View::saveProperties(KConfig* config)
01483 {
01484     // save filter settings
01485     config->writeEntry("searchLine", m_searchBar->text());
01486     config->writeEntry("searchCombo", m_searchBar->status());
01487     
01488     TreeNode* sel = m_listTabWidget->activeView()->selectedNode();
01489 
01490     if (sel)
01491     {
01492         config->writeEntry("selectedNodeID", sel->id() );
01493     }
01494 
01495     // save browser URLs
01496     QStringList urls;
01497     QPtrList<Frame> frames = m_tabs->frames();
01498     QPtrList<Frame>::ConstIterator it = frames.begin();
01499     for (; it != frames.end(); ++it)
01500     {
01501         Frame *frame = *it;
01502         KParts::ReadOnlyPart *part = frame->part();
01503         PageViewer *pageViewer = dynamic_cast<PageViewer*>(part); // don't save the ArticleViewer
01504         if (pageViewer)
01505         {
01506             KURL url = pageViewer->url();
01507             if (url.isValid())
01508                 urls.append(url.prettyURL());
01509         }
01510     }
01511 
01512     config->writeEntry("FeedBrowserURLs", urls);
01513 }
01514 
01515 void View::connectToFeedList(FeedList* feedList)
01516 {
01517     connect(feedList->rootNode(), SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotSetTotalUnread()));
01518     slotSetTotalUnread();
01519 }
01520 
01521 void View::disconnectFromFeedList(FeedList* feedList)
01522 {
01523     disconnect(feedList->rootNode(), SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotSetTotalUnread()));
01524 }
01525 
01526 void View::updateTagActions()
01527 {
01528     QStringList tags;
01529 
01530     QValueList<Article> selectedArticles = m_articleList->selectedArticles();
01531 
01532     for (QValueList<Article>::ConstIterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it)
01533     {
01534         QStringList atags = (*it).tags();
01535         for (QStringList::ConstIterator it2 = atags.begin(); it2 != atags.end(); ++it2)
01536         {
01537             if (!tags.contains(*it2))
01538                 tags += *it2;
01539         }
01540     }
01541     m_actionManager->slotUpdateTagActions(!selectedArticles.isEmpty(), tags);
01542 }
01543 
01544 } // namespace Akregator
01545 
01546 #include "akregator_view.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys