Salome HOME
Merge branch 'V9_9_BR'
[modules/gui.git] / src / ObjBrowser / OB_Browser.cxx
1 // Copyright (C) 2007-2022  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library 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 GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File   : OB_Browser.cxx
24 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
25
26 #include "OB_Browser.h"
27
28 //#include "OB_Filter.h"
29 //#include "OB_ListItem.h"
30 //#include "OB_ListView.h"
31
32 #include <QtxTreeView.h>
33 #include <QtxSearchTool.h>
34 //#include <SUIT_DataObjectIterator.h>
35
36 #include <QAction>
37 #include <QMenu>
38 #include <QItemSelection>
39 #include <QKeyEvent>
40 #include <QVBoxLayout>
41 #include <QAbstractItemModel>
42 #include <QAbstractItemDelegate>
43 #include <QHeaderView>
44
45 #include <time.h>
46
47
48 /*!
49   \class  OB_Browser::ToolTip
50   Tool tip for OB_Browser.
51 */
52
53 //TODO: ToolTip commented - to be removed or revised
54 /*
55 class OB_Browser::ToolTip : public QToolTip
56 {
57 public:
58   ToolTip( OB_Browser* b, QWidget* p = 0 );
59   virtual ~ToolTip();
60
61   void        maybeTip( const QPoint& );
62
63 private:
64   OB_Browser* myBrowser;
65 };
66 */
67 /*!
68   Constructor
69 */
70 /*
71 OB_Browser::ToolTip::ToolTip( OB_Browser* b, QWidget* p )
72 : QToolTip( p ),
73 myBrowser( b )
74 {
75 }
76 */
77 /*!
78   Destructor
79 */
80 /*
81 OB_Browser::ToolTip::~ToolTip()
82 {
83 }
84 */
85 /*!
86   It is called when there is a possibility that a tool tip
87   should be shown and must decide whether there is a tool tip for the point
88   in the widget that this QToolTip object relates to.
89   \param pos - point co-ordinates
90 */
91 /*
92 void OB_Browser::ToolTip::maybeTip( const QPoint& pos )
93 {
94   if ( !parentWidget() || !myBrowser || !myBrowser->isShowToolTips() )
95           return;
96
97   QListView* lv = myBrowser->listView();
98
99   QListViewItem* item = lv->itemAt( pos );
100   SUIT_DataObject* obj = myBrowser->dataObject( item );
101   if ( !obj )
102     return;
103
104   QString aText = obj->toolTip();
105
106   if ( aText.isEmpty() )
107     return;
108
109   QRect aRect = lv->itemRect( item );
110
111   tip( aRect, aText );
112 }
113 */
114
115
116 /*!
117   \class OB_Browser
118   \brief Object browser widget which can be used to handle tree-like data model.
119
120   The class OB_Browser implements public API of an object browser widget
121   that can be used to display arbitrary application data in a hierarchical form.
122   It is based on Qt4 model/view architecture. 
123
124   Object browser can be used with conjuction of any custom item model inherited
125   from QAbstractItemModel class (see Qt 4 reference manual).
126
127   The class provides a functionality get/modify selection, drag-n-drop of the
128   objects, etc.
129 */
130
131 /*!
132   \brief Constructor
133   \param parent paren widget
134   \param model data model
135 */
136 OB_Browser::OB_Browser( QWidget* parent, QAbstractItemModel* model )
137 : QWidget( parent ),
138   myAutoOpenLevel( 0 )
139 {
140   // set-up tree view
141   myView = new QtxTreeView( this );                                  // create tree view
142   myView->setRootIsDecorated( true );                                // show root item
143   myView->setSelectionMode( QAbstractItemView::ExtendedSelection );  // enable extended selection mode
144   myView->setAllColumnsShowFocus( true );                            // focus is shown in all columns
145
146   // enable drag-n-drop support
147   myView->setDragDropMode( QAbstractItemView::DragDrop );            // enable both drag and drop operations
148   myView->setDropIndicatorShown( true );                             // show drag indicator on dragging
149
150   // set-up search tool
151   mySearchTool = new QtxSearchTool( this, myView );                  // create search tool
152   mySearchTool->setFrameStyle( QFrame::NoFrame | QFrame::Plain );    // do not show frame
153   mySearchTool->setActivators( QtxSearchTool::StandardKey | QtxSearchTool::SlashKey ); // set activation mode
154   mySearchTool->setSearcher( new QtxTreeViewSearcher( myView ) );    // assign searcher (for tree view)
155   
156   // layout widgets
157   QVBoxLayout* main = new QVBoxLayout( this );
158   main->addWidget( myView );
159   main->addWidget( mySearchTool );
160   main->setMargin( 0 );
161   main->setSpacing( 3 );
162
163   // TODO: decide what to do with tooltip
164   //myShowToolTips = true;
165   //myTooltip = new ToolTip( this, myView->viewport() );
166
167   // TODO: drag-n-drop works differently - SUIT_TreeModel to be updated
168   // and QTreeView needs some setup
169   //connect( myView, SIGNAL( dropped( QPtrList<QListViewItem>, QListViewItem*, int ) ),
170   //         this, SLOT( onDropped( QPtrList<QListViewItem>, QListViewItem*, int ) ) );
171   setModel( model );
172
173   connect( myView, SIGNAL( selectionChanged() ),
174            this,   SIGNAL( selectionChanged() ) );
175 }
176
177 /*!
178   \brief Destructor.
179 */
180 OB_Browser::~OB_Browser()
181 {
182   //delete myTooltip;
183   //setUpdater( 0 );
184 }
185
186 /*!
187   \brief Get data model.
188   \return data model
189   \sa setModel()
190 */
191 QAbstractItemModel* OB_Browser::model() const
192 {
193   return myView->model();
194 }
195
196 /*!
197   \brief Set data model.
198   \param model data model
199   \sa model()
200 */
201 void OB_Browser::setModel( QAbstractItemModel* model )
202 {
203   myView->setModel( model );
204   myView->update();
205   setModified();
206 }
207
208 /*!
209   \brief Get current item delegate (items renderer).
210   \return currently used item delegate
211   \sa setItemDelegate()
212 */
213 QAbstractItemDelegate* OB_Browser::itemDelegate() const
214 {
215   return myView->itemDelegate();
216 }
217
218 /*!
219   \brief Set item delegate (items renderer).
220   \param d custom item delegate
221   \sa itemDelegate()
222 */
223 void OB_Browser::setItemDelegate( QAbstractItemDelegate* d )
224 {
225   myView->setItemDelegate( d );
226 }
227
228 /*!
229   \brief Check if controls for expanding and collapsing top-level items are shown.
230   \return \c true if top-level items are decorated
231   \sa setRootIsDecorated()
232 */
233 bool OB_Browser::rootIsDecorated() const
234 {
235   return myView->rootIsDecorated();
236 }
237
238 /*!
239   \brief Show/hide controls for expanding and collapsing top-level items.
240   \param decor if \c true, top-level items are decorated
241   \sa rootIsDecorated()
242 */
243 void OB_Browser::setRootIsDecorated( const bool decor )
244 {
245   if ( decor != rootIsDecorated() )
246     myView->setRootIsDecorated( decor );
247 }
248
249 /*
250   \brief Check if "Sorting" popup menu command for the header is enabled.
251   \return \c true if "Sorting" menu command is enabled
252   \sa setSortMenuEnabled()
253 */
254 bool OB_Browser::sortMenuEnabled() const
255 {
256   return myView->sortMenuEnabled();
257 }
258
259 /*
260   \brief Enable/disable "Sorting" popup menu command for the header.
261   \param enableSortMenu if \c true, enable "Sorting" menu command
262   \sa sortMenuEnabled()
263 */
264 void OB_Browser::setSortMenuEnabled( const bool enabled )
265 {
266   if ( enabled != sortMenuEnabled() )
267     myView->setSortMenuEnabled( enabled );
268 }
269
270 /*!
271   \brief Get search tool widget.
272   \return search tool widget
273   \sa isSearchToolEnabled(), setSearchToolEnabled()
274 */
275 QtxSearchTool* OB_Browser::searchTool() const
276 {
277   return mySearchTool;
278 }
279
280 /*!
281   \brief Check if search tool is enabled.
282   \return \c true if search tool is enabled
283   \sa setSearchToolEnabled(), searchTool()
284 */
285 bool OB_Browser::isSearchToolEnabled() const
286 {
287   return mySearchTool->isEnabled();
288 }
289
290 /*!
291   \brief Enable/disable search tool.
292   \param enable pass \c true to enable search tool
293   \sa isSearchToolEnabled(), searchTool()
294 */
295 void OB_Browser::setSearchToolEnabled( const bool enable )
296 {
297   if ( mySearchTool->isEnabled() == enable )
298     return;
299
300   mySearchTool->setEnabled( enable );
301   if ( !mySearchTool->isEnabled() )
302     mySearchTool->hide();
303 }
304
305 /*!
306   \brief Get number of levels which should be automatically expanded
307   when updating the data tree.
308   \return number of levels to be auto-opened on tree updating
309   \sa setAutoOpenLevel()
310 */
311 int OB_Browser::autoOpenLevel() const
312 {
313   return myAutoOpenLevel;
314 }
315
316 /*!
317   \brief Set number of levels which should be automatically expanded
318   when updating the data tree.
319   \param levels number of levels to be auto-opened on tree updating
320   \sa autoOpenLevel()
321 */
322 void OB_Browser::setAutoOpenLevel( const int levels )
323 {
324   if ( myAutoOpenLevel != levels )
325     myAutoOpenLevel = levels;
326 }
327
328 /*!
329   \brief Expand all branches to the specified number of levels.
330
331   If \a levels value is negative, then autoOpenLevel() value is used instead.
332   
333   \param levels number of levels to be expanded
334   \sa autoOpenLevel()
335 */
336 void OB_Browser::openLevels( const int levels )
337 {
338   myView->expandLevels( levels < 0 ? autoOpenLevel() : levels );
339 }
340
341 /*!
342   \return state "are tooltips shown"
343 */
344 /*
345 bool OB_Browser::isShowToolTips()
346 {
347   return myShowToolTips;
348 }
349 */
350 /*!
351   Sets new value of state "are tooltips shown"
352   \param theDisplay - new value
353 */
354 /*
355 void OB_Browser::setShowToolTips( const bool theDisplay )
356 {
357   myShowToolTips = theDisplay;
358 }
359 */
360
361 /*!
362   \brief Get number of selected items.
363   \return number of selected items
364 */
365 int OB_Browser::numberOfSelected() const
366 {
367   // we take selection by rows
368   return myView->selectionModel() ? myView->selectionModel()->selectedRows().count() : 0;
369 }
370
371 /*!
372   \brief Get all selected items.
373   \return unsorted list of selected indexes with no duplicates
374 */
375 QModelIndexList OB_Browser::selectedIndexes() const
376 {
377   // we take selection by rows
378   return myView->selectionModel() ? myView->selectionModel()->selectedRows() : QModelIndexList();
379 }
380
381 /*!
382   \brief Get selection containing information about selected ranges.
383   \return QItemSelection instance
384 */
385 const QItemSelection OB_Browser::selection() const
386 {
387   static QItemSelection emptySel;
388   QItemSelection sel = emptySel;
389   if ( myView->selectionModel() )
390     sel = myView->selectionModel()->selection();
391   return sel;
392 }
393
394 /*!
395   \brief Select/deselect specified model index.
396   \param index model index to be selected/deselected
397   \param on if \c true, the index will be selected, otherwise - deselected
398   \param keepSelection if \c true (default) the previous selection is kept, 
399   otherwise it is first cleared
400 */
401 void OB_Browser::select( const QModelIndex& index, const bool on, const bool keepSelection )
402 {
403   if ( myView->selectionModel() ) {
404     QItemSelectionModel::SelectionFlags f = on ? QItemSelectionModel::Select : QItemSelectionModel::Deselect;
405     f = f | QItemSelectionModel::Rows;
406     if ( !keepSelection )
407       f = f | QItemSelectionModel::Clear;
408
409     myView->selectionModel()->select( index, f );
410   }
411 }
412
413 /*!
414   \brief Select/deselect specified model indices.
415   \param indexes model indices to be selected/deselected
416   \param on if \c true, the indices will be selected, otherwise - deselected
417   \param keepSelection if \c true (default) the previous selection is kept, 
418   otherwise it is first cleared
419 */
420 void OB_Browser::select( const QModelIndexList& indexes, const bool on, const bool keepSelection )
421 {
422   bool blocked = myView->signalsBlocked();
423   myView->blockSignals( true );
424
425   QModelIndex idx;
426
427   if ( !indexes.isEmpty() ) {
428     QItemSelection mysel;
429     // select by range if indexes are contiguous
430     QModelIndex first=indexes.at(0);
431     QModelIndex last=first;
432     if (indexes.size() > 1) {
433       for (int i = 1; i < indexes.size(); ++i) 
434       {
435         idx=indexes.at(i);
436         if(idx.parent().row()==last.parent().row() && idx.parent().column()==last.parent().column() &&
437            idx.row()==last.row()+1 && idx.column()==last.column())
438         {
439           // index is contiguous to last: extend the range
440           last=idx;
441         }
442         else
443         {
444           // index idx is not contiguous: create a new range
445           mysel.select(first,last);
446           first=idx;
447           last=idx;
448         }
449       }
450     }
451     mysel.select(first,last);
452
453     if ( myView->selectionModel() ) {
454       QItemSelectionModel::SelectionFlags f = on ? QItemSelectionModel::Select : QItemSelectionModel::Deselect;
455       f = f | QItemSelectionModel::Rows;
456       if ( !keepSelection )
457         f = f | QItemSelectionModel::Clear;
458       myView->selectionModel()->select( mysel, f );
459     }
460   }
461   else if ( !keepSelection )
462   {
463     myView->clearSelection();
464   }
465
466   myView->blockSignals( blocked );
467   emit( selectionChanged() );
468 }
469
470 /*!
471   \brief Check if specified model index is expanded or collapsed.
472   \param index model index
473   \return \c true if model index is expanded
474   \sa setOpen()
475 */
476 bool OB_Browser::isOpen( const QModelIndex& index ) const
477 {
478   return index.isValid() && model() && model()->hasChildren( index ) && myView->isExpanded( index );
479 }
480
481 /*!
482   \brief Expand/collapse the specified model index.
483   \param index model index
484   \param open if \c true, the index will be expanded, otherwse - collapsed
485   \sa isOpen()
486 */
487 void OB_Browser::setOpen( const QModelIndex& index, const bool open )
488 {
489   myView->setExpanded( index, open );  // hasChildren() ???
490 }
491
492 /*!
493   \brief Adjust first column width to its contents.
494 */
495 void OB_Browser::adjustWidth()
496 {
497   myView->resizeColumnToEncloseContents( 0 );
498 }
499
500 /*!
501   \brief Adjust first column width to its contents.
502 */
503 void OB_Browser::adjustFirstColumnWidth()
504 {
505   myView->resizeColumnToEncloseContents( 0 );
506 }
507
508 /*!
509   \brief Adjust all columns width to its contents except the first column.
510 */
511 void OB_Browser::adjustColumnsWidth()
512 {
513   for ( int aCol = 1; aCol < myView->header()->count(); aCol++ ) {
514     if ( myView->columnWidth( aCol ) > 0 )
515       myView->resizeColumnToEncloseContents( aCol );
516   }
517 }
518
519 /*!
520   \return SUIT object correspondint to item at position 'pos'
521   \param pos - position
522 */
523 /* TODO: removed - QTreeView::indexAt() should be used
524 SUIT_DataObject* OB_Browser::dataObjectAt( const QPoint& pos ) const
525 {
526   SUIT_DataObject* obj = 0;
527
528   QListView* lv = listView();
529   if ( lv )
530     obj = dataObject( lv->itemAt( pos ) );
531
532   return obj;
533 }
534 */
535 /*!
536   \return filter of list view
537 */
538 /* TODO: removed
539 OB_Filter* OB_Browser::filter() const
540 {
541   return myView->filter();
542 }
543 */
544 /*!
545   Changes filter of list view
546   \param f - new filter
547 */
548 /* TODO: removed
549 void OB_Browser::setFilter( OB_Filter* f )
550 {
551   myView->setFilter( f );
552 }
553 */
554 /*!
555   Sets global width mode
556   \param mode - new width mode
557 */
558 /* TODO: removed
559 void OB_Browser::setWidthMode( QListView::WidthMode mode )
560 {
561   for ( int i = 0, n = myView->columns(); i < n; i++ )
562     if( mode!=QListView::Maximum || myView->columnWidth( i )>0 )
563       myView->setColumnWidthMode( i, mode );
564 }
565 */
566 /*!
567   Updates tree
568   \param obj - start object
569   \param autoOpen - to open automatically branches of autoOpenLevel()
570   \sa autoOpenLevel()
571 */
572 /* TODO: removed
573 void OB_Browser::updateTree( SUIT_DataObject* obj, const bool autoOpen )
574 {
575 //  QTime t1 = QTime::currentTime();
576
577   if ( !obj && !(obj = getRootObject()) )
578     return;
579
580   DataObjectKey curKey;
581   DataObjectMap selObjs, openObjs;
582   DataObjectKeyMap selKeys, openKeys;
583
584   int selNum = numberOfSelected();
585
586   SUIT_DataObject* curObj = storeState( selObjs, openObjs, selKeys, openKeys, curKey );
587
588   updateView( obj );
589
590   restoreState( selObjs, openObjs, curObj, selKeys, openKeys, curKey );
591
592   if ( autoOpen )
593     autoOpenBranches();
594
595   setModified();
596
597   if ( selNum != numberOfSelected() )
598     emit selectionChanged();
599
600 //  QTime t2 = QTime::currentTime();
601 //  qDebug( QString( "update tree time = %1 msecs" ).arg( t1.msecsTo( t2 ) ) );
602 }
603 */
604 /*!
605   Replaces part of tree starting at object 'src' by tree starting at object 'trg'
606 */
607 /* TODO: removed
608 void OB_Browser::replaceTree( SUIT_DataObject* src, SUIT_DataObject* trg )
609 {
610   if ( !src || !trg || src == trg || src->root() != getRootObject() )
611     return;
612
613   DataObjectKey curKey;
614   DataObjectMap selObjs, openObjs;
615   DataObjectKeyMap selKeys, openKeys;
616
617   int selNum = numberOfSelected();
618
619   SUIT_DataObject* curObj = storeState( selObjs, openObjs, selKeys, openKeys, curKey );
620
621   SUIT_DataObject* parent = src->parent();
622   int pos = parent ? parent->childPos( src ) : -1;
623
624   src->setParent( 0 );
625
626   removeConnections( src );
627   if ( isAutoDeleteObjects() )
628     delete src;
629
630   if ( parent && pos != -1 )
631     parent->insertChild( trg, pos );
632
633   trg->setParent( parent );
634
635   updateView( trg );
636   createConnections( trg );
637
638   restoreState( selObjs, openObjs, curObj, selKeys, openKeys, curKey );
639
640   setModified();
641
642   if ( selNum != numberOfSelected() )
643     emit selectionChanged();
644 }
645 */
646 /*!
647   Adjusts width by item
648   \param item
649 */
650 /*
651 void OB_Browser::adjustWidth( QListViewItem* item )
652 {
653   while ( item )
654   {
655     item->widthChanged( 0 );
656     if ( item->isOpen() )
657       adjustWidth( item->firstChild() );
658     item = item->nextSibling();
659   }
660 }
661 */
662
663 /*!
664   \remove all items referencing current (through data objects)
665 */
666 /* TODO:
667 void OB_Browser::removeReferences( QListViewItem* item )
668 {
669   if ( !item )
670     return;
671
672   SUIT_DataObject* obj = dataObject( item );
673   obj->disconnect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
674   myItems.remove( obj );
675
676   QListViewItem* i = item->firstChild();
677   while ( i )
678   {
679     removeReferences( i );
680     i = i->nextSibling();
681   }
682 }
683 */
684 /*!
685   Connects all children to SLOT onDestroyed
686 */
687 /* TODO: move to SUIT_TreeModel
688 void OB_Browser::createConnections( SUIT_DataObject* obj )
689 {
690   if ( !obj )
691     return;
692
693   DataObjectList childList;
694   obj->children( childList, true );
695
696   childList.prepend( obj );
697
698   for ( DataObjectListIterator it( childList ); it.current(); ++it )
699     it.current()->connect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
700 }
701 */
702 /*!
703   Disconnects all children from SLOT onDestroyed
704 */
705 /* TODO: move to SUIT_TreeModel
706 void OB_Browser::removeConnections( SUIT_DataObject* obj )
707 {
708   if ( !obj )
709     return;
710
711   DataObjectList childList;
712   obj->children( childList, true );
713
714   childList.prepend( obj );
715
716   for ( DataObjectListIterator it( childList ); it.current(); ++it )
717     it.current()->disconnect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
718 }
719 */
720 /*!
721   Stores states (opened, selected) of current tree items
722   \return current item
723   \param selObjs, selKeys - maps of selected objects
724   \param openObjs, openKeys - maps of opened objects
725   \param curKey - map of current objects
726 */
727 /* TODO: to be revised
728 SUIT_DataObject* OB_Browser::storeState( DataObjectMap& selObjs, DataObjectMap& openObjs,
729                                          DataObjectKeyMap& selKeys, DataObjectKeyMap& openKeys,
730                                          DataObjectKey& curKey ) const
731 {
732   QListView* lv = listView();
733   if ( !lv )
734     return 0;
735
736   SUIT_DataObject* curObj = dataObject( lv->currentItem() );
737
738   curKey = objectKey( curObj );
739
740   for ( QListViewItemIterator it( lv ); it.current(); ++it )
741   {
742     SUIT_DataObject* obj = dataObject( it.current() );
743     if ( !obj )
744       continue;
745
746     selObjs.insert( obj, lv->isSelected( it.current() ) );
747     openObjs.insert( obj, lv->isOpen( it.current() ) );
748     if ( lv->isSelected( it.current() ) )
749       selKeys.insert( objectKey( obj ), 0 );
750     if ( lv->isOpen( it.current() ) )
751       openKeys.insert( objectKey( obj ), 0 );
752   }
753
754   return curObj;
755 }
756 */
757 /*!
758   Restores states (opened, selected) of current tree items
759   \param selObjs, selKeys - maps of selected objects
760   \param openObjs, openKeys - maps of opened objects
761   \param curKey - map of current objects
762 */
763 /* TODO: to be revised
764 void OB_Browser::restoreState( const DataObjectMap& selObjs, const DataObjectMap& openObjs,
765                                const SUIT_DataObject* curObj, const DataObjectKeyMap& selKeys,
766                                const DataObjectKeyMap& openKeys, const DataObjectKey& curKey )
767 {
768   QListView* lv = listView();
769   if ( !lv )
770     return;
771
772   bool block = lv->signalsBlocked();
773   lv->blockSignals( true );
774
775   QListViewItem* curItem = 0;
776   for ( QListViewItemIterator it( lv ); it.current(); ++it )
777   {
778     QListViewItem* item = it.current();
779     SUIT_DataObject* obj = dataObject( item );
780
781     if ( !obj )
782       continue;
783
784     DataObjectKey key = objectKey( obj );
785
786     if ( selObjs.contains( obj ) )
787     {
788       if ( selObjs[obj] && !lv->isSelected( item ) )
789         lv->setSelected( item, true );
790     }
791     else if ( !key.isNull() && selKeys.contains( key ) && !lv->isSelected( item ) )
792       lv->setSelected( item, true );
793
794     if ( openObjs.contains( obj ) )
795     {
796       bool parentOpen = true;
797       if( item && item->parent() )
798         parentOpen = item->parent()->isOpen();
799
800       if ( openObjs[obj] && parentOpen )
801         lv->setOpen( item, true );
802     }
803     else if ( !key.isNull() && openKeys.contains( key ) )
804     {
805       bool parentOpen = true;
806       if( item && item->parent() )
807         parentOpen = item->parent()->isOpen();
808
809       if( parentOpen )
810         lv->setOpen( item, true );
811     }
812
813     if ( !curItem && ( curObj == obj || ( !curKey.isNull() && curKey == key )) )
814       curItem = item;
815   }
816
817   if ( curItem )
818     lv->setCurrentItem( curItem );
819
820   lv->blockSignals( block );
821 }
822 */
823 /*!
824   Creates object key by tree item
825 */
826 /* TODO: move to SUIT_TreeModel
827 OB_Browser::DataObjectKey OB_Browser::objectKey( QListViewItem* i ) const
828 {
829   return objectKey( dataObject( i ) );
830 }
831 */
832 /*!
833   Creates object key by SUIT object
834 */
835 /* TODO: move to SUIT_TreeModel
836 OB_Browser::DataObjectKey OB_Browser::objectKey( SUIT_DataObject* obj ) const
837 {
838   if ( !obj )
839     return 0;
840
841   return DataObjectKey( obj->key() );
842 }
843 */
844
845 /*!
846   \brief Get tree view widget.
847   \return tree view widget of the object browser
848 */
849 QtxTreeView* OB_Browser::treeView() const
850 {
851   return myView;
852 }
853
854 /*!
855   \brief Process context menu request event.
856   \param e context menu event
857 */
858 void OB_Browser::contextMenuEvent( QContextMenuEvent* e )
859 {
860   QMenu* popup = new QMenu();
861   
862   createPopupMenu( popup );
863
864   Qtx::simplifySeparators( popup );
865
866   if ( !popup->actions().isEmpty() )
867     popup->exec( e->globalPos() );
868   delete popup;
869 }
870
871 /*!
872   \brief Get the time of the latest updating.
873   \return latest updating time
874 */
875 unsigned long OB_Browser::getModifiedTime() const
876
877   return myModifiedTime; 
878 }
879
880 /*!
881   \brief Update the time of the latest updating.
882 */
883 void OB_Browser::setModified()
884 {
885   myModifiedTime = clock();
886 }
887
888 /*!
889   \brief Called when "Expand all" popup menu command is activated.
890   
891   Expands all selected items recursively.
892 */
893 void OB_Browser::onExpandAll()
894 {
895   QModelIndexList indexes = selectedIndexes();
896   QModelIndex index;
897   disconnect( treeView(), SIGNAL( expanded( const QModelIndex& ) ), 
898            this,       SLOT( onExpanded( const QModelIndex& ) ) );
899   
900   foreach ( index, indexes ) {
901     myView->expandAll( index );
902   }
903   connect( treeView(), SIGNAL( expanded( const QModelIndex& ) ), 
904            this,       SLOT( onExpanded( const QModelIndex& ) ) );
905   emit(onExpanded( index));
906 }
907
908 /*!
909   \brief Called when "Collapse all" popup menu command is activated.
910   
911   Collapse all selected items recursively.
912 */
913 void OB_Browser::onCollapseAll()
914 {
915   QModelIndexList indexes = selectedIndexes();
916   QModelIndex index;
917
918   foreach ( index, indexes ) {
919     myView->collapseAll( index );
920   }
921 }
922
923 /*!
924   SLOT: called if SUIT object is destroyed
925 */
926 /* TODO: moved to SUIT_TreeModel
927 void OB_Browser::onDestroyed( SUIT_DataObject* obj )
928 {
929   removeObject( obj );
930 }
931 */
932 /*!
933   SLOT: called on finish of drag-n-drop operation
934   \param items - dragged items
935   \param item - destination (item on that they were dropped)
936   \param action - QDropEvent::Action
937 */
938   // TODO: drag-n-drop works differently - SUIT_TreeModel to be updated
939   // and QTreeView needs some setup
940 /*
941 void OB_Browser::onDropped( QPtrList<QListViewItem> items, QListViewItem* item, int action )
942 {
943   SUIT_DataObject* obj = dataObject( item );
944   if ( !obj )
945     return;
946
947   DataObjectList lst;
948   for ( QPtrListIterator<QListViewItem> it( items ); it.current(); ++it )
949   {
950     SUIT_DataObject* o = dataObject( it.current() );
951     if ( o )
952       lst.append( o );
953   }
954
955   if ( !lst.isEmpty() )
956     emit dropped( lst, obj, action );
957 }
958 */
959 /*!
960   Updates texts of items
961 */
962 /* TODO: to be removed
963 void OB_Browser::updateText()
964 {
965   if ( myColumnIds.isEmpty() )
966     return;
967
968   QListView* lv = listView();
969   if ( !lv )
970     return;
971
972   for ( QListViewItemIterator it( lv ); it.current(); ++it )
973   {
974     SUIT_DataObject* obj = dataObject( it.current() );
975     if ( !obj )
976       continue;
977
978     for( QMap<int, int>::iterator itr = myColumnIds.begin(); itr != myColumnIds.end(); ++itr )
979       it.current()->setText( itr.data(), obj->text( itr.key() ) );
980   }
981 }
982 */
983 /*!
984   \return true if item must be updated
985   \param item - item to be checked
986 */
987 /* TODO: to be revised
988 bool OB_Browser::needToUpdateTexts( QListViewItem* item ) const
989 {
990   SUIT_DataObject* obj = dataObject( item );
991   if ( !obj )
992     return false;
993
994   for( QMap<int, int>::const_iterator it = myColumnIds.begin(); it != myColumnIds.end(); ++it )
995     if( item->text( it.data() ) != obj->text( it.key() ) )
996       return true;
997   return false;
998 }
999 */
1000 /*!
1001   Updates texts of item
1002   \param item - item to be updated
1003 */
1004 /* TODO: to be revised
1005 void OB_Browser::updateText( QListViewItem* item )
1006 {
1007   SUIT_DataObject* obj = dataObject( item );
1008   if ( !obj )
1009     return;
1010
1011   for( QMap<int, int>::iterator it = myColumnIds.begin(); it != myColumnIds.end(); ++it )
1012     item->setText( it.data(), obj->text( it.key() ) );
1013 }
1014 */
1015
1016 /*!
1017   \brief Add custom actions to the popup menu.
1018   \param menu popup menu
1019 */
1020 void OB_Browser::createPopupMenu( QMenu* menu )
1021 {
1022   menu->addSeparator();
1023
1024   QModelIndexList indexes = selectedIndexes();
1025
1026   bool closed = false, opened = false;
1027   
1028   for ( QModelIndexList::Iterator it = indexes.begin(); 
1029         it != indexes.end() && !closed; ++it ) {
1030     closed = hasCollased( *it );
1031   }
1032
1033   for ( QModelIndexList::Iterator it = indexes.begin(); 
1034         it != indexes.end() && !opened; ++it ) {
1035     opened = hasExpanded( *it );
1036   }
1037
1038   if ( closed )
1039     menu->addAction( tr( "MEN_EXPAND_ALL" ), this, SLOT( onExpandAll() ) );
1040   if ( opened )
1041     menu->addAction( tr( "MEN_COLLAPSE_ALL" ), this, SLOT( onCollapseAll() ) );
1042
1043   if ( isSearchToolEnabled() ) {
1044     menu->addSeparator();
1045     menu->addAction( tr( "MEN_FIND" ), searchTool(), SLOT( find() ), QKeySequence(Qt::CTRL + Qt::Key_F) );
1046     menu->addSeparator();
1047   }
1048 }
1049
1050 /*!
1051   Expands item with all it's children
1052 */
1053 /* TODO: to be revised
1054 void OB_Browser::expand( QListViewItem* item )
1055 {
1056   if ( !item )
1057     return;
1058
1059   item->setOpen( true );
1060   for ( QListViewItem* child = item->firstChild(); child; child = child->nextSibling() )
1061     expand( child );
1062 }
1063 */
1064 /*!
1065   \brief Check if model index is collapsed or has collapsed children.
1066   \return \c true if item or one of its children is collapsed
1067 */
1068 bool OB_Browser::hasCollased( const QModelIndex& index ) const
1069 {
1070   bool result = false;
1071
1072   if ( index.isValid() && model() ) {
1073     bool hasChildren = model()->hasChildren( index );
1074     result = hasChildren && !myView->isExpanded( index );
1075     if ( !result && hasChildren ) {
1076       int rows = model()->rowCount( index );
1077       for ( int i = 0; i < rows && !result; i ++ ) {
1078         QModelIndex child = model()->index( i, 0, index );
1079         result = hasCollased( child );
1080       }
1081     }
1082   }
1083   return result;
1084 }
1085
1086 /*!
1087   \brief Check if model index is expanded or has expanded children.
1088   \return \c true if item or one of its children is expanded
1089 */
1090 bool OB_Browser::hasExpanded( const QModelIndex& index ) const
1091 {
1092   bool result = false;
1093
1094   if ( index.isValid() && model() ) {
1095     bool hasChildren = model()->hasChildren( index );
1096     result = hasChildren && myView->isExpanded( index );
1097     if ( !result && hasChildren ) {
1098       int rows = model()->rowCount( index );
1099       for ( int i = 0; i < rows && !result; i ++ ) {
1100         QModelIndex child = model()->index( i, 0, index );
1101         result = hasExpanded( child );
1102       }
1103     }
1104   }
1105   return result;
1106 }
1107
1108 /*!
1109   Removes SUIT object
1110   \param obj - SUIT object to be removed
1111   \param autoUpd - auto tree updating
1112 */
1113 /* TODO: moved to SUIT_TreeModel
1114 void OB_Browser::removeObject( SUIT_DataObject* obj, const bool autoUpd )
1115 {
1116   if ( !obj )
1117     return;
1118
1119   // Removing list view items from <myItems> recursively for all children.
1120   // Otherwise, "delete item" line will destroy all item's children,
1121   // and <myItems> will contain invalid pointers (see ~QListViewItem() description in Qt docs)
1122   DataObjectList childList;
1123   obj->children( childList, true );
1124   for ( DataObjectListIterator it( childList ); it.current(); ++it )
1125   {
1126     it.current()->disconnect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
1127     myItems.remove( it.current() );
1128   }
1129
1130   QListViewItem* item = listViewItem( obj );
1131
1132   obj->disconnect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
1133   myItems.remove( obj );
1134
1135   if ( obj == myRoot )
1136   {
1137     // remove all child list view items
1138     setRootObject( 0 );
1139     return;
1140   }
1141
1142   if( !autoUpd )
1143     return;
1144
1145   if ( isAutoUpdate() )
1146   {
1147     SUIT_DataObject* pObj = item && item->parent() ? dataObject( item->parent() ) : 0;
1148     updateTree( pObj, false );
1149   }
1150
1151   delete item;
1152 }
1153 */
1154 /*!
1155   Opens branches from 1 to autoOpenLevel()
1156   \sa autoOpenLevel()
1157 */
1158 /* TODO: to be revised
1159 void OB_Browser::autoOpenBranches()
1160 {
1161   openLevels();
1162 }
1163 */
1164 /*!
1165   Opens branch
1166   \param item
1167   \param level
1168 */
1169 /* TODO: to be revised
1170 void OB_Browser::openBranch( QListViewItem* item, const int level )
1171 {
1172   if ( level < 1 )
1173     return;
1174
1175   while ( item )
1176   {
1177     item->setOpen( true );
1178     openBranch( item->firstChild(), level - 1 );
1179     item = item->nextSibling();
1180   }
1181 }
1182 */
1183 /*!
1184   SLOT: called on double click on item, emits signal
1185 */
1186 /* TODO: to be revised
1187 void OB_Browser::onDoubleClicked( QListViewItem* item )
1188 {
1189   if ( item )
1190     emit doubleClicked( dataObject( item ) );
1191 }
1192 */
1193
1194 /*!
1195   \fn void OB_Browser::selectionChanged();
1196   \brief Emitted when selection is changed in the Object Browser.
1197 */
1198
1199 QByteArray OB_Browser::getOpenStates( int theColumn ) const
1200 {
1201   QByteArray aData;
1202   QDataStream aStream( &aData, QIODevice::WriteOnly );
1203   MapOfOpenStates aMap;
1204   const_cast<OB_Browser*>( this )->openStates( true, aMap, QModelIndex(), theColumn );
1205   MapOfOpenStates::const_iterator anIt = aMap.begin(), aLast = aMap.end();
1206   for( ; anIt!=aLast; anIt++ )
1207   {
1208     QString anEntry = anIt.key();
1209     qint32 anOpenAttr = anIt.value() ? 1 : 0;
1210     aStream << anEntry << anOpenAttr;
1211   }
1212   return aData;
1213 }
1214
1215 void OB_Browser::setOpenStates( const QByteArray& theData, int theColumn )
1216 {
1217   QByteArray* aData = const_cast<QByteArray*>( &theData );
1218   QDataStream aStream( aData, QIODevice::ReadOnly );
1219   MapOfOpenStates aMap;
1220   while( !aStream.atEnd() )
1221   {
1222     QString anEntry;
1223     qint32 anOpenAttr;
1224     aStream >> anEntry >> anOpenAttr;
1225     bool isOpen = anOpenAttr!=0;
1226     aMap[anEntry] = isOpen;
1227   }
1228   openStates( false, aMap, QModelIndex(), theColumn );
1229 }
1230
1231 void OB_Browser::openStates( bool isGet, MapOfOpenStates& theMap, const QModelIndex& theIndex, int theColumn )
1232 {
1233   if( theIndex.isValid() )
1234   {
1235     QString anEntry = theIndex.sibling( theIndex.row(), theColumn ).data().toString();
1236     bool isOpen;
1237     if( isGet )
1238     {
1239       isOpen = treeView()->isExpanded( theIndex );
1240       theMap[anEntry] = isOpen;
1241     }
1242     else
1243     {
1244       isOpen = theMap.contains( anEntry ) ? theMap[anEntry] : false;
1245       treeView()->setExpanded( theIndex, isOpen );
1246     }
1247   }
1248
1249   const QAbstractItemModel* aModel = model();
1250
1251   int n = aModel->rowCount( theIndex );
1252   for( int i=0; i<n; i++ )
1253   {
1254     QModelIndex aChild = aModel->index( i, 0, theIndex );
1255     openStates( isGet, theMap, aChild, theColumn );
1256   }
1257 }