/* This file is part of Akregator. Copyright (C) 2004 Stanislav Karchebny 2004 Sashmit Bhaduri 2005 Frank Osterfeld This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of TQt, and distribute the resulting executable, without including the source code for TQt in the source distribution. */ #include "actionmanagerimpl.h" #include "akregator_part.h" #include "akregator_run.h" #include "akregator_view.h" #include "listtabwidget.h" #include "addfeeddialog.h" #include "propertiesdialog.h" #include "frame.h" #include "fetchqueue.h" #include "feedlistview.h" #include "articlelistview.h" #include "articleviewer.h" #include "viewer.h" #include "feed.h" #include "tagfolder.h" #include "folder.h" #include "feedlist.h" #include "akregatorconfig.h" #include "kernel.h" #include "pageviewer.h" #include "searchbar.h" #include "speechclient.h" #include "storage.h" #include "tabwidget.h" #include "tag.h" #include "tagset.h" #include "tagnode.h" #include "tagnodelist.h" #include "tagpropertiesdialog.h" #include "treenode.h" #include "progressmanager.h" #include "treenodevisitor.h" #include "notificationmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for startup time measure #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Akregator { class View::EditNodePropertiesVisitor : public TreeNodeVisitor { public: EditNodePropertiesVisitor(View* view) : m_view(view) {} virtual ~EditNodePropertiesVisitor() {} virtual bool visitTagNode(TagNode* node) { TagPropertiesDialog* dlg = new TagPropertiesDialog(m_view); dlg->setTag(node->tag()); dlg->exec(); delete dlg; return true; } virtual bool visitFolder(Folder* node) { m_view->m_listTabWidget->activeView()->startNodeRenaming(node); return true; } virtual bool visitFeed(Feed* node) { FeedPropertiesDialog *dlg = new FeedPropertiesDialog( m_view, "edit_feed" ); dlg->setFeed(node); dlg->exec(); delete dlg; return true; } private: View* m_view; }; class View::DeleteNodeVisitor : public TreeNodeVisitor { public: DeleteNodeVisitor(View* view) : m_view(view) {} virtual ~DeleteNodeVisitor() {} virtual bool visitTagNode(TagNode* node) { TQString msg = i18n("Are you sure you want to delete tag %1? The tag will be removed from all articles.").arg(node->title()); if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Tag"), KStdGuiItem::del()) == KMessageBox::Continue) { Tag tag = node->tag(); TQValueList
articles = m_view->m_feedList->rootNode()->articles(tag.id()); node->setNotificationMode(false); for (TQValueList
::Iterator it = articles.begin(); it != articles.end(); ++it) (*it).removeTag(tag.id()); node->setNotificationMode(true); Kernel::self()->tagSet()->remove(tag); m_view->m_listTabWidget->activeView()->setFocus(); } return true; } virtual bool visitFolder(Folder* node) { TQString msg; if (node->title().isEmpty()) msg = i18n("Are you sure you want to delete this folder and its feeds and subfolders?"); else msg = i18n("Are you sure you want to delete folder %1 and its feeds and subfolders?").arg(node->title()); if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Folder"), KStdGuiItem::del()) == KMessageBox::Continue) { delete node; m_view->m_listTabWidget->activeView()->setFocus(); } return true; } virtual bool visitFeed(Feed* node) { TQString msg; if (node->title().isEmpty()) msg = i18n("Are you sure you want to delete this feed?"); else msg = i18n("Are you sure you want to delete feed %1?").arg(node->title()); if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Feed"), KStdGuiItem::del()) == KMessageBox::Continue) { delete node; m_view->m_listTabWidget->activeView()->setFocus(); } return true; } private: View* m_view; }; View::~View() { // if m_shuttingDown is false, slotOnShutdown was not called. That // means that not the whole app is shutdown, only the part. So it // should be no risk to do the cleanups now if (!m_shuttingDown) { kdDebug() << "View::~View(): slotOnShutdown() wasn't called. Calling it now." << endl; slotOnShutdown(); } kdDebug() << "View::~View(): leaving" << endl; } View::View( Part *part, TQWidget *parent, ActionManagerImpl* actionManager, const char *name) : TQWidget(parent, name), m_viewMode(NormalView), m_actionManager(actionManager) { m_editNodePropertiesVisitor = new EditNodePropertiesVisitor(this); m_deleteNodeVisitor = new DeleteNodeVisitor(this); m_keepFlagIcon = TQPixmap(locate("data", "akregator/pics/akregator_flag.png")); m_part = part; m_feedList = new FeedList(); m_tagNodeList = new TagNodeList(m_feedList, Kernel::self()->tagSet()); m_shuttingDown = false; m_displayingAboutPage = false; m_currentFrame = 0L; setFocusPolicy(TQWidget::StrongFocus); TQVBoxLayout *lt = new TQVBoxLayout( this ); m_horizontalSplitter = new TQSplitter(Qt::Horizontal, this); m_horizontalSplitter->setOpaqueResize(true); lt->addWidget(m_horizontalSplitter); connect (Kernel::self()->fetchQueue(), TQT_SIGNAL(fetched(Feed*)), this, TQT_SLOT(slotFeedFetched(Feed*))); connect (Kernel::self()->fetchQueue(), TQT_SIGNAL(signalStarted()), this, TQT_SLOT(slotFetchingStarted())); connect (Kernel::self()->fetchQueue(), TQT_SIGNAL(signalStopped()), this, TQT_SLOT(slotFetchingStopped())); connect(Kernel::self()->tagSet(), TQT_SIGNAL(signalTagAdded(const Tag&)), this, TQT_SLOT(slotTagCreated(const Tag&))); connect(Kernel::self()->tagSet(), TQT_SIGNAL(signalTagRemoved(const Tag&)), this, TQT_SLOT(slotTagRemoved(const Tag&))); m_listTabWidget = new ListTabWidget(m_horizontalSplitter); m_actionManager->initListTabWidget(m_listTabWidget); connect(m_listTabWidget, TQT_SIGNAL(signalNodeSelected(TreeNode*)), this, TQT_SLOT(slotNodeSelected(TreeNode*))); if (!Settings::showTaggingGUI()) m_listTabWidget->setViewMode(ListTabWidget::single); m_feedListView = new NodeListView( this, "feedtree" ); m_listTabWidget->addView(m_feedListView, i18n("Feeds"), TDEGlobal::iconLoader()->loadIcon("folder", TDEIcon::Small)); connect(m_feedListView, TQT_SIGNAL(signalContextMenu(TDEListView*, TreeNode*, const TQPoint&)), this, TQT_SLOT(slotFeedTreeContextMenu(TDEListView*, TreeNode*, const TQPoint&))); connect(m_feedListView, TQT_SIGNAL(signalDropped (KURL::List &, TreeNode*, Folder*)), this, TQT_SLOT(slotFeedURLDropped (KURL::List &, TreeNode*, Folder*))); m_tagNodeListView = new NodeListView(this); m_listTabWidget->addView(m_tagNodeListView, i18n("Tags"), TDEGlobal::iconLoader()->loadIcon("rss_tag", TDEIcon::Small)); connect(m_tagNodeListView, TQT_SIGNAL(signalContextMenu(TDEListView*, TreeNode*, const TQPoint&)), this, TQT_SLOT(slotFeedTreeContextMenu(TDEListView*, TreeNode*, const TQPoint&))); ProgressManager::self()->setFeedList(m_feedList); m_tabs = new TabWidget(m_horizontalSplitter); m_actionManager->initTabWidget(m_tabs); connect( m_part, TQT_SIGNAL(signalSettingsChanged()), m_tabs, TQT_SLOT(slotSettingsChanged())); connect( m_tabs, TQT_SIGNAL( currentFrameChanged(Frame *) ), this, TQT_SLOT( slotFrameChanged(Frame *) ) ); TQWhatsThis::add(m_tabs, i18n("You can view multiple articles in several open tabs.")); m_mainTab = new TQWidget(this, "Article Tab"); TQVBoxLayout *mainTabLayout = new TQVBoxLayout( m_mainTab, 0, 2, "mainTabLayout"); TQWhatsThis::add(m_mainTab, i18n("Articles list.")); m_searchBar = new SearchBar(m_mainTab); if ( !Settings::showQuickFilter() ) m_searchBar->hide(); mainTabLayout->addWidget(m_searchBar); m_articleSplitter = new TQSplitter(Qt::Vertical, m_mainTab, "panner2"); m_articleList = new ArticleListView( m_articleSplitter, "articles" ); m_actionManager->initArticleListView(m_articleList); connect( m_articleList, TQT_SIGNAL(signalMouseButtonPressed(int, const Article&, const TQPoint &, int)), this, TQT_SLOT(slotMouseButtonPressed(int, const Article&, const TQPoint &, int))); // use selectionChanged instead of clicked connect( m_articleList, TQT_SIGNAL(signalArticleChosen(const Article&)), this, TQT_SLOT( slotArticleSelected(const Article&)) ); connect( m_articleList, TQT_SIGNAL(signalDoubleClicked(const Article&, const TQPoint&, int)), this, TQT_SLOT( slotOpenArticleExternal(const Article&, const TQPoint&, int)) ); m_articleViewer = new ArticleViewer(m_articleSplitter, "article_viewer"); m_articleViewer->setSafeMode(); // disable JS, Java, etc... m_actionManager->initArticleViewer(m_articleViewer); connect(m_searchBar, TQT_SIGNAL(signalSearch(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&)), m_articleList, TQT_SLOT(slotSetFilter(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&))); connect(m_searchBar, TQT_SIGNAL(signalSearch(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&)), m_articleViewer, TQT_SLOT(slotSetFilter(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&))); connect( m_articleViewer, TQT_SIGNAL(urlClicked(const KURL&, Viewer*, bool, bool)), this, TQT_SLOT(slotUrlClickedInViewer(const KURL&, Viewer*, bool, bool)) ); connect( m_articleViewer->browserExtension(), TQT_SIGNAL(mouseOverInfo(const KFileItem *)), this, TQT_SLOT(slotMouseOverInfo(const KFileItem *)) ); connect( m_part, TQT_SIGNAL(signalSettingsChanged()), m_articleViewer, TQT_SLOT(slotPaletteOrFontChanged())); TQWhatsThis::add(m_articleViewer->widget(), i18n("Browsing area.")); mainTabLayout->addWidget( m_articleSplitter ); m_mainFrame=new Frame(TQT_TQOBJECT(this), m_part, m_mainTab, i18n("Articles"), false); connectFrame(m_mainFrame); m_tabs->addFrame(m_mainFrame); const TQValueList sp1sizes = Settings::splitter1Sizes(); if ( sp1sizes.count() >= m_horizontalSplitter->sizes().count() ) m_horizontalSplitter->setSizes( sp1sizes ); const TQValueList sp2sizes = Settings::splitter2Sizes(); if ( sp2sizes.count() >= m_articleSplitter->sizes().count() ) m_articleSplitter->setSizes( sp2sizes ); TDEConfig *conf = Settings::self()->config(); conf->setGroup("General"); if(!conf->readBoolEntry("Disable Introduction", false)) { m_articleList->hide(); m_searchBar->hide(); m_articleViewer->displayAboutPage(); m_mainFrame->setTitle(i18n("About")); m_displayingAboutPage = true; } m_fetchTimer = new TQTimer(this); connect( m_fetchTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotDoIntervalFetches()) ); m_fetchTimer->start(1000*60); // delete expired articles once per hour m_expiryTimer = new TQTimer(this); connect(m_expiryTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotDeleteExpiredArticles()) ); m_expiryTimer->start(3600*1000); m_markReadTimer = new TQTimer(this); connect(m_markReadTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotSetCurrentArticleReadDelayed()) ); switch (Settings::viewMode()) { case CombinedView: slotCombinedView(); break; case WidescreenView: slotWidescreenView(); break; default: slotNormalView(); } if (!Settings::resetQuickFilterOnNodeChange()) { m_searchBar->slotSetStatus(Settings::statusFilter()); m_searchBar->slotSetText(Settings::textFilter()); } TQTimer::singleShot(1000, this, TQT_SLOT(slotDeleteExpiredArticles()) ); m_part->mergePart(m_articleViewer); } void View::slotSettingsChanged() { // if tagging is hidden, show only feed list m_listTabWidget->setViewMode(Settings::showTaggingGUI() ? ListTabWidget::verticalTabs : ListTabWidget::single); // In case text colors changed, repaint to apply changes immediately if (m_articleList->isShown()) { m_articleList->repaintContents(); } if (m_feedListView->isShown()) { m_feedListView->repaintContents(); } } void View::slotOnShutdown() { m_shuttingDown = true; // prevents slotFrameChanged from crashing m_articleList->slotShowNode(0); m_articleViewer->slotShowNode(0); Kernel::self()->fetchQueue()->slotAbort(); m_feedListView->setNodeList(0); ProgressManager::self()->setFeedList(0); delete m_feedList; delete m_tagNodeList; // close all pageviewers in a controlled way // fixes bug 91660, at least when no part loading data m_tabs->setCurrentPage(m_tabs->count()-1); // select last page while (m_tabs->count() > 1) // remove frames until only the main frame remains m_tabs->slotRemoveCurrentFrame(); delete m_mainTab; delete m_mainFrame; delete m_editNodePropertiesVisitor; delete m_deleteNodeVisitor; } void View::saveSettings() { const TQValueList spl1 = m_horizontalSplitter->sizes(); if ( spl1.contains( 0 ) == 0 ) Settings::setSplitter1Sizes( spl1 ); const TQValueList spl2 = m_articleSplitter->sizes(); if ( spl2.contains( 0 ) == 0 ) Settings::setSplitter2Sizes( spl2 ); Settings::setViewMode( m_viewMode ); Settings::writeConfig(); } void View::slotOpenNewTab(const KURL& url, bool background) { PageViewer* page = new PageViewer(this, "page"); connect( m_part, TQT_SIGNAL(signalSettingsChanged()), page, TQT_SLOT(slotPaletteOrFontChanged())); connect( page, TQT_SIGNAL(setTabIcon(const TQPixmap&)), this, TQT_SLOT(setTabIcon(const TQPixmap&))); connect( page, TQT_SIGNAL(urlClicked(const KURL &, Viewer*, bool, bool)), this, TQT_SLOT(slotUrlClickedInViewer(const KURL &, Viewer*, bool, bool)) ); Frame* frame = new Frame(TQT_TQOBJECT(this), page, page->widget(), i18n("Untitled")); frame->setAutoDeletePart(true); // delete page viewer when removing the tab connect(page, TQT_SIGNAL(setWindowCaption (const TQString &)), frame, TQT_SLOT(setTitle (const TQString &))); connectFrame(frame); m_tabs->addFrame(frame); if(!background) m_tabs->showPage(page->widget()); else setFocus(); page->openURL(url); } void View::setTabIcon(const TQPixmap& icon) { const PageViewer *s = dynamic_cast(sender()); if (s) { m_tabs->setTabIconSet(const_cast(s)->widget(), icon); } } void View::connectFrame(Frame *f) { connect(f, TQT_SIGNAL(statusText(const TQString &)), this, TQT_SLOT(slotStatusText(const TQString&))); connect(f, TQT_SIGNAL(captionChanged (const TQString &)), this, TQT_SLOT(slotCaptionChanged (const TQString &))); connect(f, TQT_SIGNAL(loadingProgress(int)), this, TQT_SLOT(slotLoadingProgress(int)) ); connect(f, TQT_SIGNAL(started()), this, TQT_SLOT(slotStarted())); connect(f, TQT_SIGNAL(completed()), this, TQT_SLOT(slotCompleted())); connect(f, TQT_SIGNAL(canceled(const TQString &)), this, TQT_SLOT(slotCanceled(const TQString&))); } void View::slotStatusText(const TQString &c) { if (sender() == m_currentFrame) emit setStatusBarText(c); } void View::slotCaptionChanged(const TQString &c) { if (sender() == m_currentFrame) emit setWindowCaption(c); } void View::slotStarted() { if (sender() == m_currentFrame) emit signalStarted(0); } void View::slotCanceled(const TQString &s) { if (sender() == m_currentFrame) emit signalCanceled(s); } void View::slotCompleted() { if (sender() == m_currentFrame) emit signalCompleted(); } void View::slotLoadingProgress(int percent) { if (sender() == m_currentFrame) emit setProgress(percent); } bool View::importFeeds(const TQDomDocument& doc) { FeedList* feedList = new FeedList(); bool parsed = feedList->readFromXML(doc); // FIXME: parsing error, print some message if (!parsed) { delete feedList; return false; } TQString title = feedList->title(); if (title.isEmpty()) title = i18n("Imported Folder"); bool ok; title = KInputDialog::getText(i18n("Add Imported Folder"), i18n("Imported folder name:"), title, &ok); if (!ok) { delete feedList; return false; } Folder* fg = new Folder(title); m_feedList->rootNode()->appendChild(fg); m_feedList->append(feedList, fg); return true; } bool View::loadFeeds(const TQDomDocument& doc, Folder* parent) { FeedList* feedList = new FeedList(); bool parsed = feedList->readFromXML(doc); // parsing went wrong if (!parsed) { delete feedList; return false; } m_feedListView->setUpdatesEnabled(false); m_tagNodeListView->setUpdatesEnabled(false); if (!parent) { TagSet* tagSet = Kernel::self()->tagSet(); Kernel::self()->setFeedList(feedList); ProgressManager::self()->setFeedList(feedList); disconnectFromFeedList(m_feedList); delete m_feedList; delete m_tagNodeList; m_feedList = feedList; connectToFeedList(m_feedList); m_tagNodeList = new TagNodeList(m_feedList, tagSet); m_feedListView->setNodeList(m_feedList); m_tagNodeListView->setNodeList(m_tagNodeList); TQStringList tagIDs = m_feedList->rootNode()->tags(); TQStringList::ConstIterator end = tagIDs.end(); for (TQStringList::ConstIterator it = tagIDs.begin(); it != end; ++it) { kdDebug() << *it << endl; // create a tag for every tag ID in the archive that is not part of the tagset // this is a fallback in case the tagset was corrupted, // so the tagging information from archive does not get lost. if (!tagSet->containsID(*it)) { Tag tag(*it, *it); tagSet->insert(tag); } } } else m_feedList->append(feedList, parent); m_feedListView->setUpdatesEnabled(true); m_feedListView->triggerUpdate(); m_tagNodeListView->setUpdatesEnabled(true); m_tagNodeListView->triggerUpdate(); return true; } void View::slotDeleteExpiredArticles() { TreeNode* rootNode = m_feedList->rootNode(); if (rootNode) rootNode->slotDeleteExpiredArticles(); } TQDomDocument View::feedListToOPML() { return m_feedList->toXML(); } void View::addFeedToGroup(const TQString& url, const TQString& groupName) { // Locate the group. TreeNode* node = m_feedListView->findNodeByTitle(groupName); Folder* group = 0; if (!node || !node->isGroup()) { Folder* g = new Folder( groupName ); m_feedList->rootNode()->appendChild(g); group = g; } else group = static_cast(node); // Invoke the Add Feed dialog with url filled in. if (group) addFeed(url, 0, group, true); } void View::slotNormalView() { if (m_viewMode == NormalView) return; if (m_viewMode == CombinedView) { m_articleList->slotShowNode(m_listTabWidget->activeView()->selectedNode()); m_articleList->show(); Article article = m_articleList->currentArticle(); if (!article.isNull()) m_articleViewer->slotShowArticle(article); else m_articleViewer->slotShowSummary(m_listTabWidget->activeView()->selectedNode()); } m_articleSplitter->setOrientation(Qt::Vertical); m_viewMode = NormalView; Settings::setViewMode( m_viewMode ); } void View::slotWidescreenView() { if (m_viewMode == WidescreenView) return; if (m_viewMode == CombinedView) { m_articleList->slotShowNode(m_listTabWidget->activeView()->selectedNode()); m_articleList->show(); Article article = m_articleList->currentArticle(); if (!article.isNull()) m_articleViewer->slotShowArticle(article); else m_articleViewer->slotShowSummary(m_listTabWidget->activeView()->selectedNode()); } m_articleSplitter->setOrientation(Qt::Horizontal); m_viewMode = WidescreenView; Settings::setViewMode( m_viewMode ); } void View::slotCombinedView() { if (m_viewMode == CombinedView) return; m_articleList->slotClear(); m_articleList->hide(); m_viewMode = CombinedView; slotNodeSelected(m_listTabWidget->activeView()->selectedNode()); Settings::setViewMode( m_viewMode ); } void View::slotFrameChanged(Frame *f) { if (m_shuttingDown) return; m_currentFrame=f; emit setWindowCaption(f->caption()); emit setProgress(f->progress()); emit setStatusBarText(f->statusText()); if (f->part() == m_part) m_part->mergePart(m_articleViewer); else m_part->mergePart(f->part()); f->widget()->setFocus(); switch (f->state()) { case Frame::Started: emit signalStarted(0); break; case Frame::Canceled: emit signalCanceled(TQString()); break; case Frame::Idle: case Frame::Completed: default: emit signalCompleted(); } } void View::slotFeedTreeContextMenu(TDEListView*, TreeNode* /*node*/, const TQPoint& /*p*/) { m_tabs->showPage(m_mainTab); } void View::slotMoveCurrentNodeUp() { TreeNode* current = m_listTabWidget->activeView()->selectedNode(); if (!current) return; TreeNode* prev = current->prevSibling(); Folder* parent = current->parent(); if (!prev || !parent) return; parent->removeChild(prev); parent->insertChild(prev, current); m_listTabWidget->activeView()->ensureNodeVisible(current); } void View::slotMoveCurrentNodeDown() { TreeNode* current = m_listTabWidget->activeView()->selectedNode(); if (!current) return; TreeNode* next = current->nextSibling(); Folder* parent = current->parent(); if (!next || !parent) return; parent->removeChild(current); parent->insertChild(current, next); m_listTabWidget->activeView()->ensureNodeVisible(current); } void View::slotMoveCurrentNodeLeft() { TreeNode* current = m_listTabWidget->activeView()->selectedNode(); if (!current || !current->parent() || !current->parent()->parent()) return; Folder* parent = current->parent(); Folder* grandparent = current->parent()->parent(); parent->removeChild(current); grandparent->insertChild(current, parent); m_listTabWidget->activeView()->ensureNodeVisible(current); } void View::slotMoveCurrentNodeRight() { TreeNode* current = m_listTabWidget->activeView()->selectedNode(); if (!current || !current->parent()) return; TreeNode* prev = current->prevSibling(); if ( prev && prev->isGroup() ) { Folder* fg = static_cast(prev); current->parent()->removeChild(current); fg->appendChild(current); m_listTabWidget->activeView()->ensureNodeVisible(current); } } void View::slotNodeSelected(TreeNode* node) { m_markReadTimer->stop(); if (node) { kdDebug() << "node selected: " << node->title() << endl; kdDebug() << "unread: " << node->unread() << endl; kdDebug() << "total: " << node->totalCount() << endl; } if (m_displayingAboutPage) { m_mainFrame->setTitle(i18n("Articles")); if (m_viewMode != CombinedView) m_articleList->show(); if (Settings::showQuickFilter()) m_searchBar->show(); m_displayingAboutPage = false; } m_tabs->showPage(m_mainTab); if (Settings::resetQuickFilterOnNodeChange()) m_searchBar->slotClearSearch(); if (m_viewMode == CombinedView) m_articleViewer->slotShowNode(node); else { m_articleList->slotShowNode(node); m_articleViewer->slotShowSummary(node); } if (node) m_mainFrame->setCaption(node->title()); m_actionManager->slotNodeSelected(node); updateTagActions(); } void View::slotOpenURL(const KURL& url, Viewer* currentViewer, BrowserRun::OpeningMode mode) { if (mode == BrowserRun::EXTERNAL) Viewer::displayInExternalBrowser(url); else { KParts::URLArgs args = currentViewer ? currentViewer->browserExtension()->urlArgs() : KParts::URLArgs(); BrowserRun* r = new BrowserRun(this, currentViewer, url, args, mode); connect(r, TQT_SIGNAL(signalOpenInViewer(const KURL&, Akregator::Viewer*, Akregator::BrowserRun::OpeningMode)), this, TQT_SLOT(slotOpenURLReply(const KURL&, Akregator::Viewer*, Akregator::BrowserRun::OpeningMode))); } } //TODO: KDE4 remove this ugly ugly hack void View::slotUrlClickedInViewer(const KURL& url, Viewer* viewer, bool newTab, bool background) { if (!newTab) { slotOpenURL(url, viewer, BrowserRun::CURRENT_TAB); } else { slotOpenURL(url, 0L, background ? BrowserRun::NEW_TAB_BACKGROUND : BrowserRun::NEW_TAB_FOREGROUND); } } //TODO: KDE4 remove this ugly ugly hack void View::slotOpenURLReply(const KURL& url, Viewer* currentViewer, BrowserRun::OpeningMode mode) { switch (mode) { case BrowserRun::CURRENT_TAB: currentViewer->openURL(url); break; case BrowserRun::NEW_TAB_FOREGROUND: case BrowserRun::NEW_TAB_BACKGROUND: slotOpenNewTab(url, mode == BrowserRun::NEW_TAB_BACKGROUND); break; case BrowserRun::EXTERNAL: Viewer::displayInExternalBrowser(url); break; } } void View::slotFeedAdd() { Folder* group = 0; if (!m_feedListView->selectedNode()) group = m_feedList->rootNode(); // all feeds else { //TODO: tag nodes need rework if ( m_feedListView->selectedNode()->isGroup()) group = static_cast(m_feedListView->selectedNode()); else group= m_feedListView->selectedNode()->parent(); } TreeNode* lastChild = group->children().last(); addFeed(TQString(), lastChild, group, false); } void View::addFeed(const TQString& url, TreeNode *after, Folder* parent, bool autoExec) { AddFeedDialog *afd = new AddFeedDialog( 0, "add_feed" ); afd->setURL(KURL::decode_string(url)); if (autoExec) afd->slotOk(); else { if (afd->exec() != TQDialog::Accepted) { delete afd; return; } } Feed* feed = afd->feed; delete afd; FeedPropertiesDialog *dlg = new FeedPropertiesDialog( 0, "edit_feed" ); dlg->setFeed(feed); dlg->selectFeedName(); if (!autoExec) if (dlg->exec() != TQDialog::Accepted) { delete feed; delete dlg; return; } if (!parent) parent = m_feedList->rootNode(); parent->insertChild(feed, after); m_feedListView->ensureNodeVisible(feed); delete dlg; } void View::slotFeedAddGroup() { TreeNode* node = m_feedListView->selectedNode(); TreeNode* after = 0; if (!node) node = m_feedListView->rootNode(); // if a feed is selected, add group next to it //TODO: tag nodes need rework if (!node->isGroup()) { after = node; node = node->parent(); } Folder* currentGroup = static_cast (node); bool Ok; TQString text = KInputDialog::getText(i18n("Add Folder"), i18n("Folder name:"), "", &Ok); if (Ok) { Folder* newGroup = new Folder(text); if (!after) currentGroup->appendChild(newGroup); else currentGroup->insertChild(newGroup, after); m_feedListView->ensureNodeVisible(newGroup); } } void View::slotFeedRemove() { TreeNode* selectedNode = m_listTabWidget->activeView()->selectedNode(); // don't delete root element! (safety valve) if (!selectedNode || selectedNode == m_feedList->rootNode()) return; m_deleteNodeVisitor->visit(selectedNode); } void View::slotFeedModify() { TreeNode* node = m_listTabWidget->activeView()->selectedNode(); if (node) m_editNodePropertiesVisitor->visit(node); } void View::slotNextUnreadArticle() { if (m_viewMode == CombinedView) m_listTabWidget->activeView()->slotNextUnreadFeed(); TreeNode* sel = m_listTabWidget->activeView()->selectedNode(); if (sel && sel->unread() > 0) m_articleList->slotNextUnreadArticle(); else m_listTabWidget->activeView()->slotNextUnreadFeed(); } void View::slotPrevUnreadArticle() { if (m_viewMode == CombinedView) m_listTabWidget->activeView()->slotPrevUnreadFeed(); TreeNode* sel = m_listTabWidget->activeView()->selectedNode(); if (sel && sel->unread() > 0) m_articleList->slotPreviousUnreadArticle(); else m_listTabWidget->activeView()->slotPrevUnreadFeed(); } void View::slotMarkAllFeedsRead() { m_feedList->rootNode()->slotMarkAllArticlesAsRead(); } void View::slotMarkAllRead() { if(!m_listTabWidget->activeView()->selectedNode()) return; m_listTabWidget->activeView()->selectedNode()->slotMarkAllArticlesAsRead(); } void View::slotOpenHomepage() { Feed* feed = dynamic_cast(m_listTabWidget->activeView()->selectedNode()); if (!feed) return; KURL url = KURL(feed->htmlUrl()) ; switch (Settings::lMBBehaviour()) { case Settings::EnumLMBBehaviour::OpenInExternalBrowser: slotOpenURL(url, 0, BrowserRun::EXTERNAL); break; case Settings::EnumLMBBehaviour::OpenInBackground: slotOpenURL(url, 0, BrowserRun::NEW_TAB_BACKGROUND); break; default: slotOpenURL(url, 0, BrowserRun::NEW_TAB_FOREGROUND); } } void View::slotSetTotalUnread() { emit signalUnreadCountChanged( m_feedList->rootNode()->unread() ); } void View::slotDoIntervalFetches() { m_feedList->rootNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue(), true); } void View::slotFetchCurrentFeed() { if ( !m_listTabWidget->activeView()->selectedNode() ) return; m_listTabWidget->activeView()->selectedNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue()); } void View::slotFetchAllFeeds() { m_feedList->rootNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue()); } void View::slotFetchingStarted() { m_mainFrame->setState(Frame::Started); m_actionManager->action("feed_stop")->setEnabled(true); m_mainFrame->setStatusText(i18n("Fetching Feeds...")); } void View::slotFetchingStopped() { m_mainFrame->setState(Frame::Completed); m_actionManager->action("feed_stop")->setEnabled(false); m_mainFrame->setStatusText(TQString()); } void View::slotFeedFetched(Feed *feed) { // iterate through the articles (once again) to do notifications properly if (feed->articles().count() > 0) { TQValueList
articles = feed->articles(); TQValueList
::ConstIterator it; TQValueList
::ConstIterator end = articles.end(); for (it = articles.begin(); it != end; ++it) { if ((*it).status()==Article::New && ((*it).feed()->useNotification() || Settings::useNotifications())) { NotificationManager::self()->slotNotifyArticle(*it); } } } } void View::slotMouseButtonPressed(int button, const Article& article, const TQPoint &, int) { if (button == Qt::MidButton) { KURL link = article.link(); switch (Settings::mMBBehaviour()) { case Settings::EnumMMBBehaviour::OpenInExternalBrowser: slotOpenURL(link, 0L, BrowserRun::EXTERNAL); break; case Settings::EnumMMBBehaviour::OpenInBackground: slotOpenURL(link, 0L, BrowserRun::NEW_TAB_BACKGROUND); break; default: slotOpenURL(link, 0L, BrowserRun::NEW_TAB_FOREGROUND); } } } void View::slotAssignTag(const Tag& tag, bool assign) { kdDebug() << (assign ? "assigned" : "removed") << " tag \"" << tag.id() << "\"" << endl; TQValueList
selectedArticles = m_articleList->selectedArticles(); for (TQValueList
::Iterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it) { if (assign) (*it).addTag(tag.id()); else (*it).removeTag(tag.id()); } updateTagActions(); } /* void View::slotRemoveTag(const Tag& tag) { kdDebug() << "remove tag \"" << tag.id() << "\" from selected articles" << endl; TQValueList
selectedArticles = m_articleList->selectedArticles(); for (TQValueList
::Iterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it) (*it).removeTag(tag.id()); updateTagActions(); } */ void View::slotNewTag() { Tag tag(TDEApplication::randomString(8), "New Tag"); Kernel::self()->tagSet()->insert(tag); TagNode* node = m_tagNodeList->findByTagID(tag.id()); if (node) m_tagNodeListView->startNodeRenaming(node); } void View::slotTagCreated(const Tag& tag) { if (m_tagNodeList && !m_tagNodeList->containsTagId(tag.id())) { TagNode* tagNode = new TagNode(tag, m_feedList->rootNode()); m_tagNodeList->rootNode()->appendChild(tagNode); } } void View::slotTagRemoved(const Tag& /*tag*/) { } void View::slotArticleSelected(const Article& article) { if (m_viewMode == CombinedView) return; m_markReadTimer->stop(); Feed *feed = article.feed(); if (!feed) return; Article a(article); if (a.status() != Article::Read) { int delay; if ( Settings::useMarkReadDelay() ) { delay = Settings::markReadDelay(); if (delay > 0) m_markReadTimer->start( delay*1000, true ); else a.setStatus(Article::Read); } } TDEToggleAction* maai = dynamic_cast(m_actionManager->action("article_set_status_important")); maai->setChecked(a.keep()); kdDebug() << "selected: " << a.guid() << endl; updateTagActions(); m_articleViewer->slotShowArticle(a); } void View::slotOpenArticleExternal(const Article& article, const TQPoint&, int) { if (!article.isNull()) Viewer::displayInExternalBrowser(article.link()); } void View::slotOpenCurrentArticle() { Article article = m_articleList->currentArticle(); if (article.isNull()) return; KURL link; if (article.link().isValid()) link = article.link(); else if (article.guidIsPermaLink()) link = KURL(article.guid()); if (link.isValid()) { slotOpenURL(link, 0L, BrowserRun::NEW_TAB_FOREGROUND); } } void View::slotOpenCurrentArticleExternal() { slotOpenArticleExternal(m_articleList->currentArticle(), TQPoint(), 0); } void View::slotOpenCurrentArticleBackgroundTab() { Article article = m_articleList->currentArticle(); if (article.isNull()) return; KURL link; if (article.link().isValid()) link = article.link(); else if (article.guidIsPermaLink()) link = KURL(article.guid()); if (link.isValid()) { slotOpenURL(link, 0L, BrowserRun::NEW_TAB_BACKGROUND); } } void View::slotCopyLinkAddress() { Article article = m_articleList->currentArticle(); if(article.isNull()) return; TQString link; if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid())) { // in case link isn't valid, fall back to the guid permaLink. if (article.link().isValid()) link = article.link().url(); else link = article.guid(); TQClipboard *cb = TQApplication::clipboard(); cb->setText(link, TQClipboard::Clipboard); cb->setText(link, TQClipboard::Selection); } } void View::slotFeedURLDropped(KURL::List &urls, TreeNode* after, Folder* parent) { KURL::List::iterator it; for ( it = urls.begin(); it != urls.end(); ++it ) { addFeed((*it).prettyURL(), after, parent, false); } } void View::slotToggleShowQuickFilter() { if ( Settings::showQuickFilter() ) { Settings::setShowQuickFilter(false); m_searchBar->slotClearSearch(); m_searchBar->hide(); } else { Settings::setShowQuickFilter(true); if (!m_displayingAboutPage) m_searchBar->show(); } } void View::slotArticleDelete() { if ( m_viewMode == CombinedView ) return; TQValueList
articles = m_articleList->selectedArticles(); TQString msg; switch (articles.count()) { case 0: return; case 1: msg = i18n("Are you sure you want to delete article %1?").arg(TQStyleSheet::escape(articles.first().title())); break; default: msg = i18n("Are you sure you want to delete the selected article?", "Are you sure you want to delete the %n selected articles?", articles.count()); } if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Article"), KStdGuiItem::del()) == KMessageBox::Continue) { if (m_listTabWidget->activeView()->selectedNode()) m_listTabWidget->activeView()->selectedNode()->setNotificationMode(false); TQValueList feeds; for (TQValueList
::Iterator it = articles.begin(); it != articles.end(); ++it) { Feed* feed = (*it).feed(); if (!feeds.contains(feed)) feeds.append(feed); feed->setNotificationMode(false); (*it).setDeleted(); } for (TQValueList::Iterator it = feeds.begin(); it != feeds.end(); ++it) { (*it)->setNotificationMode(true); } if (m_listTabWidget->activeView()->selectedNode()) m_listTabWidget->activeView()->selectedNode()->setNotificationMode(true); } } void View::slotArticleToggleKeepFlag(bool /*enabled*/) { TQValueList
articles = m_articleList->selectedArticles(); if (articles.isEmpty()) return; bool allFlagsSet = true; for (TQValueList
::Iterator it = articles.begin(); allFlagsSet && it != articles.end(); ++it) if (!(*it).keep()) allFlagsSet = false; for (TQValueList
::Iterator it = articles.begin(); it != articles.end(); ++it) (*it).setKeep(!allFlagsSet); } void View::slotSetSelectedArticleRead() { TQValueList
articles = m_articleList->selectedArticles(); if (articles.isEmpty()) return; for (TQValueList
::Iterator it = articles.begin(); it != articles.end(); ++it) (*it).setStatus(Article::Read); } void View::slotTextToSpeechRequest() { if (m_currentFrame == m_mainFrame) { if (m_viewMode != CombinedView) { // in non-combined view, read selected articles SpeechClient::self()->slotSpeak(m_articleList->selectedArticles()); // TODO: if article viewer has a selection, read only the selected text? } else { if (m_listTabWidget->activeView()->selectedNode()) { //TODO: read articles in current node, respecting quick filter! } } } else { TQString selectedText = static_cast(m_currentFrame->part())->selectedText(); if (!selectedText.isEmpty()) SpeechClient::self()->slotSpeak(selectedText, "en"); } } void View::slotSetSelectedArticleUnread() { TQValueList
articles = m_articleList->selectedArticles(); if (articles.isEmpty()) return; for (TQValueList
::Iterator it = articles.begin(); it != articles.end(); ++it) (*it).setStatus(Article::Unread); } void View::slotSetSelectedArticleNew() { TQValueList
articles = m_articleList->selectedArticles(); if (articles.isEmpty()) return; for (TQValueList
::Iterator it = articles.begin(); it != articles.end(); ++it) (*it).setStatus(Article::New); } void View::slotSetCurrentArticleReadDelayed() { Article article = m_articleList->currentArticle(); if (article.isNull()) return; article.setStatus(Article::Read); } void View::slotMouseOverInfo(const KFileItem *kifi) { if (kifi) { KFileItem *k=(KFileItem*)kifi; m_mainFrame->setStatusText(k->url().prettyURL());//geStatusBarInfo()); } else { m_mainFrame->setStatusText(TQString()); } } void View::readProperties(TDEConfig* config) { if (!Settings::resetQuickFilterOnNodeChange()) { m_searchBar->slotSetText(config->readEntry("searchLine")); int statusfilter = config->readNumEntry("searchCombo", -1); if (statusfilter != -1) m_searchBar->slotSetStatus(statusfilter); } int selectedID = config->readNumEntry("selectedNodeID", -1); if (selectedID != -1) { TreeNode* selNode = m_feedList->findByID(selectedID); if (selNode) m_listTabWidget->activeView()->setSelectedNode(selNode); } TQStringList urls = config->readListEntry("FeedBrowserURLs"); TQStringList::ConstIterator it = urls.begin(); for (; it != urls.end(); ++it) { KURL url = KURL::fromPathOrURL(*it); if (url.isValid()) slotOpenNewTab(url, true); // open in background } } void View::saveProperties(TDEConfig* config) { // save filter settings config->writeEntry("searchLine", m_searchBar->text()); config->writeEntry("searchCombo", m_searchBar->status()); TreeNode* sel = m_listTabWidget->activeView()->selectedNode(); if (sel) { config->writeEntry("selectedNodeID", sel->id() ); } // save browser URLs TQStringList urls; TQPtrList frames = m_tabs->frames(); TQPtrList::ConstIterator it = frames.begin(); for (; it != frames.end(); ++it) { Frame *frame = *it; KParts::ReadOnlyPart *part = frame->part(); PageViewer *pageViewer = dynamic_cast(part); // don't save the ArticleViewer if (pageViewer) { KURL url = pageViewer->url(); if (url.isValid()) urls.append(url.prettyURL()); } } config->writeEntry("FeedBrowserURLs", urls); } void View::connectToFeedList(FeedList* feedList) { connect(feedList->rootNode(), TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotSetTotalUnread())); slotSetTotalUnread(); } void View::disconnectFromFeedList(FeedList* feedList) { disconnect(feedList->rootNode(), TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotSetTotalUnread())); } void View::updateTagActions() { TQStringList tags; TQValueList
selectedArticles = m_articleList->selectedArticles(); for (TQValueList
::ConstIterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it) { TQStringList atags = (*it).tags(); for (TQStringList::ConstIterator it2 = atags.begin(); it2 != atags.end(); ++it2) { if (!tags.contains(*it2)) tags += *it2; } } m_actionManager->slotUpdateTagActions(!selectedArticles.isEmpty(), tags); } } // namespace Akregator #include "akregator_view.moc"