Salome HOME
Initial version
[modules/gui.git] / src / ObjBrowser / OB_Browser.cxx
1 #include "OB_Browser.h"
2
3 #include "OB_Filter.h"
4 #include "OB_ListItem.h"
5 #include "OB_ListView.h"
6
7 #include <qcursor.h>
8 #include <qlayout.h>
9 #include <qtooltip.h>
10 #include <qpainter.h>
11 #include <qwmatrix.h>
12 #include <qlistview.h>
13 #include <qpopupmenu.h>
14
15 /*!
16     Class: OB_Browser::ToolTip
17     Descr: Tool tip for OB_Browser.
18 */
19
20 class OB_Browser::ToolTip : public QToolTip
21 {
22 public:
23   ToolTip( OB_Browser* b, QWidget* p = 0 );
24   virtual ~ToolTip();
25
26   void        maybeTip( const QPoint& );
27
28 private:
29   OB_Browser* myBrowser;
30 };
31
32 OB_Browser::ToolTip::ToolTip( OB_Browser* b, QWidget* p )
33 : QToolTip( p ),
34 myBrowser( b )
35 {
36 }
37
38 OB_Browser::ToolTip::~ToolTip()
39 {
40 }
41
42 void OB_Browser::ToolTip::maybeTip( const QPoint& pos )
43 {
44   if ( !parentWidget() || !myBrowser || !myBrowser->isShowToolTips() )
45           return;
46
47   QListView* lv = myBrowser->getListView();
48
49   QListViewItem* item = lv->itemAt( pos );
50   SUIT_DataObject* obj = myBrowser->dataObject( item );
51   if ( !obj )
52     return;
53
54   QString aText = obj->toolTip();
55
56   if ( aText.isEmpty() )
57     return;
58
59   QRect aRect = lv->itemRect( item );
60
61   tip( aRect, aText );
62 }
63
64 /*!
65     Class: OB_Browser
66     Descr: Hierarchical tree object browser.
67 */
68
69 OB_Browser::OB_Browser( QWidget* parent, SUIT_DataObject* root )
70 : QFrame( parent ),
71
72 myRoot( 0 ),
73 myTooltip( 0 ),
74 myAutoUpdate( false ),
75 myAutoDelObjs( false ),
76 myRootDecorated( true )
77 {
78   myView = new OB_ListView( this );
79   myView->addColumn( "Data" );
80   myView->setSorting( -1 );
81   myView->setRootIsDecorated( true );
82   myView->setSelectionMode( QListView::Extended );
83   myView->installEventFilter( this );
84
85   QVBoxLayout* main = new QVBoxLayout( this );
86   main->addWidget( myView );
87   
88   myShowToolTips = true;
89   myTooltip = new ToolTip( this, myView->viewport() );
90
91   connect( myView, SIGNAL( dropped( QPtrList<QListViewItem>, QListViewItem*, int ) ),
92            this, SLOT( onDropped( QPtrList<QListViewItem>, QListViewItem*, int ) ) );
93   connect( myView, SIGNAL( selectionChanged() ), this, SIGNAL( selectionChanged() ) );
94
95   setRootObject( root );
96 }
97
98 OB_Browser::~OB_Browser()
99 {
100   myItems.clear();
101   delete myTooltip;
102 }
103
104 bool OB_Browser::rootIsDecorated() const
105 {
106   return myRootDecorated;
107 }
108
109 void OB_Browser::setRootIsDecorated( const bool decor )
110 {
111   if ( decor == rootIsDecorated() ) 
112     return;
113
114   myRootDecorated = decor;
115   updateTree();
116 }
117
118 bool OB_Browser::isShowToolTips()
119 {
120   return myShowToolTips;
121 }
122
123 void OB_Browser::setShowToolTips( const bool theDisplay )
124 {
125   myShowToolTips = theDisplay;
126 }
127
128 bool OB_Browser::isAutoUpdate() const
129 {
130   return myAutoUpdate;
131 }
132
133 void OB_Browser::setAutoUpdate( const bool on )
134 {
135   myAutoUpdate = on;
136 }
137
138 bool OB_Browser::isAutoDeleteObjects() const
139 {
140   return myAutoDelObjs;
141 }
142
143 void OB_Browser::setAutoDeleteObjects( const bool on )
144 {
145   myAutoDelObjs = on;
146 }
147
148 SUIT_DataObject* OB_Browser::getRootObject() const
149 {
150   return myRoot;
151 }
152
153 void OB_Browser::setRootObject( SUIT_DataObject* theRoot )
154 {
155   DataObjectKey curKey;
156   DataObjectMap selObjs, openObjs;
157   DataObjectKeyMap selKeys, openKeys;
158
159   int selNum = numberOfSelected();
160
161   SUIT_DataObject* curObj = 0;
162   if ( theRoot )
163     curObj = storeState( selObjs, openObjs, selKeys, openKeys, curKey );
164
165   if ( myRoot != theRoot && isAutoDeleteObjects() )
166     delete myRoot;
167
168   myRoot = theRoot;
169
170   createConnections( myRoot );
171
172   if ( myRoot )
173     updateView( myRoot );
174   else if ( getListView() )
175   {
176     myItems.clear();
177     getListView()->clear();
178   }
179
180   restoreState( selObjs, openObjs, curObj, selKeys, openKeys, curKey );
181
182   if ( selNum != numberOfSelected() )
183     emit selectionChanged();
184 }
185
186 int OB_Browser::numberOfSelected() const
187 {
188   int count = 0;
189   if ( getListView() )
190   {
191     for ( QListViewItemIterator it( getListView() ); it.current(); ++it )
192       if ( it.current()->isSelected() ) 
193         count++;
194   }
195   return count;
196 }
197
198 DataObjectList OB_Browser::getSelected() const
199 {
200   DataObjectList lst;
201   getSelected( lst );
202   return lst;
203 }
204
205 void OB_Browser::getSelected( DataObjectList& theObjList ) const
206 {
207   theObjList.clear();
208
209   if ( !getListView() )
210     return;
211
212   for ( QListViewItemIterator it( getListView() ); it.current(); ++it )
213   {
214     if ( it.current()->isSelected() ) 
215     {
216       SUIT_DataObject* obj = dataObject( it.current() );
217       if ( obj )
218               theObjList.append( obj );
219     }
220   }
221 }
222
223 void OB_Browser::setSelected( const SUIT_DataObject* theObject, const bool append )
224 {
225   DataObjectList lst;
226   lst.append( theObject );
227   setSelected( lst, append );
228 }
229
230 void OB_Browser::setSelected( const DataObjectList& theObjLst, const bool append )
231 {
232   QListView* lv = getListView();
233
234   if ( !lv )
235     return;
236
237   bool changed = false;
238   bool block = lv->signalsBlocked();
239   lv->blockSignals( true );
240
241   QMap<QListViewItem*, int> map;
242   for ( DataObjectListIterator itr( theObjLst ); itr.current(); ++itr )
243     map.insert( listViewItem( itr.current() ), 0 );
244
245   for ( QListViewItemIterator it( lv ); it.current(); ++it )
246   {
247     QListViewItem* item = it.current();
248     if ( map.contains( item ) && !lv->isSelected( item ) )
249     {
250       changed = true;
251       lv->setSelected( item, true );
252     }
253     if ( !append && !map.contains( item ) && lv->isSelected( item ) )
254     {
255       changed = true;
256       lv->setSelected( item, false );
257     }
258   }
259
260   lv->blockSignals( block );
261
262   if ( changed )
263   {
264     int count = 0;
265     QListViewItem* sel = 0;
266     QListViewItem* cur = lv->currentItem();
267     for ( QListViewItemIterator iter( lv ); iter.current() && !sel; ++iter, count++ )
268     {
269       if ( iter.current()->isSelected() && cur == iter.current() )
270         sel = iter.current();
271     }
272
273     for ( QListViewItemIterator itr( lv ); itr.current() && !sel; ++itr )
274     {
275       if ( itr.current()->isSelected() )
276               sel = itr.current();
277     }
278
279     if ( sel )
280       lv->setCurrentItem( sel );
281
282     if ( sel && count == 1 )
283       lv->ensureItemVisible( sel );
284
285     emit selectionChanged();
286   }
287 }
288
289 bool OB_Browser::isOpen( SUIT_DataObject* theObject ) const
290 {
291   bool res = false;
292   if ( getListView() )
293     res = getListView()->isOpen( listViewItem( theObject ) );
294   return res;
295 }
296
297 void OB_Browser::setOpen( SUIT_DataObject* theObject, const bool theOpen )
298 {
299   if ( getListView() )
300     getListView()->setOpen( listViewItem( theObject ), theOpen );
301 }
302
303 SUIT_DataObject* OB_Browser::dataObjectAt( const QPoint& pos ) const
304 {
305   SUIT_DataObject* obj = 0;
306
307   QListView* lv = getListView();
308   if ( lv )
309     obj = dataObject( lv->itemAt( pos ) );
310
311   return obj;
312 }
313
314 OB_Filter* OB_Browser::filter() const
315 {
316   return myView->filter();
317 }
318
319 void OB_Browser::setFilter( OB_Filter* f )
320 {
321   myView->setFilter( f );
322 }
323
324 int OB_Browser::addColumn( const QString & label, int width, int index )
325 {
326   int id = -1;
327   if ( !myView )
328     return id;
329   if ( index != -1 && myColumnIds.contains( index ) )
330     return id; // can not reuse index
331
332   int trueId = index;
333   id = myView->addColumn( label, width );
334   if ( trueId == -1 )
335     trueId = id;
336   myColumnIds.insert( trueId, id );
337   updateText();
338
339   return trueId;
340 }
341
342 int OB_Browser::addColumn( const QIconSet & iconset, const QString & label, int width, int index )
343 {
344   int id = -1;
345   if ( !myView )
346     return id;
347   if ( index != -1 && myColumnIds.contains( index ) )
348     return id; // can not reuse index
349
350   int trueId = index;
351   id = myView->addColumn( iconset, label, width );
352   if ( trueId == -1 )
353     trueId = id;
354   myColumnIds.insert( trueId, id );
355   updateText();
356
357   return trueId;
358 }
359
360 void OB_Browser::removeColumn( int index )
361 {
362   if ( !myView || !myColumnIds.contains( index ) )
363     return;
364
365   int id = myColumnIds[ index ];
366   myView->removeColumn( id );
367
368   // update map of column indeces
369   myColumnIds.remove( index );
370   for ( QMap<int, int>::iterator it = myColumnIds.begin(); it != myColumnIds.end(); ++it )
371   {
372     if ( it.key() > index )
373       it.data()--;
374   }
375   updateText();
376 }
377
378 void OB_Browser::updateTree( SUIT_DataObject* o )
379 {
380   SUIT_DataObject* obj = o ? o : getRootObject();
381   if ( !obj )
382     return;
383
384   DataObjectKey curKey;
385   DataObjectMap selObjs, openObjs;
386   DataObjectKeyMap selKeys, openKeys;
387
388   int selNum = numberOfSelected();
389
390   SUIT_DataObject* curObj = storeState( selObjs, openObjs, selKeys, openKeys, curKey );
391
392   createConnections( obj );
393   updateView( obj );
394
395   restoreState( selObjs, openObjs, curObj, selKeys, openKeys, curKey );
396
397   if ( selNum != numberOfSelected() )
398     emit selectionChanged();
399 }
400
401 void OB_Browser::replaceTree( SUIT_DataObject* src, SUIT_DataObject* trg )
402 {
403   if ( !src || !trg || src->root() != getRootObject() )
404     return;
405
406   DataObjectKey curKey;
407   DataObjectMap selObjs, openObjs;
408   DataObjectKeyMap selKeys, openKeys;
409
410   int selNum = numberOfSelected();
411
412   SUIT_DataObject* curObj = storeState( selObjs, openObjs, selKeys, openKeys, curKey );
413
414   SUIT_DataObject* parent = src->parent();
415   int pos = parent ? parent->childPos( src ) : -1;
416
417   src->setParent( 0 );
418
419   if ( src != trg && isAutoDeleteObjects() )
420     delete src;
421
422   if ( parent && pos != -1 )
423     parent->insertChild( trg, pos );
424
425   trg->setParent( parent );
426
427   createConnections( trg );
428   updateView( trg );
429
430   restoreState( selObjs, openObjs, curObj, selKeys, openKeys, curKey );
431
432   if ( selNum != numberOfSelected() )
433     emit selectionChanged();
434 }
435
436 void OB_Browser::updateView( const SUIT_DataObject* theStartObj )
437 {
438   QListView* lv = getListView();
439   if ( !lv )
440     return;
441
442   if ( !theStartObj || theStartObj->root() != getRootObject() )
443     return;
444
445   QListViewItem* after = 0;
446   QListViewItem* parent = 0;
447   QListViewItem* startItem = listViewItem( theStartObj );
448
449   if ( theStartObj->parent() )
450     parent = listViewItem( theStartObj->parent() );
451
452   QListViewItem* prv = 0;
453   QListViewItem* cur = parent ? parent->firstChild() : lv->firstChild();
454   while ( !after && cur )
455   {
456     if ( cur == startItem )
457       after = prv;
458
459     prv = cur;
460     cur = cur->nextSibling();
461   }
462
463   QPtrList<QListViewItem> delList;
464   if ( !startItem && theStartObj == getRootObject() )
465   {
466     for ( QListViewItem* item = lv->firstChild(); item; item = item->nextSibling() )
467       delList.append( item );
468   }
469   else
470     delList.append( startItem );
471
472   for ( QPtrListIterator<QListViewItem> it( delList ); it.current(); ++it )
473   {
474     removeReferences( it.current() );
475     delete it.current();
476   }
477
478   // for myRoot object, if myShowRoot==false, then creating multiple top-level QListViewItem-s
479   // (which will correspond to myRoot's children = Modules).  
480   if ( rootIsDecorated() && theStartObj == myRoot )
481   {
482     DataObjectList lst;
483     theStartObj->children( lst );
484     DataObjectListIterator it ( lst );
485     // iterating backward to preserve the order of elements in the tree
486     for ( it.toLast(); it.current(); --it )
487       createTree( it.current(), 0, 0 );
488   }
489   else
490     createTree( theStartObj, parent, after );
491 }
492
493 QListViewItem* OB_Browser::createTree( const SUIT_DataObject* obj,
494                                           QListViewItem* parent, QListViewItem* after )
495 {
496   if ( !obj )
497     return 0;
498   
499   QListViewItem* item = createItem( obj, parent, after );
500
501   DataObjectList lst;
502   obj->children( lst );
503   for ( DataObjectListIterator it ( lst ); it.current(); ++it )
504     createTree( it.current(), item );
505
506   return item;
507 }
508
509 QListViewItem* OB_Browser::createItem( const SUIT_DataObject* o,
510                                        QListViewItem* parent, QListViewItem* after )
511 {
512   QListView* lv = getListView();
513
514   if ( !lv || !o )
515     return 0;
516
517   QListViewItem* item = 0;
518   SUIT_DataObject* obj = (SUIT_DataObject*)o;
519
520   int type = -1;
521
522   switch ( obj->checkType() )
523   {
524   case SUIT_DataObject::CheckBox:
525     type = QCheckListItem::CheckBox;
526     break;
527   case SUIT_DataObject::RadioButton:
528     type = QCheckListItem::RadioButton;
529     break;
530   }
531
532   if ( parent )
533   {
534     if ( parent->childCount() && !after )
535     {
536       after = parent->firstChild();
537       while ( after->nextSibling() )
538         after = after->nextSibling();
539     }
540
541     if ( after )
542     {
543       if ( type == -1 )
544         item = new OB_ListItem( obj, parent, after );
545       else
546         item = new OB_CheckListItem( obj, parent, after, (QCheckListItem::Type)type );
547     }
548     else
549     {
550       if ( type == -1 )
551         item = new OB_ListItem( obj, parent );
552       else
553         item = new OB_CheckListItem( obj, parent,  (QCheckListItem::Type)type );
554     }
555   }
556   else // ROOT item
557   {
558     if ( type == -1 )
559       item = new OB_ListItem( obj, lv );
560     else
561       item = new OB_CheckListItem( obj, lv,  (QCheckListItem::Type)type );
562   }
563
564   myItems.insert( obj, item );
565
566   return item;
567 }
568
569 void OB_Browser::adjustWidth()
570 {
571   if ( !getListView() )
572     return;
573
574   getListView()->setColumnWidth( 0, 0 );
575   if ( getListView()->firstChild() )
576     adjustWidth( getListView()->firstChild() );
577 }
578
579 void OB_Browser::adjustWidth( QListViewItem* item )
580 {
581   while ( item )
582   {
583     item->widthChanged( 0 );
584     if ( item->isOpen() )
585       adjustWidth( item->firstChild() );
586     item = item->nextSibling();
587   }
588 }
589
590 SUIT_DataObject* OB_Browser::dataObject( const QListViewItem* item ) const
591 {
592   SUIT_DataObject* obj = 0;
593
594   if ( item && item->rtti() == OB_ListItem::RTTI() )
595     obj = ((OB_ListItem*)item)->dataObject();
596   else if ( item && item->rtti() == OB_CheckListItem::RTTI() )
597     obj = ((OB_CheckListItem*)item)->dataObject();
598
599   return obj;
600 }
601
602 QListViewItem* OB_Browser::listViewItem( const SUIT_DataObject* obj ) const
603 {
604   QListViewItem* item = 0;
605
606   if ( myItems.contains( (SUIT_DataObject*)obj ) )
607     item = myItems[(SUIT_DataObject*)obj];
608
609   return item;
610 }
611
612 QListView* OB_Browser::getListView() const
613 {
614   return myView;
615 }
616
617 void OB_Browser::removeReferences( QListViewItem* item )
618 {
619   if ( !item )
620     return;
621
622   SUIT_DataObject* obj = dataObject( item );
623   myItems.remove( obj );
624
625   QListViewItem* i = item->firstChild();
626   while ( i )
627   {
628     removeReferences( i );
629     i = i->nextSibling();
630   }
631 }
632
633 void OB_Browser::createConnections( SUIT_DataObject* obj )
634 {
635   if ( !obj )
636     return;
637
638   DataObjectList childList;
639   obj->children( childList, true );
640
641   childList.prepend( obj );
642
643   for ( DataObjectListIterator it( childList ); it.current(); ++it )
644     it.current()->connect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
645 }
646
647 SUIT_DataObject* OB_Browser::storeState( DataObjectMap& selObjs, DataObjectMap& openObjs,
648                                          DataObjectKeyMap& selKeys, DataObjectKeyMap& openKeys,
649                                          DataObjectKey& curKey ) const
650 {
651   QListView* lv = getListView();
652   if ( !lv )
653     return 0;
654
655   SUIT_DataObject* curObj = dataObject( lv->currentItem() );
656
657   curKey = objectKey( curObj );
658
659   for ( QListViewItemIterator it( lv ); it.current(); ++it )
660   {
661     SUIT_DataObject* obj = dataObject( it.current() );
662     if ( !obj )
663       continue;
664
665     selObjs.insert( obj, lv->isSelected( it.current() ) );
666     openObjs.insert( obj, lv->isOpen( it.current() ) );
667     if ( lv->isSelected( it.current() ) )
668       selKeys.insert( objectKey( obj ), 0 );
669     if ( lv->isOpen( it.current() ) )
670       openKeys.insert( objectKey( obj ), 0 );
671   }
672
673   return curObj;
674 }
675
676 void OB_Browser::restoreState( const DataObjectMap& selObjs, const DataObjectMap& openObjs,
677                                const SUIT_DataObject* curObj, const DataObjectKeyMap& selKeys,
678                                const DataObjectKeyMap& openKeys, const DataObjectKey& curKey )
679 {
680   QListView* lv = getListView();
681   if ( !lv )
682     return;
683
684   bool block = lv->signalsBlocked();
685   lv->blockSignals( true );
686
687   QListViewItem* curItem = 0;
688   for ( QListViewItemIterator it( lv ); it.current(); ++it )
689   {
690     QListViewItem* item = it.current();
691     SUIT_DataObject* obj = dataObject( item );
692
693     if ( !obj )
694       continue;
695
696     DataObjectKey key = objectKey( obj );
697
698     if ( selObjs.contains( obj ) )
699     {
700       if ( selObjs[obj] && !lv->isSelected( item ) )
701         lv->setSelected( item, true );
702     }
703     else if ( !key.isNull() && selKeys.contains( key ) && !lv->isSelected( item ) )
704       lv->setSelected( item, true );
705
706     if ( openObjs.contains( obj ) )
707     {
708       if ( openObjs[obj] )
709         lv->setOpen( item, true );
710     }
711     else if ( !key.isNull() && openKeys.contains( key ) )
712       lv->setOpen( item, true );
713
714     if ( !curItem && ( curObj == obj || ( !curKey.isNull() && curKey == key )) )
715       curItem = item;
716   }
717
718   if ( curItem )
719     lv->setCurrentItem( curItem );
720
721   lv->blockSignals( block );
722 }
723
724 OB_Browser::DataObjectKey OB_Browser::objectKey( QListViewItem* i ) const
725 {
726   return objectKey( dataObject( i ) );
727 }
728
729 OB_Browser::DataObjectKey OB_Browser::objectKey( SUIT_DataObject* obj ) const
730 {
731   if ( !obj )
732     return 0;
733
734   return DataObjectKey( obj->key() );
735 }
736
737 void OB_Browser::keyPressEvent( QKeyEvent* e )
738 {
739   if ( e->key() == Qt::Key_F5 )
740     updateTree();
741
742   if ( ( e->key() == Qt::Key_Plus || e->key() == Qt::Key_Minus ) &&
743        e->state() & ControlButton && getListView() )
744   {
745     bool isOpen = e->key() == Qt::Key_Plus;
746     for ( QListViewItemIterator it( getListView() ); it.current(); ++it )
747       if ( it.current()->childCount() )
748         it.current()->setOpen( isOpen );
749   }
750
751   QFrame::keyPressEvent( e );
752 }
753
754 void OB_Browser::onExpand()
755 {
756   DataObjectList selected;
757   getSelected( selected );
758   for ( DataObjectListIterator itr( selected ); itr.current(); ++itr )
759     expand( listViewItem( itr.current() ) );
760 }
761
762 void OB_Browser::onDestroyed( SUIT_DataObject* obj )
763 {
764   if ( !obj )
765     return;
766
767   // Removing list view items from <myItems> recursively for all children.
768   // Otherwise, "delete item" line will destroy all item's children,
769   // and <myItems> will contain invalid pointers (see ~QListViewItem() description in Qt docs)
770   DataObjectList childList;
771   obj->children( childList );
772   for ( DataObjectListIterator it( childList ); it.current(); ++it )
773     onDestroyed( it.current() );
774
775   QListViewItem* item = listViewItem( obj );
776
777   myItems.remove( obj );
778
779   if ( obj == myRoot )
780     myRoot = 0;
781
782   if ( isAutoUpdate() )
783   {
784     SUIT_DataObject* pObj = item && item->parent() ? dataObject( item->parent() ) : 0;
785     updateTree( pObj );
786   }
787   else
788     delete item;
789 }
790
791 void OB_Browser::onDropped( QPtrList<QListViewItem> items, QListViewItem* item, int action )
792 {
793   SUIT_DataObject* obj = dataObject( item );
794   if ( !obj )
795     return;
796
797   DataObjectList lst;
798   for ( QPtrListIterator<QListViewItem> it( items ); it.current(); ++it )
799   {
800     SUIT_DataObject* o = dataObject( it.current() );
801     if ( o )
802       lst.append( o );
803   }
804
805   if ( !lst.isEmpty() )
806     emit dropped( lst, obj, action );
807 }
808
809 void OB_Browser::updateText()
810 {
811   if ( !myView )
812     return;
813   
814   if ( myColumnIds.size() )
815   {
816     QListViewItemIterator it( myView );
817     for ( ; it.current() != 0; ++it )
818     {
819       QListViewItem* item = it.current();
820       SUIT_DataObject* obj = dataObject( item );
821       if ( !item || !obj )
822         continue;
823       QMap<int, int>::iterator it = myColumnIds.begin();
824       for( ; it != myColumnIds.end(); ++it )
825         item->setText( it.data(), obj->text( it.key() ) );
826     }
827   }
828   updateView();
829 }
830
831 bool OB_Browser::eventFilter(QObject* watched, QEvent* e)
832 {
833   if ( watched == myView && e->type() == QEvent::ContextMenu )
834   {
835     contextMenuRequest( (QContextMenuEvent*)e );
836     return true;
837   }
838   return QFrame::eventFilter(watched, e);
839 }
840
841 void OB_Browser::contextMenuPopup( QPopupMenu* menu )
842 {
843   DataObjectList selected;
844   getSelected( selected );
845
846   bool closed = false;
847   for ( DataObjectListIterator itr( selected ); itr.current() && !closed; ++itr )
848     closed = hasClosed( listViewItem( itr.current() ) );
849
850   if ( closed )
851     menu->insertItem( tr( "MEN_EXPAND_ALL" ), this, SLOT( onExpand() ) );
852 }
853
854 void OB_Browser::expand( QListViewItem* item )
855 {
856   if ( !item )
857     return;
858
859   item->setOpen( true );
860   for ( QListViewItem* child = item->firstChild(); child; child = child->nextSibling() )
861     expand( child );
862 }
863
864 bool OB_Browser::hasClosed( QListViewItem* item ) const
865 {
866   if ( !item )
867     return false;
868
869   if ( item->childCount() && !item->isOpen() )
870     return true;
871
872   bool has = false;
873   for ( QListViewItem* child = item->firstChild(); child && !has; child = child->nextSibling() )
874     has = hasClosed( child );
875
876   return has;
877 }