akregator/src

feedlistview.cpp
1 /*
2  This file is part of Akregator.
3 
4  Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net>
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 
20  As a special exception, permission is given to link this program
21  with any edition of TQt, and distribute the resulting executable,
22  without including the source code for TQt in the source distribution.
23 */
24 
25 #include "dragobjects.h"
26 #include "folder.h"
27 #include "folderitem.h"
28 #include "tagfolder.h"
29 #include "tagfolderitem.h"
30 #include "feedlistview.h"
31 #include "feed.h"
32 #include "feeditem.h"
33 #include "feedlist.h"
34 #include "tag.h"
35 #include "tagnode.h"
36 #include "tagnodeitem.h"
37 #include "tagnodelist.h"
38 #include "treenode.h"
39 #include "treenodeitem.h"
40 #include "treenodevisitor.h"
41 
42 #include <kdebug.h>
43 #include <kiconeffect.h>
44 #include <kiconloader.h>
45 #include <klocale.h>
46 #include <kmultipledrag.h>
47 #include <kstringhandler.h>
48 #include <kurldrag.h>
49 
50 #include <tqfont.h>
51 #include <tqheader.h>
52 #include <tqpainter.h>
53 #include <tqptrdict.h>
54 #include <tqtimer.h>
55 #include <tqwhatsthis.h>
56 
57 namespace Akregator {
58 
59 class NodeListView::NodeListViewPrivate
60 {
61  public:
63  TQPtrDict<TreeNodeItem> itemDict;
64  NodeList* nodeList;
65  bool showTagFolders;
66 
67  // Drag and Drop variables
68  TQListViewItem *parent;
69  TQListViewItem *afterme;
70  TQTimer autoopentimer;
71  ConnectNodeVisitor* connectNodeVisitor;
72  DisconnectNodeVisitor* disconnectNodeVisitor;
73  CreateItemVisitor* createItemVisitor;
74  DeleteItemVisitor* deleteItemVisitor;
75 };
76 
77 class NodeListView::ConnectNodeVisitor : public TreeNodeVisitor
78 {
79  public:
80  ConnectNodeVisitor(NodeListView* view) : m_view(view) {}
81 
82  virtual bool visitTreeNode(TreeNode* node)
83  {
84  connect(node, TQT_SIGNAL(signalDestroyed(TreeNode*)), m_view, TQT_SLOT(slotNodeDestroyed(TreeNode*) ));
85  connect(node, TQT_SIGNAL(signalChanged(TreeNode*)), m_view, TQT_SLOT(slotNodeChanged(TreeNode*) ));
86  return true;
87  }
88 
89  virtual bool visitFolder(Folder* node)
90  {
91  visitTreeNode(node);
92  connect(node, TQT_SIGNAL(signalChildAdded(TreeNode*)), m_view, TQT_SLOT(slotNodeAdded(TreeNode*) ));
93  connect(node, TQT_SIGNAL(signalChildRemoved(Folder*, TreeNode*)), m_view, TQT_SLOT(slotNodeRemoved(Folder*, TreeNode*) ));
94  return true;
95  }
96 
97  virtual bool visitFeed(Feed* node)
98  {
99  visitTreeNode(node);
100 
101  connect(node, TQT_SIGNAL(fetchStarted(Feed*)), m_view, TQT_SLOT(slotFeedFetchStarted(Feed*)));
102  connect(node, TQT_SIGNAL(fetchAborted(Feed*)), m_view, TQT_SLOT(slotFeedFetchAborted(Feed*)));
103  connect(node, TQT_SIGNAL(fetchError(Feed*)), m_view, TQT_SLOT(slotFeedFetchError(Feed*)));
104  connect(node, TQT_SIGNAL(fetched(Feed*)), m_view, TQT_SLOT(slotFeedFetchCompleted(Feed*)));
105  return true;
106  }
107  private:
108 
109  NodeListView* m_view;
110 
111 };
112 
113 class NodeListView::DisconnectNodeVisitor : public TreeNodeVisitor
114 {
115  public:
116  DisconnectNodeVisitor(NodeListView* view) : m_view(view) {}
117 
118  virtual bool visitTagNode(TagNode* node)
119  {
120  disconnect(node, TQT_SIGNAL(signalDestroyed(TreeNode*)), m_view, TQT_SLOT(slotNodeDestroyed(TreeNode*) ));
121  disconnect(node, TQT_SIGNAL(signalChanged(TreeNode*)), m_view, TQT_SLOT(slotNodeChanged(TreeNode*) ));
122  return true;
123  }
124 
125  virtual bool visitFolder(Folder* node)
126  {
127  disconnect(node, TQT_SIGNAL(signalChildAdded(TreeNode*)), m_view, TQT_SLOT(slotNodeAdded(TreeNode*) ));
128  disconnect(node, TQT_SIGNAL(signalChildRemoved(Folder*, TreeNode*)), m_view, TQT_SLOT(slotNodeRemoved(Folder*, TreeNode*) ));
129 
130  disconnect(node, TQT_SIGNAL(signalDestroyed(TreeNode*)), m_view, TQT_SLOT(slotNodeDestroyed(TreeNode*) ));
131  disconnect(node, TQT_SIGNAL(signalChanged(TreeNode*)), m_view, TQT_SLOT(slotNodeChanged(TreeNode*) ));
132  return true;
133  }
134 
135  virtual bool visitFeed(Feed* node)
136  {
137 
138  disconnect(node, TQT_SIGNAL(signalDestroyed(TreeNode*)), m_view, TQT_SLOT(slotNodeDestroyed(TreeNode*) ));
139  disconnect(node, TQT_SIGNAL(signalChanged(TreeNode*)), m_view, TQT_SLOT(slotNodeChanged(TreeNode*) ));
140  disconnect(node, TQT_SIGNAL(fetchStarted(Feed*)), m_view, TQT_SLOT(slotFeedFetchStarted(Feed*)));
141  disconnect(node, TQT_SIGNAL(fetchAborted(Feed*)), m_view, TQT_SLOT(slotFeedFetchAborted(Feed*)));
142  disconnect(node, TQT_SIGNAL(fetchError(Feed*)), m_view, TQT_SLOT(slotFeedFetchError(Feed*)));
143  disconnect(node, TQT_SIGNAL(fetched(Feed*)), m_view, TQT_SLOT(slotFeedFetchCompleted(Feed*)));
144  return true;
145  }
146  private:
147 
148  NodeListView* m_view;
149 };
150 
151 class NodeListView::DeleteItemVisitor : public TreeNodeVisitor
152 {
153  public:
154 
155  DeleteItemVisitor(NodeListView* view) : m_view(view) {}
156 
157  virtual bool visitTreeNode(TreeNode* node)
158  {
159  TreeNodeItem* item = m_view->d->itemDict.take(node);
160 
161  if (!item)
162  return true;
163 
164  if ( m_selectNeighbour && item->isSelected() )
165  {
166  if (item->itemBelow())
167  m_view->setSelected(item->itemBelow(), true);
168  else if (item->itemAbove())
169  m_view->setSelected(item->itemAbove(), true);
170  else
171  m_view->setSelected(item, false);
172  }
173 
174  m_view->disconnectFromNode(node);
175  delete item;
176  return true;
177 
178  }
179 
180  virtual bool visitFolder(Folder* node)
181  {
182  // delete child items recursively before deleting parent
183  TQValueList<TreeNode*> children = node->children();
184  for (TQValueList<TreeNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )
185  visit(*it);
186 
187  visitTreeNode(node);
188 
189  return true;
190  }
191 
192  void deleteItem(TreeNode* node, bool selectNeighbour)
193  {
194  m_selectNeighbour = selectNeighbour;
195  visit(node);
196  }
197 
198  private:
199  NodeListView* m_view;
200  bool m_selectNeighbour;
201 };
202 
203 class NodeListView::CreateItemVisitor : public TreeNodeVisitor
204 {
205  public:
206  CreateItemVisitor(NodeListView* view) : m_view(view) {}
207 
208  virtual bool visitTagNode(TagNode* node)
209  {
210  if (m_view->findNodeItem(node))
211  return true;
212 
213  TagNodeItem* item = 0;
214  TreeNode* prev = node->prevSibling();
215  FolderItem* parentItem = static_cast<FolderItem*>(m_view->findNodeItem(node->parent()));
216  if (parentItem)
217  {
218  if (prev)
219  {
220  item = new TagNodeItem( parentItem, m_view->findNodeItem(prev), node);
221  }
222  else
223  item = new TagNodeItem( parentItem, node);
224  }
225  else
226  {
227  if (prev)
228  {
229  item = new TagNodeItem(m_view, m_view->findNodeItem(prev), node);
230  }
231  else
232  item = new TagNodeItem(m_view, node);
233  }
234  item->nodeChanged();
235  m_view->d->itemDict.insert(node, item);
236  m_view->connectToNode(node);
237  if (parentItem)
238  parentItem->sortChildItems(0, true);
239  return true;
240  }
241 
242  virtual bool visitTagFolder(TagFolder* node)
243  {
244  if (m_view->findNodeItem(node))
245  return true;
246 
247  TagFolderItem* item = 0;
248  TreeNode* prev = node->prevSibling();
249  FolderItem* parentItem = static_cast<FolderItem*>(m_view->findNodeItem(node->parent()));
250  if (parentItem)
251  {
252  if (prev)
253  {
254  item = new TagFolderItem( parentItem, m_view->findNodeItem(prev), node);
255  }
256  else
257  item = new TagFolderItem(parentItem, node);
258  }
259  else
260  {
261  if (prev)
262  {
263  item = new TagFolderItem(m_view, m_view->findNodeItem(prev), node);
264  }
265  else
266  item = new TagFolderItem(m_view, node);
267 
268  }
269  m_view->d->itemDict.insert(node, item);
270  TQValueList<TreeNode*> children = node->children();
271 
272  // add children recursively
273  for (TQValueList<TreeNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )
274  visit(*it);
275 
276  m_view->connectToNode(node);
277  return true;
278  }
279 
280  virtual bool visitFolder(Folder* node)
281  {
282  if (m_view->findNodeItem(node))
283  return true;
284 
285  FolderItem* item = 0;
286  TreeNode* prev = node->prevSibling();
287  FolderItem* parentItem = static_cast<FolderItem*>(m_view->findNodeItem(node->parent()));
288  if (parentItem)
289  {
290  if (prev)
291  {
292  item = new FolderItem( parentItem, m_view->findNodeItem(prev), node);
293  }
294  else
295  item = new FolderItem(parentItem, node);
296  }
297  else
298  {
299  if (prev)
300  {
301  item = new FolderItem(m_view, m_view->findNodeItem(prev), node);
302  }
303  else
304  item = new FolderItem(m_view, node);
305  }
306  m_view->d->itemDict.insert(node, item);
307 
308  // add children recursively
309  TQValueList<TreeNode*> children = node->children();
310  for (TQValueList<TreeNode*>::ConstIterator it = children.begin(); it != children.end(); ++it )
311  visit(*it);
312 
313  m_view->connectToNode(node);
314  return true;
315  }
316 
317  virtual bool visitFeed(Feed* node)
318  {
319  if (m_view->findNodeItem(node))
320  return true;
321 
322  FeedItem* item = 0;
323  TreeNode* prev = node->prevSibling();
324  FolderItem* parentItem = static_cast<FolderItem*>(m_view->findNodeItem(node->parent()));
325 
326  if (parentItem)
327  {
328  if (prev)
329  {
330  item = new FeedItem( parentItem, m_view->findNodeItem(prev), node);
331  }
332  else
333  item = new FeedItem( parentItem, node);
334  }
335  else
336  {
337  if (prev)
338  {
339  item = new FeedItem(m_view, m_view->findNodeItem(prev), node);
340  }
341  else
342  item = new FeedItem(m_view, node);
343  }
344 
345  item->nodeChanged();
346  m_view->d->itemDict.insert(node, item);
347  m_view->connectToNode(node);
348  return true;
349  }
350 
351  private:
352  NodeListView* m_view;
353 };
354 
355 NodeListView::NodeListView( TQWidget *parent, const char *name)
356  : KListView(parent, name), d(new NodeListViewPrivate)
357 {
358  d->showTagFolders = true;
359  d->connectNodeVisitor = new ConnectNodeVisitor(this),
360  d->disconnectNodeVisitor = new DisconnectNodeVisitor(this);
361  d->createItemVisitor = new CreateItemVisitor(this);
362  d->deleteItemVisitor = new DeleteItemVisitor(this);
363 
364  setMinimumSize(150, 150);
365  addColumn(i18n("Feeds"));
366  setRootIsDecorated(false);
367  setItemsRenameable(false); // NOTE: setting this this to true collides with setRenameEnabled() in items and breaks in-place renaming in strange ways. Do not enable!
368  setItemMargin(2);
369 
370  setFullWidth(true);
371  setSorting(-1);
372  setDragAutoScroll(true);
373  setDropVisualizer(true);
374  //setDropHighlighter(false);
375 
376  setDragEnabled(true);
377  setAcceptDrops(true);
378  setItemsMovable(true);
379 
380  connect( this, TQT_SIGNAL(dropped(TQDropEvent*, TQListViewItem*)), this, TQT_SLOT(slotDropped(TQDropEvent*, TQListViewItem*)) );
381  connect( this, TQT_SIGNAL(selectionChanged(TQListViewItem*)), this, TQT_SLOT(slotSelectionChanged(TQListViewItem*)) );
382  connect( this, TQT_SIGNAL(itemRenamed(TQListViewItem*, int, const TQString&)), this, TQT_SLOT(slotItemRenamed(TQListViewItem*, int, const TQString&)) );
383  connect( this, TQT_SIGNAL(contextMenu(KListView*, TQListViewItem*, const TQPoint&)), this, TQT_SLOT(slotContextMenu(KListView*, TQListViewItem*, const TQPoint&)) );
384  connect( &(d->autoopentimer), TQT_SIGNAL( timeout() ), this, TQT_SLOT( openFolder() ) );
385 
386  clear();
387 
388  TQWhatsThis::add(this, i18n("<h2>Feeds tree</h2>"
389  "Here you can browse tree of feeds. "
390  "You can also add feeds or feed groups (folders) "
391  "using right-click menu, or reorganize them using "
392  "drag and drop."));
393  setUpdatesEnabled(true);
394 }
395 
396 NodeListView::~NodeListView()
397 {
398  delete d->connectNodeVisitor;
399  delete d->disconnectNodeVisitor;
400  delete d->createItemVisitor;
401  delete d->deleteItemVisitor;
402  delete d;
403  d = 0;
404 }
405 
406 void NodeListView::setNodeList(NodeList* nodeList)
407 {
408  if (nodeList == d->nodeList)
409  return;
410 
411  clear();
412 
413  disconnectFromNodeList(d->nodeList);
414 
415  if (!nodeList)
416  return;
417 
418  d->nodeList = nodeList;
419  connectToNodeList(nodeList);
420 
421 
422  Folder* rootNode = nodeList->rootNode();
423  if (!rootNode)
424  return;
425 
426  slotNodeAdded(rootNode);
427  slotRootNodeChanged(rootNode);
428 }
429 
430 Folder* NodeListView::rootNode()
431 {
432  return d->nodeList ? d->nodeList->rootNode() : 0;
433 }
434 
435 TreeNode* NodeListView::selectedNode()
436 {
437  TreeNodeItem* item = dynamic_cast<TreeNodeItem*> (selectedItem());
438 
439  return ( item ? item->node() : 0) ;
440 }
441 
442 void NodeListView::setSelectedNode(TreeNode* node)
443 {
444  TreeNodeItem* item = findNodeItem(node);
445  if ( node && item )
446  setSelected(item, true);
447 }
448 
449 TreeNode* NodeListView::findNodeByTitle(const TQString& title)
450 {
451  TreeNodeItem* item = dynamic_cast<TreeNodeItem*>(findItemByTitle(title, 0));
452  if (!item)
453  return 0;
454  else
455  return item->node();
456 }
457 
458 TreeNodeItem* NodeListView::findNodeItem(TreeNode* node)
459 {
460  return d->itemDict.find(node);
461 }
462 
463 TreeNodeItem* NodeListView::findItemByTitle(const TQString& text, int column, ComparisonFlags compare) const
464 {
465  return dynamic_cast<TreeNodeItem*> (KListView::findItem(text, column, compare));
466 }
467 
468 void NodeListView::ensureNodeVisible(TreeNode* node)
469 {
470  ensureItemVisible(findNodeItem(node));
471 }
472 
473 void NodeListView::startNodeRenaming(TreeNode* node)
474 {
475  TreeNodeItem* item = findNodeItem(node);
476  if (item)
477  {
478  item->startRename(0);
479  }
480 }
481 
482 void NodeListView::clear()
483 {
484  TQPtrDictIterator<TreeNodeItem> it(d->itemDict);
485  for( ; it.current(); ++it )
486  disconnectFromNode( it.current()->node() );
487  d->itemDict.clear();
488  d->nodeList = 0;
489 
490  KListView::clear();
491 }
492 
493 void NodeListView::drawContentsOffset( TQPainter * p, int ox, int oy,
494  int cx, int cy, int cw, int ch )
495 {
496  bool oldUpdatesEnabled = isUpdatesEnabled();
497  setUpdatesEnabled(false);
498  KListView::drawContentsOffset( p, ox, oy, cx, cy, cw, ch );
499  setUpdatesEnabled(oldUpdatesEnabled);
500 }
501 
502 void NodeListView::slotDropped( TQDropEvent *e, TQListViewItem*
503 /*after*/)
504 {
505  d->autoopentimer.stop();
506 
507  if (e->source() != viewport())
508  {
509  openFolder();
510 
511  if (KURLDrag::canDecode(e))
512  {
513  FolderItem* parent = dynamic_cast<FolderItem*> (d->parent);
514  TreeNodeItem* afterMe = 0;
515 
516  if(d->afterme)
517  afterMe = dynamic_cast<TreeNodeItem*> (d->afterme);
518 
519  KURL::List urls;
520  KURLDrag::decode( e, urls );
521  e->accept();
522  emit signalDropped( urls, afterMe ? afterMe->node() : 0, parent ? parent->node() : 0);
523  }
524  }
525  else
526  {
527  }
528 }
529 
530 void NodeListView::movableDropEvent(TQListViewItem* /*parent*/, TQListViewItem* /*afterme*/)
531 {
532  d->autoopentimer.stop();
533  if (d->parent)
534  {
535  openFolder();
536 
537  Folder* parentNode = (dynamic_cast<FolderItem*> (d->parent))->node();
538  TreeNode* afterMeNode = 0;
539  TreeNode* current = selectedNode();
540 
541  if (d->afterme)
542  afterMeNode = (dynamic_cast<TreeNodeItem*> (d->afterme))->node();
543 
544  current->parent()->removeChild(current);
545  parentNode->insertChild(current, afterMeNode);
546  KListView::movableDropEvent(d->parent, d->afterme);
547  }
548 }
549 
550 void NodeListView::setShowTagFolders(bool enabled)
551 {
552  d->showTagFolders = enabled;
553 }
554 
555 void NodeListView::contentsDragMoveEvent(TQDragMoveEvent* event)
556 {
557  TQPoint vp = contentsToViewport(event->pos());
558  TQListViewItem *i = itemAt(vp);
559 
560  TQListViewItem *qiparent;
561  TQListViewItem *qiafterme;
562  findDrop( event->pos(), qiparent, qiafterme );
563 
564  if (event->source() == viewport()) {
565  // disable any drops where the result would be top level nodes
566  if (i && !i->parent())
567  {
568  event->ignore();
569  d->autoopentimer.stop();
570  return;
571  }
572 
573  // prevent dragging nodes from All Feeds to My Tags or vice versa
574  TQListViewItem* root1 = i;
575  while (root1 && root1->parent())
576  root1 = root1->parent();
577 
578  TQListViewItem* root2 = selectedItem();
579  while (root2 && root2->parent())
580  root2 = root2->parent();
581 
582  if (root1 != root2)
583  {
584  event->ignore();
585  d->autoopentimer.stop();
586  return;
587  }
588 
589  // don't drop node into own subtree
590  TQListViewItem* p = qiparent;
591  while (p)
592  if (p == selectedItem())
593  {
594  event->ignore();
595  d->autoopentimer.stop();
596  return;
597  }
598  else
599  {
600  p = p->parent();
601  }
602 
603  // disable drags onto the item itself
604  if (selectedItem() == i)
605  {
606  event->ignore();
607  d->autoopentimer.stop();
608  return;
609  }
610  }
611 
612  // what the hell was this good for? -fo
613  // if (!i || event->pos().x() > header()->cellPos(header()->mapToIndex(0)) +
614  // treeStepSize() * (i->depth() + 1) + itemMargin() ||
615  // event->pos().x() < header()->cellPos(header()->mapToIndex(0)))
616  // {} else
617 
618  // do we want to move inside the old parent or do we want to move to a new parent
619  if (i && (itemAt(vp - TQPoint(0,5)) == i && itemAt(vp + TQPoint(0,5)) == i))
620  {
621  setDropVisualizer(false);
622  setDropHighlighter(true);
623  cleanDropVisualizer();
624 
625  TreeNode *iNode = (dynamic_cast<TreeNodeItem*> (i))->node();
626  if (iNode->isGroup())
627  {
628  if (i != d->parent)
629  d->autoopentimer.start(750);
630 
631  d->parent = i;
632  d->afterme = 0;
633  }
634  else
635  {
636  event->ignore();
637  d->autoopentimer.stop();
638  d->afterme = i;
639  return;
640  }
641  }
642  else
643  {
644  setDropVisualizer(true);
645  setDropHighlighter(false);
646  cleanItemHighlighter();
647  d->parent = qiparent;
648  d->afterme = qiafterme;
649  d->autoopentimer.stop();
650  }
651 
652  // the rest is handled by KListView.
653  KListView::contentsDragMoveEvent(event);
654 }
655 
656 bool NodeListView::acceptDrag(TQDropEvent *e) const
657 {
658  if (!acceptDrops() || !itemsMovable())
659  return false;
660 
661  if (e->source() != viewport())
662  {
663  return KURLDrag::canDecode(e);
664  }
665  else
666  {
667  // disable dragging of top-level nodes (All Feeds, My Tags)
668  if (selectedItem() && !selectedItem()->parent())
669  return false;
670  else
671  return true;
672  }
673 
674  return true;
675 }
676 
677 void NodeListView::slotItemUp()
678 {
679  if (selectedItem() && selectedItem()->itemAbove())
680  {
681  setSelected( selectedItem()->itemAbove(), true );
682  ensureItemVisible(selectedItem());
683  }
684 }
685 
686 void NodeListView::slotItemDown()
687 {
688  if (selectedItem() && selectedItem()->itemBelow())
689  {
690  setSelected( selectedItem()->itemBelow(), true );
691  ensureItemVisible(selectedItem());
692  }
693 }
694 
695 void NodeListView::slotItemBegin()
696 {
697  setSelected( firstChild(), true );
698  ensureItemVisible(firstChild());
699 }
700 
701 void NodeListView::slotItemEnd()
702 {
703  TQListViewItem* elt = firstChild();
704  if (elt)
705  while (elt->itemBelow())
706  elt = elt->itemBelow();
707  setSelected( elt, true );
708  ensureItemVisible(elt);
709 }
710 
711 void NodeListView::slotItemLeft()
712 {
713  TQListViewItem* sel = selectedItem();
714 
715  if (!sel || sel == findNodeItem(rootNode()))
716  return;
717 
718  if (sel->isOpen())
719  sel->setOpen(false);
720  else
721  {
722  if (sel->parent())
723  setSelected( sel->parent(), true );
724  }
725 
726  ensureItemVisible( selectedItem() );
727 }
728 
729 void NodeListView::slotItemRight()
730 {
731  TQListViewItem* sel = selectedItem();
732  if (!sel)
733  {
734  setSelected( firstChild(), true );
735  sel = firstChild();
736  }
737  if (sel->isExpandable() && !sel->isOpen())
738  sel->setOpen(true);
739  else
740  {
741  if (sel->firstChild())
742  setSelected( sel->firstChild(), true );
743  }
744  ensureItemVisible( selectedItem() );
745 }
746 
747 void NodeListView::slotPrevFeed()
748 {
749  for (TQListViewItemIterator it( selectedItem()); it.current(); --it )
750  {
751  TreeNodeItem* tni = dynamic_cast<TreeNodeItem*>(*it);
752  if (tni && !tni->isSelected() && !tni->node()->isGroup() )
753  {
754  setSelected(tni, true);
755  ensureItemVisible(tni);
756  return;
757  }
758  }
759 }
760 
761 void NodeListView::slotNextFeed()
762 {
763  for (TQListViewItemIterator it( selectedItem()); it.current(); ++it )
764  {
765  TreeNodeItem* tni = dynamic_cast<TreeNodeItem*>(*it);
766  if ( tni && !tni->isSelected() && !tni->node()->isGroup() )
767  {
768  setSelected(tni, true);
769  ensureItemVisible(tni);
770  return;
771  }
772  }
773 }
774 
775 void NodeListView::slotPrevUnreadFeed()
776 {
777  if (!firstChild() || !firstChild()->firstChild())
778  return;
779  if ( !selectedItem() )
780  slotNextUnreadFeed();
781 
782  TQListViewItemIterator it( selectedItem() );
783 
784  for ( ; it.current(); --it )
785  {
786  TreeNodeItem* tni = dynamic_cast<TreeNodeItem*> (it.current());
787  if (!tni)
788  break;
789  if ( !tni->isSelected() && !tni->node()->isGroup() && tni->node()->unread() > 0)
790  {
791  setSelected(tni, true);
792  ensureItemVisible(tni);
793  return;
794  }
795  }
796  // reached when there is no unread feed above the selected one
797  // => cycle: go to end of list...
798  if (rootNode()->unread() > 0)
799  {
800 
801  it = TQListViewItemIterator(lastItem());
802 
803  for ( ; it.current(); --it)
804  {
805 
806  TreeNodeItem* tni = dynamic_cast<TreeNodeItem*> (it.current());
807 
808  if (!tni)
809  break;
810 
811  if (!tni->isSelected() && !tni->node()->isGroup() && tni->node()->unread() > 0)
812  {
813  setSelected(tni, true);
814  ensureItemVisible(tni);
815  return;
816  }
817  }
818  }
819 }
820 
821 void NodeListView::slotNextUnreadFeed()
822 {
823  TQListViewItemIterator it;
824 
825  if ( !selectedItem() )
826  {
827  // if all feeds doesnt exists or is empty, return
828  if (!firstChild() || !firstChild()->firstChild())
829  return;
830  else
831  it = TQListViewItemIterator( firstChild()->firstChild());
832  }
833  else
834  it = TQListViewItemIterator( selectedItem() );
835 
836  for ( ; it.current(); ++it )
837  {
838  TreeNodeItem* tni = dynamic_cast<TreeNodeItem*> (it.current());
839  if (!tni)
840  break;
841  if ( !tni->isSelected() && !tni->node()->isGroup() && tni->node()->unread() > 0)
842  {
843  setSelected(tni, true);
844  ensureItemVisible(tni);
845  return;
846  }
847  }
848  // if reached, we are at the end of the list++
849  if (rootNode()->unread() > 0)
850  {
851  clearSelection();
852  slotNextUnreadFeed();
853  }
854 }
855 
856 void NodeListView::slotSelectionChanged(TQListViewItem* item)
857 {
858  TreeNodeItem* ni = dynamic_cast<TreeNodeItem*> (item);
859 
860  if (ni)
861  {
862  emit signalNodeSelected(ni->node());
863  }
864 }
865 
866 void NodeListView::slotItemRenamed(TQListViewItem* item, int col, const TQString& text)
867 {
868  TreeNodeItem* ni = dynamic_cast<TreeNodeItem*> (item);
869  if ( !ni || !ni->node() )
870  return;
871  if (col == 0)
872  {
873  if (text != ni->node()->title())
874  {
875  ni->node()->setTitle(text);
876  }
877  }
878 }
879 void NodeListView::slotContextMenu(KListView* list, TQListViewItem* item, const TQPoint& p)
880 {
881  TreeNodeItem* ti = dynamic_cast<TreeNodeItem*>(item);
882  emit signalContextMenu(list, ti ? ti->node() : 0, p);
883  if (ti)
884  ti->showContextMenu(p);
885 }
886 
887 void NodeListView::slotFeedFetchStarted(Feed* feed)
888 {
889  // Disable icon to show it is fetching.
890  if (!feed->favicon().isNull())
891  {
892  TreeNodeItem* item = findNodeItem(feed);
893  if (item)
894  {
895  KIconEffect iconEffect;
896  TQPixmap tempIcon = iconEffect.apply(feed->favicon(), KIcon::Small, KIcon::DisabledState);
897  item->setPixmap(0, tempIcon);
898  }
899  }
900 
901 }
902 
903 void NodeListView::slotFeedFetchAborted(Feed* feed)
904 {
905  TreeNodeItem* item = findNodeItem(feed);
906  if (item)
907  item->nodeChanged();
908 }
909 
910 void NodeListView::slotFeedFetchError(Feed* feed)
911 {
912  TreeNodeItem* item = findNodeItem(feed);
913  if (item)
914  item->nodeChanged();
915 }
916 
917 void NodeListView::slotFeedFetchCompleted(Feed* feed)
918 {
919  TreeNodeItem* item = findNodeItem(feed);
920  if (item)
921  item->nodeChanged();
922 }
923 
924 void NodeListView::slotNodeAdded(TreeNode* node)
925 {
926  if (node)
927  d->createItemVisitor->visit(node);
928 }
929 
930 void NodeListView::slotNodeRemoved(Folder* /*parent*/, TreeNode* node)
931 {
932  if (node)
933  d->deleteItemVisitor->deleteItem(node, false);
934 }
935 
936 void NodeListView::connectToNode(TreeNode* node)
937 {
938  if (node)
939  d->connectNodeVisitor->visit(node);
940 }
941 
942 void NodeListView::connectToNodeList(NodeList* list)
943 {
944  if (!list)
945  return;
946 
947  connect(list, TQT_SIGNAL(signalDestroyed(NodeList*)), this, TQT_SLOT(slotNodeListDestroyed(NodeList*)) );
948  connect(list->rootNode(), TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotRootNodeChanged(TreeNode*)));
949 }
950 
951 void NodeListView::disconnectFromNodeList(NodeList* list)
952 {
953  if (!list)
954  return;
955 
956  disconnect(list, TQT_SIGNAL(signalDestroyed(NodeList*)), this, TQT_SLOT(slotNodeListDestroyed(NodeList*)) );
957  disconnect(list->rootNode(), TQT_SIGNAL(signalChanged(TreeNode*)), this, TQT_SLOT(slotRootNodeChanged(TreeNode*)));
958 }
959 
960 void NodeListView::disconnectFromNode(TreeNode* node)
961 {
962  if (node)
963  d->disconnectNodeVisitor->visit(node);
964 }
965 
966 void NodeListView::slotNodeListDestroyed(NodeList* list)
967 {
968  if (list != d->nodeList)
969  return;
970 
971  setNodeList(0);
972 }
973 
974 void NodeListView::slotNodeDestroyed(TreeNode* node)
975 {
976  if (node)
977  d->deleteItemVisitor->deleteItem(node, true);
978 }
979 
980 void NodeListView::slotRootNodeChanged(TreeNode* rootNode)
981 {
982  emit signalRootNodeChanged(this, rootNode);
983 }
984 
985 void NodeListView::slotNodeChanged(TreeNode* node)
986 {
987  TreeNodeItem* item = findNodeItem(node);
988  if (item)
989  {
990  item->nodeChanged();
991  triggerUpdate();
992  }
993 }
994 
995 TQDragObject *NodeListView::dragObject()
996 {
997  KMultipleDrag *md = new KMultipleDrag(viewport());
998  TQDragObject *obj = KListView::dragObject();
999  if (obj) {
1000  md->addDragObject(obj);
1001  }
1002  TreeNodeItem *i = dynamic_cast<TreeNodeItem*>(currentItem());
1003  if (i) {
1004  md->setPixmap(*(i->pixmap(0)));
1005  FeedItem *fi = dynamic_cast<FeedItem*>(i);
1006  if (fi) {
1007  md->addDragObject(new KURLDrag(KURL(fi->node()->xmlUrl()), 0L));
1008  }
1009  }
1010  return md;
1011 }
1012 
1013 void NodeListView::openFolder() {
1014  d->autoopentimer.stop();
1015  if (d->parent && !d->parent->isOpen())
1016  {
1017  d->parent->setOpen(true);
1018  }
1019 }
1020 
1021 } // namespace Akregator
1022 
1023 #include "feedlistview.moc"