akregator/src

akregator_view.cpp
1 /*
2  This file is part of Akregator.
3 
4  Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net>
5  2004 Sashmit Bhaduri <smt@vfemail.net>
6  2005 Frank Osterfeld <frank.osterfeld at kdemail.net>
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 
22  As a special exception, permission is given to link this program
23  with any edition of TQt, and distribute the resulting executable,
24  without including the source code for TQt in the source distribution.
25 */
26 
27 #include "actionmanagerimpl.h"
28 #include "akregator_part.h"
29 #include "akregator_run.h"
30 #include "akregator_view.h"
31 #include "listtabwidget.h"
32 #include "addfeeddialog.h"
33 #include "propertiesdialog.h"
34 #include "frame.h"
35 #include "fetchqueue.h"
36 #include "feedlistview.h"
37 #include "articlelistview.h"
38 #include "articleviewer.h"
39 #include "viewer.h"
40 #include "feed.h"
41 #include "tagfolder.h"
42 #include "folder.h"
43 #include "feedlist.h"
44 #include "akregatorconfig.h"
45 #include "kernel.h"
46 #include "pageviewer.h"
47 #include "searchbar.h"
48 #include "speechclient.h"
49 #include "storage.h"
50 #include "tabwidget.h"
51 #include "tag.h"
52 #include "tagset.h"
53 #include "tagnode.h"
54 #include "tagnodelist.h"
55 #include "tagpropertiesdialog.h"
56 #include "treenode.h"
57 #include "progressmanager.h"
58 #include "treenodevisitor.h"
59 #include "notificationmanager.h"
60 
61 #include <tdeaction.h>
62 #include <tdeapplication.h>
63 #include <kcharsets.h>
64 #include <kcombobox.h>
65 #include <tdeconfig.h>
66 #include <kdebug.h>
67 #include <kdialog.h>
68 #include <tdefiledialog.h>
69 #include <tdefileitem.h>
70 #include <tdehtml_part.h>
71 #include <tdehtmlview.h>
72 #include <kiconloader.h>
73 #include <kinputdialog.h>
74 #include <klineedit.h>
75 #include <tdelistview.h>
76 #include <tdelocale.h>
77 #include <tdemessagebox.h>
78 #include <kpassdlg.h>
79 #include <tdeprocess.h>
80 #include <krun.h>
81 #include <kshell.h>
82 #include <kstandarddirs.h>
83 #include <kurl.h>
84 #include <kxmlguifactory.h>
85 #include <tdeparts/partmanager.h>
86 
87 #include <tqbuttongroup.h>
88 #include <tqcheckbox.h>
89 #include <tqdatetime.h> // for startup time measure
90 #include <tqfile.h>
91 #include <tqhbox.h>
92 #include <tqlabel.h>
93 #include <tqlayout.h>
94 #include <tqmultilineedit.h>
95 #include <tqpopupmenu.h>
96 #include <tqptrlist.h>
97 #include <tqstylesheet.h>
98 #include <tqtextstream.h>
99 #include <tqtimer.h>
100 #include <tqtoolbutton.h>
101 #include <tqtooltip.h>
102 #include <tqvaluevector.h>
103 #include <tqwhatsthis.h>
104 #include <tqclipboard.h>
105 
106 namespace Akregator {
107 
108 class View::EditNodePropertiesVisitor : public TreeNodeVisitor
109 {
110  public:
111  EditNodePropertiesVisitor(View* view) : m_view(view) {}
112  virtual ~EditNodePropertiesVisitor() {}
113 
114  virtual bool visitTagNode(TagNode* node)
115  {
116  TagPropertiesDialog* dlg = new TagPropertiesDialog(m_view);
117  dlg->setTag(node->tag());
118  dlg->exec();
119  delete dlg;
120  return true;
121  }
122 
123  virtual bool visitFolder(Folder* node)
124  {
125  m_view->m_listTabWidget->activeView()->startNodeRenaming(node);
126  return true;
127  }
128 
129  virtual bool visitFeed(Feed* node)
130  {
131  FeedPropertiesDialog *dlg = new FeedPropertiesDialog( m_view, "edit_feed" );
132  dlg->setFeed(node);
133  dlg->exec();
134  delete dlg;
135  return true;
136  }
137  private:
138 
139  View* m_view;
140 };
141 
142 class View::DeleteNodeVisitor : public TreeNodeVisitor
143 {
144  public:
145  DeleteNodeVisitor(View* view) : m_view(view) {}
146  virtual ~DeleteNodeVisitor() {}
147 
148  virtual bool visitTagNode(TagNode* node)
149  {
150  TQString 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());
151  if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Tag"), KStdGuiItem::del()) == KMessageBox::Continue)
152  {
153  Tag tag = node->tag();
154  TQValueList<Article> articles = m_view->m_feedList->rootNode()->articles(tag.id());
155  node->setNotificationMode(false);
156  for (TQValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
157  (*it).removeTag(tag.id());
158  node->setNotificationMode(true);
159  Kernel::self()->tagSet()->remove(tag);
160  m_view->m_listTabWidget->activeView()->setFocus();
161  }
162  return true;
163  }
164 
165  virtual bool visitFolder(Folder* node)
166  {
167  TQString msg;
168  if (node->title().isEmpty())
169  msg = i18n("<qt>Are you sure you want to delete this folder and its feeds and subfolders?</qt>");
170  else
171  msg = i18n("<qt>Are you sure you want to delete folder <b>%1</b> and its feeds and subfolders?</qt>").arg(node->title());
172 
173  if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Folder"), KStdGuiItem::del()) == KMessageBox::Continue)
174  {
175  delete node;
176  m_view->m_listTabWidget->activeView()->setFocus();
177  }
178  return true;
179  }
180 
181  virtual bool visitFeed(Feed* node)
182  {
183  TQString msg;
184  if (node->title().isEmpty())
185  msg = i18n("<qt>Are you sure you want to delete this feed?</qt>");
186  else
187  msg = i18n("<qt>Are you sure you want to delete feed <b>%1</b>?</qt>").arg(node->title());
188 
189  if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Feed"), KStdGuiItem::del()) == KMessageBox::Continue)
190  {
191  delete node;
192  m_view->m_listTabWidget->activeView()->setFocus();
193  }
194  return true;
195  }
196  private:
197 
198  View* m_view;
199 };
200 
201 
203 {
204  // if m_shuttingDown is false, slotOnShutdown was not called. That
205  // means that not the whole app is shutdown, only the part. So it
206  // should be no risk to do the cleanups now
207  if (!m_shuttingDown)
208  {
209  kdDebug() << "View::~View(): slotOnShutdown() wasn't called. Calling it now." << endl;
210  slotOnShutdown();
211  }
212  kdDebug() << "View::~View(): leaving" << endl;
213 }
214 
215 View::View( Part *part, TQWidget *parent, ActionManagerImpl* actionManager, const char *name)
216  : TQWidget(parent, name), m_viewMode(NormalView), m_actionManager(actionManager)
217 {
218  m_editNodePropertiesVisitor = new EditNodePropertiesVisitor(this);
219  m_deleteNodeVisitor = new DeleteNodeVisitor(this);
220  m_keepFlagIcon = TQPixmap(locate("data", "akregator/pics/akregator_flag.png"));
221  m_part = part;
222  m_feedList = new FeedList();
223  m_tagNodeList = new TagNodeList(m_feedList, Kernel::self()->tagSet());
224  m_shuttingDown = false;
225  m_displayingAboutPage = false;
226  m_currentFrame = 0L;
227  setFocusPolicy(TQWidget::StrongFocus);
228 
229  TQVBoxLayout *lt = new TQVBoxLayout( this );
230 
231  m_horizontalSplitter = new TQSplitter(TQt::Horizontal, this);
232 
233  m_horizontalSplitter->setOpaqueResize(true);
234  lt->addWidget(m_horizontalSplitter);
235 
236  connect (Kernel::self()->fetchQueue(), TQ_SIGNAL(fetched(Feed*)), this, TQ_SLOT(slotFeedFetched(Feed*)));
237  connect (Kernel::self()->fetchQueue(), TQ_SIGNAL(signalStarted()), this, TQ_SLOT(slotFetchingStarted()));
238  connect (Kernel::self()->fetchQueue(), TQ_SIGNAL(signalStopped()), this, TQ_SLOT(slotFetchingStopped()));
239 
240  connect(Kernel::self()->tagSet(), TQ_SIGNAL(signalTagAdded(const Tag&)), this, TQ_SLOT(slotTagCreated(const Tag&)));
241  connect(Kernel::self()->tagSet(), TQ_SIGNAL(signalTagRemoved(const Tag&)), this, TQ_SLOT(slotTagRemoved(const Tag&)));
242 
243  m_listTabWidget = new ListTabWidget(m_horizontalSplitter);
244  m_actionManager->initListTabWidget(m_listTabWidget);
245 
246  connect(m_listTabWidget, TQ_SIGNAL(signalNodeSelected(TreeNode*)), this, TQ_SLOT(slotNodeSelected(TreeNode*)));
247 
248  if (!Settings::showTaggingGUI())
249  m_listTabWidget->setViewMode(ListTabWidget::single);
250 
251  m_feedListView = new NodeListView( this, "feedtree" );
252  m_listTabWidget->addView(m_feedListView, i18n("Feeds"), TDEGlobal::iconLoader()->loadIcon("folder", TDEIcon::Small));
253 
254  connect(m_feedListView, TQ_SIGNAL(signalContextMenu(TDEListView*, TreeNode*, const TQPoint&)), this, TQ_SLOT(slotFeedTreeContextMenu(TDEListView*, TreeNode*, const TQPoint&)));
255 
256  connect(m_feedListView, TQ_SIGNAL(signalDropped (KURL::List &, TreeNode*,
257  Folder*)), this, TQ_SLOT(slotFeedURLDropped (KURL::List &,
258  TreeNode*, Folder*)));
259 
260  m_tagNodeListView = new NodeListView(this);
261  m_listTabWidget->addView(m_tagNodeListView, i18n("Tags"), TDEGlobal::iconLoader()->loadIcon("rss_tag", TDEIcon::Small));
262 
263  connect(m_tagNodeListView, TQ_SIGNAL(signalContextMenu(TDEListView*, TreeNode*, const TQPoint&)), this, TQ_SLOT(slotFeedTreeContextMenu(TDEListView*, TreeNode*, const TQPoint&)));
264 
265 
266  ProgressManager::self()->setFeedList(m_feedList);
267 
268  m_tabs = new TabWidget(m_horizontalSplitter);
269  m_actionManager->initTabWidget(m_tabs);
270 
271  connect( m_part, TQ_SIGNAL(signalSettingsChanged()), m_tabs, TQ_SLOT(slotSettingsChanged()));
272 
273  connect( m_tabs, TQ_SIGNAL( currentFrameChanged(Frame *) ), this,
274  TQ_SLOT( slotFrameChanged(Frame *) ) );
275 
276  TQWhatsThis::add(m_tabs, i18n("You can view multiple articles in several open tabs."));
277 
278  m_mainTab = new TQWidget(this, "Article Tab");
279  TQVBoxLayout *mainTabLayout = new TQVBoxLayout( m_mainTab, 0, 2, "mainTabLayout");
280 
281  TQWhatsThis::add(m_mainTab, i18n("Articles list."));
282 
283  m_searchBar = new SearchBar(m_mainTab);
284 
285  if ( !Settings::showQuickFilter() )
286  m_searchBar->hide();
287 
288  mainTabLayout->addWidget(m_searchBar);
289 
290  m_articleSplitter = new TQSplitter(TQt::Vertical, m_mainTab, "panner2");
291 
292  m_articleList = new ArticleListView( m_articleSplitter, "articles" );
293  m_actionManager->initArticleListView(m_articleList);
294 
295  connect( m_articleList, TQ_SIGNAL(signalMouseButtonPressed(int, const Article&, const TQPoint &, int)), this, TQ_SLOT(slotMouseButtonPressed(int, const Article&, const TQPoint &, int)));
296 
297  // use selectionChanged instead of clicked
298  connect( m_articleList, TQ_SIGNAL(signalArticleChosen(const Article&)),
299  this, TQ_SLOT( slotArticleSelected(const Article&)) );
300  connect( m_articleList, TQ_SIGNAL(signalDoubleClicked(const Article&, const TQPoint&, int)),
301  this, TQ_SLOT( slotOpenArticleExternal(const Article&, const TQPoint&, int)) );
302 
303  m_articleViewer = new ArticleViewer(m_articleSplitter, "article_viewer");
304  m_articleViewer->setSafeMode(); // disable JS, Java, etc...
305 
306  m_actionManager->initArticleViewer(m_articleViewer);
307 
308  connect(m_searchBar, TQ_SIGNAL(signalSearch(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&)), m_articleList, TQ_SLOT(slotSetFilter(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&)));
309 
310  connect(m_searchBar, TQ_SIGNAL(signalSearch(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&)), m_articleViewer, TQ_SLOT(slotSetFilter(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&)));
311 
312  connect( m_articleViewer, TQ_SIGNAL(urlClicked(const KURL&, Viewer*, bool, bool)),
313  this, TQ_SLOT(slotUrlClickedInViewer(const KURL&, Viewer*, bool, bool)) );
314 
315  connect( m_articleViewer->browserExtension(), TQ_SIGNAL(mouseOverInfo(const KFileItem *)),
316  this, TQ_SLOT(slotMouseOverInfo(const KFileItem *)) );
317 
318  connect( m_part, TQ_SIGNAL(signalSettingsChanged()), m_articleViewer, TQ_SLOT(slotPaletteOrFontChanged()));
319  TQWhatsThis::add(m_articleViewer->widget(), i18n("Browsing area."));
320  mainTabLayout->addWidget( m_articleSplitter );
321 
322  m_mainFrame=new Frame(this, m_part, m_mainTab, i18n("Articles"), false);
323  connectFrame(m_mainFrame);
324  m_tabs->addFrame(m_mainFrame);
325 
326  const TQValueList<int> sp1sizes = Settings::splitter1Sizes();
327  if ( sp1sizes.count() >= m_horizontalSplitter->sizes().count() )
328  m_horizontalSplitter->setSizes( sp1sizes );
329  const TQValueList<int> sp2sizes = Settings::splitter2Sizes();
330  if ( sp2sizes.count() >= m_articleSplitter->sizes().count() )
331  m_articleSplitter->setSizes( sp2sizes );
332 
333  TDEConfig *conf = Settings::self()->config();
334  conf->setGroup("General");
335  if(!conf->readBoolEntry("Disable Introduction", false))
336  {
337  m_articleList->hide();
338  m_searchBar->hide();
339  m_articleViewer->displayAboutPage();
340  m_mainFrame->setTitle(i18n("About"));
341  m_displayingAboutPage = true;
342  }
343 
344  m_fetchTimer = new TQTimer(this);
345  connect( m_fetchTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotDoIntervalFetches()) );
346  m_fetchTimer->start(1000*60);
347 
348  // delete expired articles once per hour
349  m_expiryTimer = new TQTimer(this);
350  connect(m_expiryTimer, TQ_SIGNAL(timeout()), this,
351  TQ_SLOT(slotDeleteExpiredArticles()) );
352  m_expiryTimer->start(3600*1000);
353 
354  m_markReadTimer = new TQTimer(this);
355  connect(m_markReadTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotSetCurrentArticleReadDelayed()) );
356 
357  switch (Settings::viewMode())
358  {
359  case CombinedView:
361  break;
362  case WidescreenView:
364  break;
365  default:
366  slotNormalView();
367  }
368 
369  if (!Settings::resetQuickFilterOnNodeChange())
370  {
371  m_searchBar->slotSetStatus(Settings::statusFilter());
372  m_searchBar->slotSetText(Settings::textFilter());
373  }
374 
375  TQTimer::singleShot(1000, this, TQ_SLOT(slotDeleteExpiredArticles()) );
376  m_part->mergePart(m_articleViewer);
377 }
378 
379 void View::slotSettingsChanged()
380 {
381  // if tagging is hidden, show only feed list
382  m_listTabWidget->setViewMode(Settings::showTaggingGUI() ? ListTabWidget::verticalTabs : ListTabWidget::single);
383 
384  // In case text colors changed, repaint to apply changes immediately
385  if (m_articleList->isShown()) {
386  m_articleList->repaintContents();
387  }
388  if (m_feedListView->isShown()) {
389  m_feedListView->repaintContents();
390  }
391 }
392 
393 void View::slotOnShutdown()
394 {
395  m_shuttingDown = true; // prevents slotFrameChanged from crashing
396 
397  m_articleList->slotShowNode(0);
398  m_articleViewer->slotShowNode(0);
399 
400  Kernel::self()->fetchQueue()->slotAbort();
401 
402  m_feedListView->setNodeList(0);
403  ProgressManager::self()->setFeedList(0);
404 
405  delete m_feedList;
406  delete m_tagNodeList;
407 
408  // close all pageviewers in a controlled way
409  // fixes bug 91660, at least when no part loading data
410  m_tabs->setCurrentPage(m_tabs->count()-1); // select last page
411  while (m_tabs->count() > 1) // remove frames until only the main frame remains
412  m_tabs->slotRemoveCurrentFrame();
413 
414  delete m_mainTab;
415  delete m_mainFrame;
416  delete m_editNodePropertiesVisitor;
417  delete m_deleteNodeVisitor;
418 }
419 
421 {
422  const TQValueList<int> spl1 = m_horizontalSplitter->sizes();
423  if ( spl1.contains( 0 ) == 0 )
424  Settings::setSplitter1Sizes( spl1 );
425  const TQValueList<int> spl2 = m_articleSplitter->sizes();
426  if ( spl2.contains( 0 ) == 0 )
427  Settings::setSplitter2Sizes( spl2 );
428  Settings::setViewMode( m_viewMode );
429  Settings::writeConfig();
430 }
431 
432 void View::slotOpenNewTab(const KURL& url, bool background)
433 {
434  PageViewer* page = new PageViewer(this, "page");
435 
436  connect( m_part, TQ_SIGNAL(signalSettingsChanged()), page, TQ_SLOT(slotPaletteOrFontChanged()));
437 
438  connect( page, TQ_SIGNAL(setTabIcon(const TQPixmap&)),
439  this, TQ_SLOT(setTabIcon(const TQPixmap&)));
440  connect( page, TQ_SIGNAL(urlClicked(const KURL &, Viewer*, bool, bool)),
441  this, TQ_SLOT(slotUrlClickedInViewer(const KURL &, Viewer*, bool, bool)) );
442 
443  Frame* frame = new Frame(this, page, page->widget(), i18n("Untitled"));
444  frame->setAutoDeletePart(true); // delete page viewer when removing the tab
445 
446  connect(page, TQ_SIGNAL(setWindowCaption (const TQString &)), frame, TQ_SLOT(setTitle (const TQString &)));
447  connectFrame(frame);
448  m_tabs->addFrame(frame);
449 
450  if(!background)
451  m_tabs->showPage(page->widget());
452  else
453  setFocus();
454 
455  page->openURL(url);
456 }
457 
458 
459 void View::setTabIcon(const TQPixmap& icon)
460 {
461  const PageViewer *s = dynamic_cast<const PageViewer*>(sender());
462  if (s) {
463  m_tabs->setTabIconSet(const_cast<PageViewer*>(s)->widget(), icon);
464  }
465 }
466 
467 void View::connectFrame(Frame *f)
468 {
469  connect(f, TQ_SIGNAL(statusText(const TQString &)), this, TQ_SLOT(slotStatusText(const TQString&)));
470  connect(f, TQ_SIGNAL(captionChanged (const TQString &)), this, TQ_SLOT(slotCaptionChanged (const TQString &)));
471  connect(f, TQ_SIGNAL(loadingProgress(int)), this, TQ_SLOT(slotLoadingProgress(int)) );
472  connect(f, TQ_SIGNAL(started()), this, TQ_SLOT(slotStarted()));
473  connect(f, TQ_SIGNAL(completed()), this, TQ_SLOT(slotCompleted()));
474  connect(f, TQ_SIGNAL(canceled(const TQString &)), this, TQ_SLOT(slotCanceled(const TQString&)));
475 }
476 
477 void View::slotStatusText(const TQString &c)
478 {
479  if (sender() == m_currentFrame)
480  emit setStatusBarText(c);
481 }
482 
483 void View::slotCaptionChanged(const TQString &c)
484 {
485  if (sender() == m_currentFrame)
486  emit setWindowCaption(c);
487 }
488 
489 void View::slotStarted()
490 {
491  if (sender() == m_currentFrame)
492  emit signalStarted(0);
493 }
494 
495 void View::slotCanceled(const TQString &s)
496 {
497  if (sender() == m_currentFrame)
498  emit signalCanceled(s);
499 }
500 
501 void View::slotCompleted()
502 {
503  if (sender() == m_currentFrame)
504  emit signalCompleted();
505 }
506 
507 void View::slotLoadingProgress(int percent)
508 {
509  if (sender() == m_currentFrame)
510  emit setProgress(percent);
511 }
512 
513 bool View::importFeeds(const TQDomDocument& doc)
514 {
515  FeedList* feedList = new FeedList();
516  bool parsed = feedList->readFromXML(doc);
517 
518  // FIXME: parsing error, print some message
519  if (!parsed)
520  {
521  delete feedList;
522  return false;
523  }
524  TQString title = feedList->title();
525 
526  if (title.isEmpty())
527  title = i18n("Imported Folder");
528 
529  bool ok;
530  title = KInputDialog::getText(i18n("Add Imported Folder"), i18n("Imported folder name:"), title, &ok);
531 
532  if (!ok)
533  {
534  delete feedList;
535  return false;
536  }
537 
538  Folder* fg = new Folder(title);
539  m_feedList->rootNode()->appendChild(fg);
540  m_feedList->append(feedList, fg);
541 
542  return true;
543 }
544 
545 bool View::loadFeeds(const TQDomDocument& doc, Folder* parent)
546 {
547  FeedList* feedList = new FeedList();
548  bool parsed = feedList->readFromXML(doc);
549 
550  // parsing went wrong
551  if (!parsed)
552  {
553  delete feedList;
554  return false;
555  }
556  m_feedListView->setUpdatesEnabled(false);
557  m_tagNodeListView->setUpdatesEnabled(false);
558  if (!parent)
559  {
560  TagSet* tagSet = Kernel::self()->tagSet();
561 
562  Kernel::self()->setFeedList(feedList);
563  ProgressManager::self()->setFeedList(feedList);
564  disconnectFromFeedList(m_feedList);
565  delete m_feedList;
566  delete m_tagNodeList;
567  m_feedList = feedList;
568  connectToFeedList(m_feedList);
569 
570  m_tagNodeList = new TagNodeList(m_feedList, tagSet);
571  m_feedListView->setNodeList(m_feedList);
572  m_tagNodeListView->setNodeList(m_tagNodeList);
573 
574  TQStringList tagIDs = m_feedList->rootNode()->tags();
575  TQStringList::ConstIterator end = tagIDs.end();
576  for (TQStringList::ConstIterator it = tagIDs.begin(); it != end; ++it)
577  {
578  kdDebug() << *it << endl;
579  // create a tag for every tag ID in the archive that is not part of the tagset
580  // this is a fallback in case the tagset was corrupted,
581  // so the tagging information from archive does not get lost.
582  if (!tagSet->containsID(*it))
583  {
584  Tag tag(*it, *it);
585  tagSet->insert(tag);
586  }
587  }
588  }
589  else
590  m_feedList->append(feedList, parent);
591 
592  m_feedListView->setUpdatesEnabled(true);
593  m_feedListView->triggerUpdate();
594  m_tagNodeListView->setUpdatesEnabled(true);
595  m_tagNodeListView->triggerUpdate();
596  return true;
597 }
598 
599 void View::slotDeleteExpiredArticles()
600 {
601  TreeNode* rootNode = m_feedList->rootNode();
602  if (rootNode)
603  rootNode->slotDeleteExpiredArticles();
604 }
605 
606 TQDomDocument View::feedListToOPML()
607 {
608  return m_feedList->toXML();
609 }
610 
611 void View::addFeedToGroup(const TQString& url, const TQString& groupName)
612 {
613 
614  // Locate the group.
615  TreeNode* node = m_feedListView->findNodeByTitle(groupName);
616 
617  Folder* group = 0;
618  if (!node || !node->isGroup())
619  {
620  Folder* g = new Folder( groupName );
621  m_feedList->rootNode()->appendChild(g);
622  group = g;
623  }
624  else
625  group = static_cast<Folder*>(node);
626 
627  // Invoke the Add Feed dialog with url filled in.
628  if (group)
629  addFeed(url, 0, group, true);
630 }
631 
633 {
634  if (m_viewMode == NormalView)
635  return;
636 
637  if (m_viewMode == CombinedView)
638  {
639  m_articleList->slotShowNode(m_listTabWidget->activeView()->selectedNode());
640  m_articleList->show();
641 
642  Article article = m_articleList->currentArticle();
643 
644  if (!article.isNull())
645  m_articleViewer->slotShowArticle(article);
646  else
647  m_articleViewer->slotShowSummary(m_listTabWidget->activeView()->selectedNode());
648  }
649 
650  m_articleSplitter->setOrientation(TQt::Vertical);
651  m_viewMode = NormalView;
652 
653  Settings::setViewMode( m_viewMode );
654 }
655 
657 {
658  if (m_viewMode == WidescreenView)
659  return;
660 
661  if (m_viewMode == CombinedView)
662  {
663  m_articleList->slotShowNode(m_listTabWidget->activeView()->selectedNode());
664  m_articleList->show();
665 
666  Article article = m_articleList->currentArticle();
667 
668  if (!article.isNull())
669  m_articleViewer->slotShowArticle(article);
670  else
671  m_articleViewer->slotShowSummary(m_listTabWidget->activeView()->selectedNode());
672  }
673 
674  m_articleSplitter->setOrientation(TQt::Horizontal);
675  m_viewMode = WidescreenView;
676 
677  Settings::setViewMode( m_viewMode );
678 }
679 
681 {
682  if (m_viewMode == CombinedView)
683  return;
684 
685  m_articleList->slotClear();
686  m_articleList->hide();
687  m_viewMode = CombinedView;
688 
689  slotNodeSelected(m_listTabWidget->activeView()->selectedNode());
690  Settings::setViewMode( m_viewMode );
691 }
692 
694 {
695  if (m_shuttingDown)
696  return;
697 
698  m_currentFrame=f;
699 
700  emit setWindowCaption(f->caption());
701  emit setProgress(f->progress());
702  emit setStatusBarText(f->statusText());
703 
704  if (f->part() == m_part)
705  m_part->mergePart(m_articleViewer);
706  else
707  m_part->mergePart(f->part());
708 
709  f->widget()->setFocus();
710 
711  switch (f->state())
712  {
713  case Frame::Started:
714  emit signalStarted(0);
715  break;
716  case Frame::Canceled:
717  emit signalCanceled(TQString());
718  break;
719  case Frame::Idle:
720  case Frame::Completed:
721  default:
722  emit signalCompleted();
723  }
724 }
725 
726 void View::slotFeedTreeContextMenu(TDEListView*, TreeNode* /*node*/, const TQPoint& /*p*/)
727 {
728  m_tabs->showPage(m_mainTab);
729 }
730 
731 void View::slotMoveCurrentNodeUp()
732 {
733  TreeNode* current = m_listTabWidget->activeView()->selectedNode();
734  if (!current)
735  return;
736  TreeNode* prev = current->prevSibling();
737  Folder* parent = current->parent();
738 
739  if (!prev || !parent)
740  return;
741 
742  parent->removeChild(prev);
743  parent->insertChild(prev, current);
744  m_listTabWidget->activeView()->ensureNodeVisible(current);
745 }
746 
747 void View::slotMoveCurrentNodeDown()
748 {
749  TreeNode* current = m_listTabWidget->activeView()->selectedNode();
750  if (!current)
751  return;
752  TreeNode* next = current->nextSibling();
753  Folder* parent = current->parent();
754 
755  if (!next || !parent)
756  return;
757 
758  parent->removeChild(current);
759  parent->insertChild(current, next);
760  m_listTabWidget->activeView()->ensureNodeVisible(current);
761 }
762 
763 void View::slotMoveCurrentNodeLeft()
764 {
765  TreeNode* current = m_listTabWidget->activeView()->selectedNode();
766  if (!current || !current->parent() || !current->parent()->parent())
767  return;
768 
769  Folder* parent = current->parent();
770  Folder* grandparent = current->parent()->parent();
771 
772  parent->removeChild(current);
773  grandparent->insertChild(current, parent);
774  m_listTabWidget->activeView()->ensureNodeVisible(current);
775 }
776 
777 void View::slotMoveCurrentNodeRight()
778 {
779  TreeNode* current = m_listTabWidget->activeView()->selectedNode();
780  if (!current || !current->parent())
781  return;
782  TreeNode* prev = current->prevSibling();
783 
784  if ( prev && prev->isGroup() )
785  {
786  Folder* fg = static_cast<Folder*>(prev);
787  current->parent()->removeChild(current);
788  fg->appendChild(current);
789  m_listTabWidget->activeView()->ensureNodeVisible(current);
790  }
791 }
792 
794 {
795  m_markReadTimer->stop();
796 
797  if (node)
798  {
799  kdDebug() << "node selected: " << node->title() << endl;
800  kdDebug() << "unread: " << node->unread() << endl;
801  kdDebug() << "total: " << node->totalCount() << endl;
802  }
803 
804  if (m_displayingAboutPage)
805  {
806  m_mainFrame->setTitle(i18n("Articles"));
807  if (m_viewMode != CombinedView)
808  m_articleList->show();
809  if (Settings::showQuickFilter())
810  m_searchBar->show();
811  m_displayingAboutPage = false;
812  }
813 
814  m_tabs->showPage(m_mainTab);
815 
816  if (Settings::resetQuickFilterOnNodeChange())
817  m_searchBar->slotClearSearch();
818 
819  if (m_viewMode == CombinedView)
820  m_articleViewer->slotShowNode(node);
821  else
822  {
823  m_articleList->slotShowNode(node);
824  m_articleViewer->slotShowSummary(node);
825  }
826 
827  if (node)
828  m_mainFrame->setCaption(node->title());
829 
830  m_actionManager->slotNodeSelected(node);
831 
832  updateTagActions();
833 }
834 
835 void View::slotOpenURL(const KURL& url, Viewer* currentViewer, BrowserRun::OpeningMode mode)
836 {
837  if (mode == BrowserRun::EXTERNAL)
838  Viewer::displayInExternalBrowser(url);
839  else
840  {
841  KParts::URLArgs args = currentViewer ? currentViewer->browserExtension()->urlArgs() : KParts::URLArgs();
842 
843  BrowserRun* r = new BrowserRun(this, currentViewer, url, args, mode);
844  connect(r, TQ_SIGNAL(signalOpenInViewer(const KURL&, Akregator::Viewer*, Akregator::BrowserRun::OpeningMode)),
845  this, TQ_SLOT(slotOpenURLReply(const KURL&, Akregator::Viewer*, Akregator::BrowserRun::OpeningMode)));
846  }
847 }
848 
849 //TODO: KDE4 remove this ugly ugly hack
850 void View::slotUrlClickedInViewer(const KURL& url, Viewer* viewer, bool newTab, bool background)
851 {
852 
853  if (!newTab)
854  {
855  slotOpenURL(url, viewer, BrowserRun::CURRENT_TAB);
856  }
857  else
858  {
859  slotOpenURL(url, 0L, background ? BrowserRun::NEW_TAB_BACKGROUND : BrowserRun::NEW_TAB_FOREGROUND);
860  }
861 }
862 
863 //TODO: KDE4 remove this ugly ugly hack
864 void View::slotOpenURLReply(const KURL& url, Viewer* currentViewer, BrowserRun::OpeningMode mode)
865 {
866  switch (mode)
867  {
868  case BrowserRun::CURRENT_TAB:
869  currentViewer->openURL(url);
870  break;
871  case BrowserRun::NEW_TAB_FOREGROUND:
872  case BrowserRun::NEW_TAB_BACKGROUND:
873  slotOpenNewTab(url, mode == BrowserRun::NEW_TAB_BACKGROUND);
874  break;
875  case BrowserRun::EXTERNAL:
876  Viewer::displayInExternalBrowser(url);
877  break;
878  }
879 }
880 
882 {
883  Folder* group = 0;
884  if (!m_feedListView->selectedNode())
885  group = m_feedList->rootNode(); // all feeds
886  else
887  {
888  //TODO: tag nodes need rework
889  if ( m_feedListView->selectedNode()->isGroup())
890  group = static_cast<Folder*>(m_feedListView->selectedNode());
891  else
892  group= m_feedListView->selectedNode()->parent();
893 
894  }
895 
896  TreeNode* lastChild = group->children().last();
897 
898  addFeed(TQString(), lastChild, group, false);
899 }
900 
901 void View::addFeed(const TQString& url, TreeNode *after, Folder* parent, bool autoExec)
902 {
903 
904  AddFeedDialog *afd = new AddFeedDialog( 0, "add_feed" );
905 
906  afd->setURL(KURL::decode_string(url));
907 
908  if (autoExec)
909  afd->slotOk();
910  else
911  {
912  if (afd->exec() != TQDialog::Accepted)
913  {
914  delete afd;
915  return;
916  }
917  }
918 
919  Feed* feed = afd->feed;
920  delete afd;
921 
922  FeedPropertiesDialog *dlg = new FeedPropertiesDialog( 0, "edit_feed" );
923  dlg->setFeed(feed);
924 
925  dlg->selectFeedName();
926 
927  if (!autoExec)
928  if (dlg->exec() != TQDialog::Accepted)
929  {
930  delete feed;
931  delete dlg;
932  return;
933  }
934 
935  if (!parent)
936  parent = m_feedList->rootNode();
937 
938  parent->insertChild(feed, after);
939 
940  m_feedListView->ensureNodeVisible(feed);
941 
942 
943  delete dlg;
944 }
945 
947 {
948  TreeNode* node = m_feedListView->selectedNode();
949  TreeNode* after = 0;
950 
951  if (!node)
952  node = m_feedListView->rootNode();
953 
954  // if a feed is selected, add group next to it
955  //TODO: tag nodes need rework
956  if (!node->isGroup())
957  {
958  after = node;
959  node = node->parent();
960  }
961 
962  Folder* currentGroup = static_cast<Folder*> (node);
963 
964  bool Ok;
965 
966  TQString text = KInputDialog::getText(i18n("Add Folder"), i18n("Folder name:"), "", &Ok);
967 
968  if (Ok)
969  {
970  Folder* newGroup = new Folder(text);
971  if (!after)
972  currentGroup->appendChild(newGroup);
973  else
974  currentGroup->insertChild(newGroup, after);
975 
976  m_feedListView->ensureNodeVisible(newGroup);
977  }
978 }
979 
981 {
982  TreeNode* selectedNode = m_listTabWidget->activeView()->selectedNode();
983 
984  // don't delete root element! (safety valve)
985  if (!selectedNode || selectedNode == m_feedList->rootNode())
986  return;
987 
988  m_deleteNodeVisitor->visit(selectedNode);
989 }
990 
992 {
993  TreeNode* node = m_listTabWidget->activeView()->selectedNode();
994  if (node)
995  m_editNodePropertiesVisitor->visit(node);
996 
997 }
998 
1000 {
1001  if (m_viewMode == CombinedView)
1002  m_listTabWidget->activeView()->slotNextUnreadFeed();
1003 
1004  TreeNode* sel = m_listTabWidget->activeView()->selectedNode();
1005  if (sel && sel->unread() > 0)
1006  m_articleList->slotNextUnreadArticle();
1007  else
1008  m_listTabWidget->activeView()->slotNextUnreadFeed();
1009 }
1010 
1012 {
1013  if (m_viewMode == CombinedView)
1014  m_listTabWidget->activeView()->slotPrevUnreadFeed();
1015 
1016  TreeNode* sel = m_listTabWidget->activeView()->selectedNode();
1017  if (sel && sel->unread() > 0)
1018  m_articleList->slotPreviousUnreadArticle();
1019  else
1020  m_listTabWidget->activeView()->slotPrevUnreadFeed();
1021 }
1022 
1024 {
1025  m_feedList->rootNode()->slotMarkAllArticlesAsRead();
1026 }
1027 
1029 {
1030  if(!m_listTabWidget->activeView()->selectedNode()) return;
1031  m_listTabWidget->activeView()->selectedNode()->slotMarkAllArticlesAsRead();
1032 }
1033 
1035 {
1036  Feed* feed = dynamic_cast<Feed *>(m_listTabWidget->activeView()->selectedNode());
1037 
1038  if (!feed)
1039  return;
1040 
1041  KURL url = KURL(feed->htmlUrl())
1042 ;
1043  switch (Settings::lMBBehaviour())
1044  {
1045  case Settings::EnumLMBBehaviour::OpenInExternalBrowser:
1046  slotOpenURL(url, 0, BrowserRun::EXTERNAL);
1047  break;
1048  case Settings::EnumLMBBehaviour::OpenInBackground:
1049  slotOpenURL(url, 0, BrowserRun::NEW_TAB_BACKGROUND);
1050  break;
1051  default:
1052  slotOpenURL(url, 0, BrowserRun::NEW_TAB_FOREGROUND);
1053  }
1054 }
1055 
1057 {
1058  emit signalUnreadCountChanged( m_feedList->rootNode()->unread() );
1059 }
1060 
1061 void View::slotDoIntervalFetches()
1062 {
1063  m_feedList->rootNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue(), true);
1064 }
1065 
1067 {
1068  if ( !m_listTabWidget->activeView()->selectedNode() )
1069  return;
1070  m_listTabWidget->activeView()->selectedNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue());
1071 }
1072 
1074 {
1075  m_feedList->rootNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue());
1076 }
1077 
1078 void View::slotFetchingStarted()
1079 {
1080  m_mainFrame->setState(Frame::Started);
1081  m_actionManager->action("feed_stop")->setEnabled(true);
1082  m_mainFrame->setStatusText(i18n("Fetching Feeds..."));
1083 }
1084 
1085 void View::slotFetchingStopped()
1086 {
1087  m_mainFrame->setState(Frame::Completed);
1088  m_actionManager->action("feed_stop")->setEnabled(false);
1089  m_mainFrame->setStatusText(TQString());
1090 }
1091 
1093 {
1094  // iterate through the articles (once again) to do notifications properly
1095  if (feed->articles().count() > 0)
1096  {
1097  TQValueList<Article> articles = feed->articles();
1098  TQValueList<Article>::ConstIterator it;
1099  TQValueList<Article>::ConstIterator end = articles.end();
1100  for (it = articles.begin(); it != end; ++it)
1101  {
1102  if ((*it).status()==Article::New && ((*it).feed()->useNotification() || Settings::useNotifications()))
1103  {
1105  }
1106  }
1107  }
1108 }
1109 
1110 void View::slotMouseButtonPressed(int button, const Article& article, const TQPoint &, int)
1111 {
1112  if (button == TQt::MidButton)
1113  {
1114  KURL link = article.link();
1115  switch (Settings::mMBBehaviour())
1116  {
1117  case Settings::EnumMMBBehaviour::OpenInExternalBrowser:
1118  slotOpenURL(link, 0L, BrowserRun::EXTERNAL);
1119  break;
1120  case Settings::EnumMMBBehaviour::OpenInBackground:
1121  slotOpenURL(link, 0L, BrowserRun::NEW_TAB_BACKGROUND);
1122  break;
1123  default:
1124  slotOpenURL(link, 0L, BrowserRun::NEW_TAB_FOREGROUND);
1125  }
1126  }
1127 }
1128 
1129 void View::slotAssignTag(const Tag& tag, bool assign)
1130 {
1131  kdDebug() << (assign ? "assigned" : "removed") << " tag \"" << tag.id() << "\"" << endl;
1132  TQValueList<Article> selectedArticles = m_articleList->selectedArticles();
1133  for (TQValueList<Article>::Iterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it)
1134  {
1135  if (assign)
1136  (*it).addTag(tag.id());
1137  else
1138  (*it).removeTag(tag.id());
1139  }
1140  updateTagActions();
1141 }
1142 /*
1143 void View::slotRemoveTag(const Tag& tag)
1144 {
1145  kdDebug() << "remove tag \"" << tag.id() << "\" from selected articles" << endl;
1146  TQValueList<Article> selectedArticles = m_articleList->selectedArticles();
1147  for (TQValueList<Article>::Iterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it)
1148  (*it).removeTag(tag.id());
1149 
1150  updateTagActions();
1151 }
1152 */
1153 void View::slotNewTag()
1154 {
1155  Tag tag(TDEApplication::randomString(8), "New Tag");
1156  Kernel::self()->tagSet()->insert(tag);
1157  TagNode* node = m_tagNodeList->findByTagID(tag.id());
1158  if (node)
1159  m_tagNodeListView->startNodeRenaming(node);
1160 }
1161 
1162 void View::slotTagCreated(const Tag& tag)
1163 {
1164  if (m_tagNodeList && !m_tagNodeList->containsTagId(tag.id()))
1165  {
1166  TagNode* tagNode = new TagNode(tag, m_feedList->rootNode());
1167  m_tagNodeList->rootNode()->appendChild(tagNode);
1168  }
1169 }
1170 
1171 void View::slotTagRemoved(const Tag& /*tag*/)
1172 {
1173 }
1174 
1176 {
1177  if (m_viewMode == CombinedView)
1178  return;
1179 
1180  m_markReadTimer->stop();
1181 
1182  Feed *feed = article.feed();
1183  if (!feed)
1184  return;
1185 
1186  Article a(article);
1187  if (a.status() != Article::Read)
1188  {
1189  int delay;
1190 
1191  if ( Settings::useMarkReadDelay() )
1192  {
1193  delay = Settings::markReadDelay();
1194 
1195  if (delay > 0)
1196  m_markReadTimer->start( delay*1000, true );
1197  else
1198  a.setStatus(Article::Read);
1199  }
1200  }
1201 
1202  TDEToggleAction* maai = dynamic_cast<TDEToggleAction*>(m_actionManager->action("article_set_status_important"));
1203  maai->setChecked(a.keep());
1204 
1205  kdDebug() << "selected: " << a.guid() << endl;
1206 
1207  updateTagActions();
1208 
1209  m_articleViewer->slotShowArticle(a);
1210 }
1211 
1212 void View::slotOpenArticleExternal(const Article& article, const TQPoint&, int)
1213 {
1214  if (!article.isNull())
1215  Viewer::displayInExternalBrowser(article.link());
1216 }
1217 
1218 
1220 {
1221  Article article = m_articleList->currentArticle();
1222 
1223  if (article.isNull())
1224  return;
1225 
1226  KURL link;
1227  if (article.link().isValid())
1228  link = article.link();
1229  else if (article.guidIsPermaLink())
1230  link = KURL(article.guid());
1231 
1232  if (link.isValid())
1233  {
1234  slotOpenURL(link, 0L, BrowserRun::NEW_TAB_FOREGROUND);
1235  }
1236 }
1237 
1239 {
1240  slotOpenArticleExternal(m_articleList->currentArticle(), TQPoint(), 0);
1241 }
1242 
1244 {
1245  Article article = m_articleList->currentArticle();
1246 
1247  if (article.isNull())
1248  return;
1249 
1250  KURL link;
1251 
1252  if (article.link().isValid())
1253  link = article.link();
1254  else if (article.guidIsPermaLink())
1255  link = KURL(article.guid());
1256 
1257  if (link.isValid())
1258  {
1259  slotOpenURL(link, 0L, BrowserRun::NEW_TAB_BACKGROUND);
1260  }
1261 }
1262 
1264 {
1265  Article article = m_articleList->currentArticle();
1266 
1267  if(article.isNull())
1268  return;
1269 
1270  TQString link;
1271  if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid()))
1272  {
1273  // in case link isn't valid, fall back to the guid permaLink.
1274  if (article.link().isValid())
1275  link = article.link().url();
1276  else
1277  link = article.guid();
1278  TQClipboard *cb = TQApplication::clipboard();
1279  cb->setText(link, TQClipboard::Clipboard);
1280  cb->setText(link, TQClipboard::Selection);
1281  }
1282 }
1283 
1284 void View::slotFeedURLDropped(KURL::List &urls, TreeNode* after, Folder* parent)
1285 {
1286  KURL::List::iterator it;
1287  for ( it = urls.begin(); it != urls.end(); ++it )
1288  {
1289  addFeed((*it).prettyURL(), after, parent, false);
1290  }
1291 }
1292 
1294 {
1295  if ( Settings::showQuickFilter() )
1296  {
1297  Settings::setShowQuickFilter(false);
1298  m_searchBar->slotClearSearch();
1299  m_searchBar->hide();
1300  }
1301  else
1302  {
1303  Settings::setShowQuickFilter(true);
1304  if (!m_displayingAboutPage)
1305  m_searchBar->show();
1306  }
1307 
1308 }
1309 
1311 {
1312 
1313  if ( m_viewMode == CombinedView )
1314  return;
1315 
1316  TQValueList<Article> articles = m_articleList->selectedArticles();
1317 
1318  TQString msg;
1319  switch (articles.count())
1320  {
1321  case 0:
1322  return;
1323  case 1:
1324  msg = i18n("<qt>Are you sure you want to delete article <b>%1</b>?</qt>").arg(TQStyleSheet::escape(articles.first().title()));
1325  break;
1326  default:
1327  msg = i18n("<qt>Are you sure you want to delete the selected article?</qt>",
1328  "<qt>Are you sure you want to delete the %n selected articles?</qt>",
1329  articles.count());
1330  }
1331 
1332  if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Article"), KStdGuiItem::del()) == KMessageBox::Continue)
1333  {
1334  if (m_listTabWidget->activeView()->selectedNode())
1335  m_listTabWidget->activeView()->selectedNode()->setNotificationMode(false);
1336 
1337  TQValueList<Feed*> feeds;
1338  for (TQValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
1339  {
1340  Feed* feed = (*it).feed();
1341  if (!feeds.contains(feed))
1342  feeds.append(feed);
1343  feed->setNotificationMode(false);
1344  (*it).setDeleted();
1345  }
1346 
1347  for (TQValueList<Feed*>::Iterator it = feeds.begin(); it != feeds.end(); ++it)
1348  {
1349  (*it)->setNotificationMode(true);
1350  }
1351 
1352  if (m_listTabWidget->activeView()->selectedNode())
1353  m_listTabWidget->activeView()->selectedNode()->setNotificationMode(true);
1354  }
1355 }
1356 
1357 
1358 void View::slotArticleToggleKeepFlag(bool /*enabled*/)
1359 {
1360  TQValueList<Article> articles = m_articleList->selectedArticles();
1361 
1362  if (articles.isEmpty())
1363  return;
1364 
1365  bool allFlagsSet = true;
1366  for (TQValueList<Article>::Iterator it = articles.begin(); allFlagsSet && it != articles.end(); ++it)
1367  if (!(*it).keep())
1368  allFlagsSet = false;
1369 
1370  for (TQValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
1371  (*it).setKeep(!allFlagsSet);
1372 }
1373 
1375 {
1376  TQValueList<Article> articles = m_articleList->selectedArticles();
1377 
1378  if (articles.isEmpty())
1379  return;
1380 
1381  for (TQValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
1382  (*it).setStatus(Article::Read);
1383 }
1384 
1386 {
1387  if (m_currentFrame == m_mainFrame)
1388  {
1389  if (m_viewMode != CombinedView)
1390  {
1391  // in non-combined view, read selected articles
1392  SpeechClient::self()->slotSpeak(m_articleList->selectedArticles());
1393  // TODO: if article viewer has a selection, read only the selected text?
1394  }
1395  else
1396  {
1397  if (m_listTabWidget->activeView()->selectedNode())
1398  {
1399  //TODO: read articles in current node, respecting quick filter!
1400  }
1401  }
1402  }
1403  else
1404  {
1405  TQString selectedText = static_cast<PageViewer *>(m_currentFrame->part())->selectedText();
1406 
1407  if (!selectedText.isEmpty())
1408  SpeechClient::self()->slotSpeak(selectedText, "en");
1409  }
1410 }
1411 
1413 {
1414  TQValueList<Article> articles = m_articleList->selectedArticles();
1415 
1416  if (articles.isEmpty())
1417  return;
1418 
1419  for (TQValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
1420  (*it).setStatus(Article::Unread);
1421 }
1422 
1424 {
1425  TQValueList<Article> articles = m_articleList->selectedArticles();
1426 
1427  if (articles.isEmpty())
1428  return;
1429 
1430  for (TQValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it)
1431  (*it).setStatus(Article::New);
1432 }
1433 
1435 {
1436  Article article = m_articleList->currentArticle();
1437 
1438  if (article.isNull())
1439  return;
1440 
1441  article.setStatus(Article::Read);
1442 }
1443 
1444 void View::slotMouseOverInfo(const KFileItem *kifi)
1445 {
1446  if (kifi)
1447  {
1448  KFileItem *k=(KFileItem*)kifi;
1449  m_mainFrame->setStatusText(k->url().prettyURL());//geStatusBarInfo());
1450  }
1451  else
1452  {
1453  m_mainFrame->setStatusText(TQString());
1454  }
1455 }
1456 
1457 void View::readProperties(TDEConfig* config)
1458 {
1459 
1460  if (!Settings::resetQuickFilterOnNodeChange())
1461  {
1462  m_searchBar->slotSetText(config->readEntry("searchLine"));
1463  int statusfilter = config->readNumEntry("searchCombo", -1);
1464  if (statusfilter != -1)
1465  m_searchBar->slotSetStatus(statusfilter);
1466  }
1467 
1468  int selectedID = config->readNumEntry("selectedNodeID", -1);
1469  if (selectedID != -1)
1470  {
1471  TreeNode* selNode = m_feedList->findByID(selectedID);
1472  if (selNode)
1473  m_listTabWidget->activeView()->setSelectedNode(selNode);
1474  }
1475 
1476  TQStringList urls = config->readListEntry("FeedBrowserURLs");
1477  TQStringList::ConstIterator it = urls.begin();
1478  for (; it != urls.end(); ++it)
1479  {
1480  KURL url = KURL::fromPathOrURL(*it);
1481  if (url.isValid())
1482  slotOpenNewTab(url, true); // open in background
1483  }
1484 }
1485 
1486 void View::saveProperties(TDEConfig* config)
1487 {
1488  // save filter settings
1489  config->writeEntry("searchLine", m_searchBar->text());
1490  config->writeEntry("searchCombo", m_searchBar->status());
1491 
1492  TreeNode* sel = m_listTabWidget->activeView()->selectedNode();
1493 
1494  if (sel)
1495  {
1496  config->writeEntry("selectedNodeID", sel->id() );
1497  }
1498 
1499  // save browser URLs
1500  TQStringList urls;
1501  TQPtrList<Frame> frames = m_tabs->frames();
1502  TQPtrList<Frame>::ConstIterator it = frames.begin();
1503  for (; it != frames.end(); ++it)
1504  {
1505  Frame *frame = *it;
1506  KParts::ReadOnlyPart *part = frame->part();
1507  PageViewer *pageViewer = dynamic_cast<PageViewer*>(part); // don't save the ArticleViewer
1508  if (pageViewer)
1509  {
1510  KURL url = pageViewer->url();
1511  if (url.isValid())
1512  urls.append(url.prettyURL());
1513  }
1514  }
1515 
1516  config->writeEntry("FeedBrowserURLs", urls);
1517 }
1518 
1519 void View::connectToFeedList(FeedList* feedList)
1520 {
1521  connect(feedList->rootNode(), TQ_SIGNAL(signalChanged(TreeNode*)), this, TQ_SLOT(slotSetTotalUnread()));
1523 }
1524 
1525 void View::disconnectFromFeedList(FeedList* feedList)
1526 {
1527  disconnect(feedList->rootNode(), TQ_SIGNAL(signalChanged(TreeNode*)), this, TQ_SLOT(slotSetTotalUnread()));
1528 }
1529 
1530 void View::updateTagActions()
1531 {
1532  TQStringList tags;
1533 
1534  TQValueList<Article> selectedArticles = m_articleList->selectedArticles();
1535 
1536  for (TQValueList<Article>::ConstIterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it)
1537  {
1538  TQStringList atags = (*it).tags();
1539  for (TQStringList::ConstIterator it2 = atags.begin(); it2 != atags.end(); ++it2)
1540  {
1541  if (!tags.contains(*it2))
1542  tags += *it2;
1543  }
1544  }
1545  m_actionManager->slotUpdateTagActions(!selectedArticles.isEmpty(), tags);
1546 }
1547 
1548 } // namespace Akregator
1549 
1550 #include "akregator_view.moc"
Akregator-specific implementation of the ActionManager interface.
void slotUpdateTagActions(bool enabled, const TQStringList &tagIds)
fills the remove tag menu with the given tags enables/disables tag menu action according to enabled
This HTML viewer is used to display articles.
Definition: articleviewer.h:49
void slotShowArticle(const Article &article)
Show single article (normal view)
void slotShowNode(TreeNode *node)
Shows the articles of the tree node node (combined view).
A proxy class for RSS::Article with some additional methods to assist sorting.
Definition: article.h:58
bool keep() const
if true, the article should be kept even when expired
Definition: article.cpp:387
The model of a feed tree, represents an OPML document.
Definition: feedlist.h:45
virtual TQDomDocument toXML() const
exports the feed list as OPML.
Definition: feedlist.cpp:238
virtual bool readFromXML(const TQDomDocument &doc)
reads an OPML document and appends the items to this list
Definition: feedlist.cpp:145
void append(FeedList *list, Folder *parent=0, TreeNode *after=0)
appends another feed list as sub tree.
Definition: feedlist.cpp:219
represents a feed
Definition: feed.h:63
virtual TQValueList< Article > articles(const TQString &tag=TQString())
Returns a sequence of the articles this node contains.
Definition: feed.cpp:192
const TQString & htmlUrl() const
returns the URL of the HTML page of this feed
Definition: feed.cpp:355
a powerful matcher supporting multiple criterions, which can be combined via logical OR or AND
Represents a folder (containing feeds and/or other folders)
Definition: folder.h:45
virtual void insertChild(TreeNode *node, TreeNode *after)
inserts node as child after child node after.
Definition: folder.cpp:138
virtual TQValueList< TreeNode * > children() const
returns the (direct) children of this node.
Definition: folder.cpp:133
virtual void removeChild(TreeNode *node)
remove node from children.
Definition: folder.cpp:202
virtual void appendChild(TreeNode *node)
inserts node as last child
Definition: folder.cpp:168
A widget containing multiple list views, e.g.
Definition: listtabwidget.h:46
void slotNotifyArticle(const Article &article)
notifies an article.
static NotificationManager * self()
singleton instance of notification manager
This is a RSS Aggregator "Part".
virtual bool mergePart(KParts::Part *)
merges a nested part's GUI into the gui of this part
represents a set of tags (see Tag) In an application, there is usually one central tag set that is us...
Definition: tagset.h:48
bool containsID(const TQString &id) const
returns true if this set contains a tag with a given ID
Definition: tagset.cpp:75
void insert(const Tag &tag)
adds a tag to the tag set.
Definition: tagset.cpp:55
Abstract base class for all kind of elements in the feed tree, like feeds and feed groups (and search...
Definition: treenode.h:52
virtual uint id() const
returns the ID of this node.
Definition: treenode.cpp:145
virtual bool isGroup() const =0
Helps the rest of the app to decide if node should be handled as group or not.
virtual int totalCount() const =0
returns the number of total articles in the node (for groups: the accumulated count of the subtree)
virtual TreeNode * prevSibling() const
Get the previous sibling.
Definition: treenode.cpp:104
virtual void setNotificationMode(bool doNotify, bool notifyOccurredChanges=true)
Definition: treenode.cpp:125
virtual int unread() const =0
The unread count, returns the number of new/unread articles in the node (for groups: the accumulated ...
virtual const TQString & title() const
Get title of node.
Definition: treenode.cpp:77
virtual Folder * parent() const
Returns the parent node.
Definition: treenode.cpp:115
virtual void slotDeleteExpiredArticles()=0
Deletes all expired articles in the node (depending on the expiry settings).
void slotMouseButtonPressed(int button, const Article &article, const TQPoint &pos, int c)
special behaviour in article list view (TODO: move code there?)
void slotFeedURLDropped(KURL::List &urls, TreeNode *after, Folder *parent)
called when URLs are dropped into the tree view
void slotFeedAdd()
adds a new feed to the feed tree
bool importFeeds(const TQDomDocument &doc)
Adds the feeds in doc to the "Imported Folder".
void slotUrlClickedInViewer(const KURL &url, Viewer *viewer, bool newTab, bool background)
HACK: part of the url opening hack for 3.5.
void slotSetSelectedArticleRead()
marks the currently selected article as read
void slotFetchCurrentFeed()
fetches the currently selected feed
void slotOpenCurrentArticle()
opens current article in new tab, background/foreground depends on settings TODO: use selected instea...
TQDomDocument feedListToOPML()
void slotMouseOverInfo(const KFileItem *kifi)
displays a URL in the status bar when the user moves the mouse over a link
void slotOpenCurrentArticleExternal()
opens the current article (currentItem) in external browser TODO: use selected instead of current?
void slotSetCurrentArticleReadDelayed()
marks the currenctly selected article as read after a user-set delay
virtual void readProperties(TDEConfig *config)
session management
void slotPrevUnreadArticle()
selects the previous unread article in the article list
void slotFeedTreeContextMenu(TDEListView *, TreeNode *, const TQPoint &)
Shows requested popup menu for feed tree.
void saveSettings()
saves settings.
void slotOpenArticleExternal(const Article &article, const TQPoint &, int)
opens article of item in external browser
void slotNodeSelected(TreeNode *node)
selected tree node has changed
void slotSetSelectedArticleUnread()
marks the currently selected article as unread
void slotArticleToggleKeepFlag(bool enabled)
toggles the keep flag of the currently selected article
View(Akregator::Part *part, TQWidget *parent, ActionManagerImpl *actionManager, const char *name)
constructor
void slotOpenCurrentArticleBackgroundTab()
opens the current article (currentItem) in background tab TODO: use selected instead of current?
void slotCopyLinkAddress()
copies the link of current article to clipboard
void slotSetSelectedArticleNew()
marks the currently selected article as new
void slotArticleDelete()
deletes the currently selected article
void slotCaptionChanged(const TQString &)
sets the window caption after a frame change
void slotNormalView()
switches view mode to normal view
void signalUnreadCountChanged(int)
emitted when the unread count of "All Feeds" was changed
void slotOpenNewTab(const KURL &url, bool background=false)
opens a page viewer in a new tab and loads an URL
void slotFeedRemove()
removes the currently selected feed (ask for confirmation)
~View()
destructor.
void slotOpenURLReply(const KURL &url, Akregator::Viewer *currentViewer, Akregator::BrowserRun::OpeningMode mode)
HACK: receives signal from browserrun when the browserrun detects an HTML mimetype and actually loads...
void addFeedToGroup(const TQString &url, const TQString &group)
Add a feed to a group.
void slotMarkAllRead()
marks all articles in the currently selected feed as read
void slotFrameChanged(Frame *f)
called when another part/frame is activated.
void slotCombinedView()
switches view mode to combined view
void slotSetTotalUnread()
emits signalUnreadCountChanged(int)
void slotStatusText(const TQString &)
sets the status bar text to a given string
void slotToggleShowQuickFilter()
toggles the visibility of the filter bar
void slotFeedModify()
calls the properties dialog for feeds, starts renaming for feed groups
void slotOpenHomepage()
opens the homepage of the currently selected feed
void slotFetchAllFeeds()
starts fetching of all feeds in the tree
void slotWidescreenView()
switches view mode to widescreen view
void slotTextToSpeechRequest()
reads the currently selected articles using KTTSD
void slotArticleSelected(const Article &)
the article selection has changed
void slotFeedFetched(Feed *)
Feed has been fetched, populate article view if needed and update counters.
bool loadFeeds(const TQDomDocument &doc, Folder *parent=0)
Parse OPML presentation of feeds and read in articles archive, if present.
void slotMarkAllFeedsRead()
marks all articles in all feeds in the tree as read
void slotFeedAddGroup()
adds a feed group to the feed tree
void slotNextUnreadArticle()
selects the next unread article in the article list