Salome HOME
refs #416: to store the open state of object browser items in the binary array
[modules/gui.git] / src / ObjBrowser / OB_Browser.cxx
1 // Copyright (C) 2007-2014  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().internalId()==last.parent().internalId() && idx.row()==last.row()+1 && idx.column()==last.column())
437         {
438           // index is contiguous to last: extend the range
439           last=idx;
440         }
441         else
442         {
443           // index idx is not contiguous: create a new range
444           mysel.select(first,last);
445           first=idx;
446           last=idx;
447         }
448       }
449     }
450     mysel.select(first,last);
451
452     if ( myView->selectionModel() ) {
453       QItemSelectionModel::SelectionFlags f = on ? QItemSelectionModel::Select : QItemSelectionModel::Deselect;
454       f = f | QItemSelectionModel::Rows;
455       if ( !keepSelection )
456         f = f | QItemSelectionModel::Clear;
457       myView->selectionModel()->select( mysel, f );
458     }
459   }
460   else if ( !keepSelection )
461   {
462     myView->clearSelection();
463   }
464
465   myView->blockSignals( blocked );
466   emit( selectionChanged() );
467 }
468
469 /*!
470   \brief Check if specified model index is expanded or collapsed.
471   \param index model index
472   \return \c true if model index is expanded
473   \sa setOpen()
474 */
475 bool OB_Browser::isOpen( const QModelIndex& index ) const
476 {
477   return index.isValid() && model() && model()->hasChildren( index ) && myView->isExpanded( index );
478 }
479
480 /*!
481   \brief Expand/collapse the specified model index.
482   \param index model index
483   \param open if \c true, the index will be expanded, otherwse - collapsed
484   \sa isOpen()
485 */
486 void OB_Browser::setOpen( const QModelIndex& index, const bool open )
487 {
488   myView->setExpanded( index, open );  // hasChildren() ???
489 }
490
491 /*!
492   \brief Adjust first column width to its contents.
493 */
494 void OB_Browser::adjustWidth()
495 {
496   myView->resizeColumnToEncloseContents( 0 );
497 }
498
499 /*!
500   \brief Adjust first column width to its contents.
501 */
502 void OB_Browser::adjustFirstColumnWidth()
503 {
504   myView->resizeColumnToEncloseContents( 0 );
505 }
506
507 /*!
508   \brief Adjust all columns width to its contents except the first column.
509 */
510 void OB_Browser::adjustColumnsWidth()
511 {
512   for ( int aCol = 1; aCol < myView->header()->count(); aCol++ ) {
513     if ( myView->columnWidth( aCol ) > 0 )
514       myView->resizeColumnToEncloseContents( aCol );
515   }
516 }
517
518 /*!
519   \return SUIT object correspondint to item at position 'pos'
520   \param pos - position
521 */
522 /* TODO: removed - QTreeView::indexAt() should be used
523 SUIT_DataObject* OB_Browser::dataObjectAt( const QPoint& pos ) const
524 {
525   SUIT_DataObject* obj = 0;
526
527   QListView* lv = listView();
528   if ( lv )
529     obj = dataObject( lv->itemAt( pos ) );
530
531   return obj;
532 }
533 */
534 /*!
535   \return filter of list view
536 */
537 /* TODO: removed
538 OB_Filter* OB_Browser::filter() const
539 {
540   return myView->filter();
541 }
542 */
543 /*!
544   Changes filter of list view
545   \param f - new filter
546 */
547 /* TODO: removed
548 void OB_Browser::setFilter( OB_Filter* f )
549 {
550   myView->setFilter( f );
551 }
552 */
553 /*!
554   Sets global width mode
555   \param mode - new width mode
556 */
557 /* TODO: removed
558 void OB_Browser::setWidthMode( QListView::WidthMode mode )
559 {
560   for ( int i = 0, n = myView->columns(); i < n; i++ )
561     if( mode!=QListView::Maximum || myView->columnWidth( i )>0 )
562       myView->setColumnWidthMode( i, mode );
563 }
564 */
565 /*!
566   Updates tree
567   \param obj - start object
568   \param autoOpen - to open automatically branches of autoOpenLevel()
569   \sa autoOpenLevel()
570 */
571 /* TODO: removed
572 void OB_Browser::updateTree( SUIT_DataObject* obj, const bool autoOpen )
573 {
574 //  QTime t1 = QTime::currentTime();
575
576   if ( !obj && !(obj = getRootObject()) )
577     return;
578
579   DataObjectKey curKey;
580   DataObjectMap selObjs, openObjs;
581   DataObjectKeyMap selKeys, openKeys;
582
583   int selNum = numberOfSelected();
584
585   SUIT_DataObject* curObj = storeState( selObjs, openObjs, selKeys, openKeys, curKey );
586
587   updateView( obj );
588
589   restoreState( selObjs, openObjs, curObj, selKeys, openKeys, curKey );
590
591   if ( autoOpen )
592     autoOpenBranches();
593
594   setModified();
595
596   if ( selNum != numberOfSelected() )
597     emit selectionChanged();
598
599 //  QTime t2 = QTime::currentTime();
600 //  qDebug( QString( "update tree time = %1 msecs" ).arg( t1.msecsTo( t2 ) ) );
601 }
602 */
603 /*!
604   Replaces part of tree starting at object 'src' by tree starting at object 'trg'
605 */
606 /* TODO: removed
607 void OB_Browser::replaceTree( SUIT_DataObject* src, SUIT_DataObject* trg )
608 {
609   if ( !src || !trg || src == trg || src->root() != getRootObject() )
610     return;
611
612   DataObjectKey curKey;
613   DataObjectMap selObjs, openObjs;
614   DataObjectKeyMap selKeys, openKeys;
615
616   int selNum = numberOfSelected();
617
618   SUIT_DataObject* curObj = storeState( selObjs, openObjs, selKeys, openKeys, curKey );
619
620   SUIT_DataObject* parent = src->parent();
621   int pos = parent ? parent->childPos( src ) : -1;
622
623   src->setParent( 0 );
624
625   removeConnections( src );
626   if ( isAutoDeleteObjects() )
627     delete src;
628
629   if ( parent && pos != -1 )
630     parent->insertChild( trg, pos );
631
632   trg->setParent( parent );
633
634   updateView( trg );
635   createConnections( trg );
636
637   restoreState( selObjs, openObjs, curObj, selKeys, openKeys, curKey );
638
639   setModified();
640
641   if ( selNum != numberOfSelected() )
642     emit selectionChanged();
643 }
644 */
645 /*!
646   Adjusts width by item
647   \param item
648 */
649 /*
650 void OB_Browser::adjustWidth( QListViewItem* item )
651 {
652   while ( item )
653   {
654     item->widthChanged( 0 );
655     if ( item->isOpen() )
656       adjustWidth( item->firstChild() );
657     item = item->nextSibling();
658   }
659 }
660 */
661
662 /*!
663   \remove all items referencing current (through data objects)
664 */
665 /* TODO:
666 void OB_Browser::removeReferences( QListViewItem* item )
667 {
668   if ( !item )
669     return;
670
671   SUIT_DataObject* obj = dataObject( item );
672   obj->disconnect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
673   myItems.remove( obj );
674
675   QListViewItem* i = item->firstChild();
676   while ( i )
677   {
678     removeReferences( i );
679     i = i->nextSibling();
680   }
681 }
682 */
683 /*!
684   Connects all children to SLOT onDestroyed
685 */
686 /* TODO: move to SUIT_TreeModel
687 void OB_Browser::createConnections( SUIT_DataObject* obj )
688 {
689   if ( !obj )
690     return;
691
692   DataObjectList childList;
693   obj->children( childList, true );
694
695   childList.prepend( obj );
696
697   for ( DataObjectListIterator it( childList ); it.current(); ++it )
698     it.current()->connect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
699 }
700 */
701 /*!
702   Disconnects all children from SLOT onDestroyed
703 */
704 /* TODO: move to SUIT_TreeModel
705 void OB_Browser::removeConnections( SUIT_DataObject* obj )
706 {
707   if ( !obj )
708     return;
709
710   DataObjectList childList;
711   obj->children( childList, true );
712
713   childList.prepend( obj );
714
715   for ( DataObjectListIterator it( childList ); it.current(); ++it )
716     it.current()->disconnect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
717 }
718 */
719 /*!
720   Stores states (opened, selected) of current tree items
721   \return current item
722   \param selObjs, selKeys - maps of selected objects
723   \param openObjs, openKeys - maps of opened objects
724   \param curKey - map of current objects
725 */
726 /* TODO: to be revised
727 SUIT_DataObject* OB_Browser::storeState( DataObjectMap& selObjs, DataObjectMap& openObjs,
728                                          DataObjectKeyMap& selKeys, DataObjectKeyMap& openKeys,
729                                          DataObjectKey& curKey ) const
730 {
731   QListView* lv = listView();
732   if ( !lv )
733     return 0;
734
735   SUIT_DataObject* curObj = dataObject( lv->currentItem() );
736
737   curKey = objectKey( curObj );
738
739   for ( QListViewItemIterator it( lv ); it.current(); ++it )
740   {
741     SUIT_DataObject* obj = dataObject( it.current() );
742     if ( !obj )
743       continue;
744
745     selObjs.insert( obj, lv->isSelected( it.current() ) );
746     openObjs.insert( obj, lv->isOpen( it.current() ) );
747     if ( lv->isSelected( it.current() ) )
748       selKeys.insert( objectKey( obj ), 0 );
749     if ( lv->isOpen( it.current() ) )
750       openKeys.insert( objectKey( obj ), 0 );
751   }
752
753   return curObj;
754 }
755 */
756 /*!
757   Restores states (opened, selected) of current tree items
758   \param selObjs, selKeys - maps of selected objects
759   \param openObjs, openKeys - maps of opened objects
760   \param curKey - map of current objects
761 */
762 /* TODO: to be revised
763 void OB_Browser::restoreState( const DataObjectMap& selObjs, const DataObjectMap& openObjs,
764                                const SUIT_DataObject* curObj, const DataObjectKeyMap& selKeys,
765                                const DataObjectKeyMap& openKeys, const DataObjectKey& curKey )
766 {
767   QListView* lv = listView();
768   if ( !lv )
769     return;
770
771   bool block = lv->signalsBlocked();
772   lv->blockSignals( true );
773
774   QListViewItem* curItem = 0;
775   for ( QListViewItemIterator it( lv ); it.current(); ++it )
776   {
777     QListViewItem* item = it.current();
778     SUIT_DataObject* obj = dataObject( item );
779
780     if ( !obj )
781       continue;
782
783     DataObjectKey key = objectKey( obj );
784
785     if ( selObjs.contains( obj ) )
786     {
787       if ( selObjs[obj] && !lv->isSelected( item ) )
788         lv->setSelected( item, true );
789     }
790     else if ( !key.isNull() && selKeys.contains( key ) && !lv->isSelected( item ) )
791       lv->setSelected( item, true );
792
793     if ( openObjs.contains( obj ) )
794     {
795       bool parentOpen = true;
796       if( item && item->parent() )
797         parentOpen = item->parent()->isOpen();
798
799       if ( openObjs[obj] && parentOpen )
800         lv->setOpen( item, true );
801     }
802     else if ( !key.isNull() && openKeys.contains( key ) )
803     {
804       bool parentOpen = true;
805       if( item && item->parent() )
806         parentOpen = item->parent()->isOpen();
807
808       if( parentOpen )
809         lv->setOpen( item, true );
810     }
811
812     if ( !curItem && ( curObj == obj || ( !curKey.isNull() && curKey == key )) )
813       curItem = item;
814   }
815
816   if ( curItem )
817     lv->setCurrentItem( curItem );
818
819   lv->blockSignals( block );
820 }
821 */
822 /*!
823   Creates object key by tree item
824 */
825 /* TODO: move to SUIT_TreeModel
826 OB_Browser::DataObjectKey OB_Browser::objectKey( QListViewItem* i ) const
827 {
828   return objectKey( dataObject( i ) );
829 }
830 */
831 /*!
832   Creates object key by SUIT object
833 */
834 /* TODO: move to SUIT_TreeModel
835 OB_Browser::DataObjectKey OB_Browser::objectKey( SUIT_DataObject* obj ) const
836 {
837   if ( !obj )
838     return 0;
839
840   return DataObjectKey( obj->key() );
841 }
842 */
843
844 /*!
845   \brief Get tree view widget.
846   \return tree view widget of the object browser
847 */
848 QtxTreeView* OB_Browser::treeView() const
849 {
850   return myView;
851 }
852
853 /*!
854   \brief Process context menu request event.
855   \param e context menu event
856 */
857 void OB_Browser::contextMenuEvent( QContextMenuEvent* e )
858 {
859   QMenu* popup = new QMenu();
860   
861   createPopupMenu( popup );
862
863   Qtx::simplifySeparators( popup );
864
865   if ( !popup->actions().isEmpty() )
866     popup->exec( e->globalPos() );
867   delete popup;
868 }
869
870 /*!
871   \brief Get the time of the latest updating.
872   \return latest updating time
873 */
874 unsigned long OB_Browser::getModifiedTime() const
875
876   return myModifiedTime; 
877 }
878
879 /*!
880   \brief Update the time of the latest updating.
881 */
882 void OB_Browser::setModified()
883 {
884   myModifiedTime = clock();
885 }
886
887 /*!
888   \brief Called when "Expand all" popup menu command is activated.
889   
890   Expands all selected items recursively.
891 */
892 void OB_Browser::onExpandAll()
893 {
894   QModelIndexList indexes = selectedIndexes();
895   QModelIndex index;
896   disconnect( treeView(), SIGNAL( expanded( const QModelIndex& ) ), 
897            this,       SLOT( onExpanded( const QModelIndex& ) ) );
898   
899   foreach ( index, indexes ) {
900     myView->expandAll( index );
901   }
902   connect( treeView(), SIGNAL( expanded( const QModelIndex& ) ), 
903            this,       SLOT( onExpanded( const QModelIndex& ) ) );
904   emit(onExpanded( index));
905 }
906
907 /*!
908   \brief Called when "Collapse all" popup menu command is activated.
909   
910   Collapse all selected items recursively.
911 */
912 void OB_Browser::onCollapseAll()
913 {
914   QModelIndexList indexes = selectedIndexes();
915   QModelIndex index;
916
917   foreach ( index, indexes ) {
918     myView->collapseAll( index );
919   }
920 }
921
922 /*!
923   SLOT: called if SUIT object is destroyed
924 */
925 /* TODO: moved to SUIT_TreeModel
926 void OB_Browser::onDestroyed( SUIT_DataObject* obj )
927 {
928   removeObject( obj );
929 }
930 */
931 /*!
932   SLOT: called on finish of drag-n-drop operation
933   \param items - dragged items
934   \param item - destination (item on that they were dropped)
935   \param action - QDropEvent::Action
936 */
937   // TODO: drag-n-drop works differently - SUIT_TreeModel to be updated
938   // and QTreeView needs some setup
939 /*
940 void OB_Browser::onDropped( QPtrList<QListViewItem> items, QListViewItem* item, int action )
941 {
942   SUIT_DataObject* obj = dataObject( item );
943   if ( !obj )
944     return;
945
946   DataObjectList lst;
947   for ( QPtrListIterator<QListViewItem> it( items ); it.current(); ++it )
948   {
949     SUIT_DataObject* o = dataObject( it.current() );
950     if ( o )
951       lst.append( o );
952   }
953
954   if ( !lst.isEmpty() )
955     emit dropped( lst, obj, action );
956 }
957 */
958 /*!
959   Updates texts of items
960 */
961 /* TODO: to be removed
962 void OB_Browser::updateText()
963 {
964   if ( myColumnIds.isEmpty() )
965     return;
966
967   QListView* lv = listView();
968   if ( !lv )
969     return;
970
971   for ( QListViewItemIterator it( lv ); it.current(); ++it )
972   {
973     SUIT_DataObject* obj = dataObject( it.current() );
974     if ( !obj )
975       continue;
976
977     for( QMap<int, int>::iterator itr = myColumnIds.begin(); itr != myColumnIds.end(); ++itr )
978       it.current()->setText( itr.data(), obj->text( itr.key() ) );
979   }
980 }
981 */
982 /*!
983   \return true if item must be updated
984   \param item - item to be checked
985 */
986 /* TODO: to be revised
987 bool OB_Browser::needToUpdateTexts( QListViewItem* item ) const
988 {
989   SUIT_DataObject* obj = dataObject( item );
990   if ( !obj )
991     return false;
992
993   for( QMap<int, int>::const_iterator it = myColumnIds.begin(); it != myColumnIds.end(); ++it )
994     if( item->text( it.data() ) != obj->text( it.key() ) )
995       return true;
996   return false;
997 }
998 */
999 /*!
1000   Updates texts of item
1001   \param item - item to be updated
1002 */
1003 /* TODO: to be revised
1004 void OB_Browser::updateText( QListViewItem* item )
1005 {
1006   SUIT_DataObject* obj = dataObject( item );
1007   if ( !obj )
1008     return;
1009
1010   for( QMap<int, int>::iterator it = myColumnIds.begin(); it != myColumnIds.end(); ++it )
1011     item->setText( it.data(), obj->text( it.key() ) );
1012 }
1013 */
1014
1015 /*!
1016   \brief Add custom actions to the popup menu.
1017   \param menu popup menu
1018 */
1019 void OB_Browser::createPopupMenu( QMenu* menu )
1020 {
1021   menu->addSeparator();
1022
1023   QModelIndexList indexes = selectedIndexes();
1024
1025   bool closed = false, opened = false;
1026   
1027   for ( QModelIndexList::Iterator it = indexes.begin(); 
1028         it != indexes.end() && !closed; ++it ) {
1029     closed = hasCollased( *it );
1030   }
1031
1032   for ( QModelIndexList::Iterator it = indexes.begin(); 
1033         it != indexes.end() && !opened; ++it ) {
1034     opened = hasExpanded( *it );
1035   }
1036
1037   if ( closed )
1038     menu->addAction( tr( "MEN_EXPAND_ALL" ), this, SLOT( onExpandAll() ) );
1039   if ( opened )
1040     menu->addAction( tr( "MEN_COLLAPSE_ALL" ), this, SLOT( onCollapseAll() ) );
1041
1042   if ( isSearchToolEnabled() ) {
1043     menu->addSeparator();
1044     menu->addAction( tr( "MEN_FIND" ), searchTool(), SLOT( find() ), QKeySequence(Qt::CTRL + Qt::Key_F) );
1045     menu->addSeparator();
1046   }
1047 }
1048
1049 /*!
1050   Expands item with all it's children
1051 */
1052 /* TODO: to be revised
1053 void OB_Browser::expand( QListViewItem* item )
1054 {
1055   if ( !item )
1056     return;
1057
1058   item->setOpen( true );
1059   for ( QListViewItem* child = item->firstChild(); child; child = child->nextSibling() )
1060     expand( child );
1061 }
1062 */
1063 /*!
1064   \brief Check if model index is collapsed or has collapsed children.
1065   \return \c true if item or one of its children is collapsed
1066 */
1067 bool OB_Browser::hasCollased( const QModelIndex& index ) const
1068 {
1069   bool result = false;
1070
1071   if ( index.isValid() && model() ) {
1072     bool hasChildren = model()->hasChildren( index );
1073     result = hasChildren && !myView->isExpanded( index );
1074     if ( !result && hasChildren ) {
1075       int rows = model()->rowCount( index );
1076       for ( int i = 0; i < rows && !result; i ++ ) {
1077         QModelIndex child = model()->index( i, 0, index );
1078         result = hasCollased( child );
1079       }
1080     }
1081   }
1082   return result;
1083 }
1084
1085 /*!
1086   \brief Check if model index is expanded or has expanded children.
1087   \return \c true if item or one of its children is expanded
1088 */
1089 bool OB_Browser::hasExpanded( const QModelIndex& index ) const
1090 {
1091   bool result = false;
1092
1093   if ( index.isValid() && model() ) {
1094     bool hasChildren = model()->hasChildren( index );
1095     result = hasChildren && myView->isExpanded( index );
1096     if ( !result && hasChildren ) {
1097       int rows = model()->rowCount( index );
1098       for ( int i = 0; i < rows && !result; i ++ ) {
1099         QModelIndex child = model()->index( i, 0, index );
1100         result = hasExpanded( child );
1101       }
1102     }
1103   }
1104   return result;
1105 }
1106
1107 /*!
1108   Removes SUIT object
1109   \param obj - SUIT object to be removed
1110   \param autoUpd - auto tree updating
1111 */
1112 /* TODO: moved to SUIT_TreeModel
1113 void OB_Browser::removeObject( SUIT_DataObject* obj, const bool autoUpd )
1114 {
1115   if ( !obj )
1116     return;
1117
1118   // Removing list view items from <myItems> recursively for all children.
1119   // Otherwise, "delete item" line will destroy all item's children,
1120   // and <myItems> will contain invalid pointers (see ~QListViewItem() description in Qt docs)
1121   DataObjectList childList;
1122   obj->children( childList, true );
1123   for ( DataObjectListIterator it( childList ); it.current(); ++it )
1124   {
1125     it.current()->disconnect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
1126     myItems.remove( it.current() );
1127   }
1128
1129   QListViewItem* item = listViewItem( obj );
1130
1131   obj->disconnect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
1132   myItems.remove( obj );
1133
1134   if ( obj == myRoot )
1135   {
1136     // remove all child list view items
1137     setRootObject( 0 );
1138     return;
1139   }
1140
1141   if( !autoUpd )
1142     return;
1143
1144   if ( isAutoUpdate() )
1145   {
1146     SUIT_DataObject* pObj = item && item->parent() ? dataObject( item->parent() ) : 0;
1147     updateTree( pObj, false );
1148   }
1149
1150   delete item;
1151 }
1152 */
1153 /*!
1154   Opens branches from 1 to autoOpenLevel()
1155   \sa autoOpenLevel()
1156 */
1157 /* TODO: to be revised
1158 void OB_Browser::autoOpenBranches()
1159 {
1160   openLevels();
1161 }
1162 */
1163 /*!
1164   Opens branch
1165   \param item
1166   \param level
1167 */
1168 /* TODO: to be revised
1169 void OB_Browser::openBranch( QListViewItem* item, const int level )
1170 {
1171   if ( level < 1 )
1172     return;
1173
1174   while ( item )
1175   {
1176     item->setOpen( true );
1177     openBranch( item->firstChild(), level - 1 );
1178     item = item->nextSibling();
1179   }
1180 }
1181 */
1182 /*!
1183   SLOT: called on double click on item, emits signal
1184 */
1185 /* TODO: to be revised
1186 void OB_Browser::onDoubleClicked( QListViewItem* item )
1187 {
1188   if ( item )
1189     emit doubleClicked( dataObject( item ) );
1190 }
1191 */
1192
1193 /*!
1194   \fn void OB_Browser::selectionChanged();
1195   \brief Emitted when selection is changed in the Object Browser.
1196 */
1197
1198 QByteArray OB_Browser::getOpenStates( int theColumn ) const
1199 {
1200   QByteArray aData;
1201   QDataStream aStream( &aData, QIODevice::WriteOnly );
1202   MapOfOpenStates aMap;
1203   const_cast<OB_Browser*>( this )->openStates( true, aMap, QModelIndex(), theColumn );
1204   MapOfOpenStates::const_iterator anIt = aMap.begin(), aLast = aMap.end();
1205   for( ; anIt!=aLast; anIt++ )
1206   {
1207     QString anEntry = anIt.key();
1208     qint32 anOpenAttr = anIt.value() ? 1 : 0;
1209     aStream << anEntry << anOpenAttr;
1210   }
1211   return aData;
1212 }
1213
1214 void OB_Browser::setOpenStates( const QByteArray& theData, int theColumn )
1215 {
1216   QByteArray* aData = const_cast<QByteArray*>( &theData );
1217   QDataStream aStream( aData, QIODevice::ReadOnly );
1218   MapOfOpenStates aMap;
1219   while( !aStream.atEnd() )
1220   {
1221     QString anEntry;
1222     qint32 anOpenAttr;
1223     aStream >> anEntry >> anOpenAttr;
1224     bool isOpen = anOpenAttr!=0;
1225     aMap[anEntry] = isOpen;
1226   }
1227   openStates( false, aMap, QModelIndex(), theColumn );
1228 }
1229
1230 void OB_Browser::openStates( bool isGet, MapOfOpenStates& theMap, const QModelIndex& theIndex, int theColumn )
1231 {
1232   if( theIndex.isValid() )
1233   {
1234     QString anEntry = theIndex.sibling( theIndex.row(), theColumn ).data().toString();
1235     bool isOpen;
1236     if( isGet )
1237     {
1238       isOpen = treeView()->isExpanded( theIndex );
1239       theMap[anEntry] = isOpen;
1240     }
1241     else
1242     {
1243       isOpen = theMap.contains( anEntry ) ? theMap[anEntry] : false;
1244       treeView()->setExpanded( theIndex, isOpen );
1245     }
1246   }
1247
1248   const QAbstractItemModel* aModel = model();
1249
1250   int n = aModel->rowCount( theIndex );
1251   for( int i=0; i<n; i++ )
1252   {
1253     QModelIndex aChild = aModel->index( i, 0, theIndex );
1254     openStates( isGet, theMap, aChild, theColumn );
1255   }
1256 }