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