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