]> SALOME platform Git repositories - modules/gui.git/blob - src/ObjBrowser/OB_Browser.cxx
Salome HOME
3b408284a74c0118641a0d7437ffb2798328fc92
[modules/gui.git] / src / ObjBrowser / OB_Browser.cxx
1 // Copyright (C) 2005  OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
2 // 
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either 
6 // version 2.1 of the License.
7 // 
8 // This library is distributed in the hope that it will be useful 
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public  
14 // License along with this library; if not, write to the Free Software 
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/
18 //
19 #include "OB_Browser.h"
20
21 #include "OB_Filter.h"
22 #include "OB_ListItem.h"
23 #include "OB_ListView.h"
24
25 #include <SUIT_DataObjectIterator.h>
26 #include <SUIT_TreeSync.h>
27
28 #include <qcursor.h>
29 #include <qlayout.h>
30 #include <qtooltip.h>
31 #include <qpainter.h>
32 #include <qwmatrix.h>
33 #include <qlistview.h>
34 #include <qpopupmenu.h>
35 #include <qdatetime.h>
36
37 #include <time.h>
38
39 /*!
40   \class  OB_Browser::ToolTip
41   Tool tip for OB_Browser.
42 */
43
44 class OB_Browser::ToolTip : public QToolTip
45 {
46 public:
47   ToolTip( OB_Browser* b, QWidget* p = 0 );
48   virtual ~ToolTip();
49
50   void        maybeTip( const QPoint& );
51
52 private:
53   OB_Browser* myBrowser;
54 };
55
56 /*!
57   Constructor
58 */
59 OB_Browser::ToolTip::ToolTip( OB_Browser* b, QWidget* p )
60 : QToolTip( p ),
61 myBrowser( b )
62 {
63 }
64
65 /*!
66   Destructor
67 */
68 OB_Browser::ToolTip::~ToolTip()
69 {
70 }
71
72 /*!
73   It is called when there is a possibility that a tool tip
74   should be shown and must decide whether there is a tool tip for the point
75   in the widget that this QToolTip object relates to.
76   \param pos - point co-ordinates
77 */
78 void OB_Browser::ToolTip::maybeTip( const QPoint& pos )
79 {
80   if ( !parentWidget() || !myBrowser || !myBrowser->isShowToolTips() )
81           return;
82
83   QListView* lv = myBrowser->listView();
84
85   QListViewItem* item = lv->itemAt( pos );
86   SUIT_DataObject* obj = myBrowser->dataObject( item );
87   if ( !obj )
88     return;
89
90   QString aText = obj->toolTip();
91
92   if ( aText.isEmpty() )
93     return;
94
95   QRect aRect = lv->itemRect( item );
96
97   tip( aRect, aText );
98 }
99
100
101 typedef SUIT_DataObject*   ObjPtr;
102 typedef OB_ListItem*       ItemPtr;
103
104 /*!
105    \class  OB_BrowserSync
106    Auxiliary class for synchronizing tree of SUIT_DataObjects and list view items
107 */
108 class OB_BrowserSync
109 {
110 public:
111   OB_BrowserSync( OB_Browser* );
112   bool     isEqual( const ObjPtr&, const ItemPtr& ) const;
113   ObjPtr   nullSrc() const;
114   ItemPtr  nullTrg() const;
115   ItemPtr  createItem( const ObjPtr&, const ItemPtr&, const ItemPtr&, const bool ) const;
116   void     updateItem( const ItemPtr& ) const;
117   void     deleteItemWithChildren( const ItemPtr& ) const;
118   void     children( const ObjPtr&, QValueList<ObjPtr>& ) const;
119   void     children( const ItemPtr&, QValueList<ItemPtr>& ) const;
120   ItemPtr  parent( const ItemPtr& ) const;
121 private:
122   bool     needUpdate( const ItemPtr& ) const;
123   OB_Browser*   myBrowser;
124 };
125
126
127 /*!
128   Constructor
129 */
130 OB_BrowserSync::OB_BrowserSync( OB_Browser* ob )
131 : myBrowser( ob )
132 {
133 }
134
135 /*!
136   \return true if item must be updated
137   \param item - item to be checked
138 */
139 bool OB_BrowserSync::needUpdate( const ItemPtr& item ) const
140 {
141   bool update = false;
142   if ( item ) {
143     SUIT_DataObject* obj = item->dataObject();
144     if ( obj ) {
145       // 1. check text
146       update = ( item->text( 0 ) != obj->name() ) || myBrowser->needToUpdateTexts( item );
147
148       if ( !update ) { 
149         // 2. check pixmap (compare serialNumber()-s)
150         QPixmap objPix = obj->icon();
151         const QPixmap* itemPix = item->pixmap( 0 );
152         update = (  objPix.isNull() && (  itemPix && !itemPix->isNull() ) ) || 
153                  ( !objPix.isNull() && ( !itemPix ||  itemPix->isNull() ) ); 
154         if ( !update && !objPix.isNull() && itemPix && !itemPix->isNull() ) {
155           int aIconW = objPix.width();
156           if( aIconW > 20 ) {
157             QWMatrix aM;
158             double aScale = 20.0 / aIconW;
159             aM.scale( aScale, aScale );
160             objPix = objPix.xForm( aM );
161           }
162           update = ( objPix.serialNumber() != itemPix->serialNumber() );
163         }
164       }
165     }
166   }
167   return update;
168 }
169
170 /*!
171   Updates item
172   \param p - item
173 */
174 void OB_BrowserSync::updateItem( const ItemPtr& p ) const
175 {
176   if ( p && needUpdate( p ) ) { 
177     //    printf( "--- needUpdate for %s = true ---\n", p->text( 0 ).latin1() );
178     myBrowser->updateText( p );
179     p->update();
180   }
181 }
182
183 /*!
184   Creates item by SUIT object
185   \param src - corresponding SUIT object
186   \param parent - parent for item
187   \param after - previous sibling for item
188   \param prepend - item must be added to start of children list
189 */
190 ItemPtr OB_BrowserSync::createItem( const ObjPtr& src,
191                                     const ItemPtr& parent, const ItemPtr& after,
192                                     const bool prepend ) const
193 {
194   ItemPtr i = myBrowser ? dynamic_cast<ItemPtr>( myBrowser->createItem( src, parent, after, prepend ) ) : 0;
195   if( i )
196     i->setOpen( src->isOpen() );
197   return i;
198 }
199
200 /*!
201   Deletes object with all children
202   \param i - item
203 */
204 void OB_BrowserSync::deleteItemWithChildren( const ItemPtr& i ) const
205 {
206   if( myBrowser && myBrowser->myItems.contains( i->dataObject() ) )
207   {
208     myBrowser->removeReferences( i );
209     delete i;
210   }
211 }
212
213 /*!
214   \return true if objects correspond each other at all
215   \param p - suit object
216   \param q - object browser item
217 */
218 bool OB_BrowserSync::isEqual( const ObjPtr& p, const ItemPtr& q ) const
219 {
220   bool isRoot = p==myBrowser->getRootObject() && !q,
221        isEq = p && q && q->dataObject()==p;
222   return isRoot || ( !p && !q ) || isEq;
223 }
224
225 /*!
226   \return null suit object
227 */
228 ObjPtr OB_BrowserSync::nullSrc() const
229 {
230   return 0;
231 }
232
233 /*!
234   \return null item
235 */
236 ItemPtr OB_BrowserSync::nullTrg() const
237 {
238   return 0;
239 }
240
241 /*!
242   Fills list with children of SUIT object
243   \param p - SUIT object
244   \param ch - list to be filled
245 */
246 void OB_BrowserSync::children( const ObjPtr& p, QValueList<ObjPtr>& ch ) const
247 {
248   DataObjectList l;
249   if( p )
250   {
251     p->children( l );
252     ch.clear();
253     for( SUIT_DataObject* o = l.first(); o; o = l.next() )
254       ch.append( o );
255   }
256 }
257
258 /*!
259   Fills list with children of item
260   \param p - item
261   \param ch - list to be filled
262 */
263 void OB_BrowserSync::children( const ItemPtr& p, QValueList<ItemPtr>& ch ) const
264 {
265   for( QListViewItem* item = p ? p->firstChild() : myBrowser->listView()->firstChild(); item; item = item->nextSibling() )
266   {
267     ItemPtr p = dynamic_cast<ItemPtr>( item );
268     if( p )
269       ch.append( p );
270   }
271 }
272
273 /*!
274   \return parent of item
275   \param p - item
276 */
277 ItemPtr OB_BrowserSync::parent( const ItemPtr& p ) const
278 {
279   return p ? dynamic_cast<ItemPtr>( p->parent() ) : 0;
280 }
281
282
283 /*!
284   Constructor
285 */
286 OB_Browser::OB_Browser( QWidget* parent, SUIT_DataObject* root )
287 : QFrame( parent ),
288
289 myRoot( 0 ),
290 myTooltip( 0 ),
291 myAutoOpenLevel( 0 ),
292 myAutoUpdate( false ),
293 myAutoDelObjs( false ),
294 myRootDecorated( true )
295 {
296   myView = new OB_ListView( QtxListView::HeaderAuto, this );
297   myView->setAppropriate( myView->addColumn( "Data" ), false );
298   myView->setSorting( -1 );
299   myView->setRootIsDecorated( true );
300   myView->setSelectionMode( QListView::Extended );
301   myView->installEventFilter( this );
302   myView->viewport()->installEventFilter( this );
303
304   QVBoxLayout* main = new QVBoxLayout( this );
305   main->addWidget( myView );
306
307   myShowToolTips = true;
308   myTooltip = new ToolTip( this, myView->viewport() );
309
310   connect( myView, SIGNAL( dropped( QPtrList<QListViewItem>, QListViewItem*, int ) ),
311            this, SLOT( onDropped( QPtrList<QListViewItem>, QListViewItem*, int ) ) );
312   connect( myView, SIGNAL( selectionChanged() ), this, SIGNAL( selectionChanged() ) );
313   connect( myView, SIGNAL( doubleClicked( QListViewItem* ) ),
314            this, SLOT( onDoubleClicked( QListViewItem* ) ) );
315
316   setRootObject( root );
317
318   setModified();
319 }
320
321 /*!
322   Destructor
323 */
324 OB_Browser::~OB_Browser()
325 {
326   myItems.clear();
327   delete myTooltip;
328 }
329
330 /*!
331   \return true if root is decorated by +
332 */
333 bool OB_Browser::rootIsDecorated() const
334 {
335   return myRootDecorated;
336 }
337
338 /*!
339   Sets state "root is recorated"
340   \param decor - new value of state
341 */
342 void OB_Browser::setRootIsDecorated( const bool decor )
343 {
344   if ( decor == rootIsDecorated() ) 
345     return;
346
347   myRootDecorated = decor;
348   updateTree( 0, false );
349 }
350
351 /*!
352   \return number of levels to be auto opened on update tree
353 */
354 int OB_Browser::autoOpenLevel() const
355 {
356   return myAutoOpenLevel;
357 }
358
359 /*!
360   Changes number of levels to be auto opened on update tree
361   \param level - new number of levels
362 */
363 void OB_Browser::setAutoOpenLevel( const int level )
364 {
365   if ( myAutoOpenLevel == level )
366     return;
367
368   myAutoOpenLevel = level;
369
370   autoOpenBranches();
371 }
372
373 /*!
374   \return state "are tooltips shown"
375 */
376 bool OB_Browser::isShowToolTips()
377 {
378   return myShowToolTips;
379 }
380
381 /*!
382   Sets new value of state "are tooltips shown"
383   \param theDisplay - new value
384 */
385 void OB_Browser::setShowToolTips( const bool theDisplay )
386 {
387   myShowToolTips = theDisplay;
388 }
389
390 /*!
391   \return true if object browser automatically updates tree after SUIT object removing
392 */
393 bool OB_Browser::isAutoUpdate() const
394 {
395   return myAutoUpdate;
396 }
397
398 /*!
399   Sets new value of "auto update": whether object browser automatically updates tree after SUIT object removing
400 */
401 void OB_Browser::setAutoUpdate( const bool on )
402 {
403   myAutoUpdate = on;
404 }
405
406 /*!
407   \return true if object browser must delete old tree on setRootObject(), replaceTree()
408   \sa setRootObject(), replaceTree()
409 */
410 bool OB_Browser::isAutoDeleteObjects() const
411 {
412   return myAutoDelObjs;
413 }
414
415 /*!
416   Sets whether object browser must delete old tree on setRootObject(), replaceTree()
417   \sa setRootObject(), replaceTree()
418 */
419 void OB_Browser::setAutoDeleteObjects( const bool on )
420 {
421   myAutoDelObjs = on;
422 }
423
424 /*!
425   \return root SUIT object of browser
426 */
427 SUIT_DataObject* OB_Browser::getRootObject() const
428 {
429   return myRoot;
430 }
431
432 /*!
433   Sets new root SUIT object of browser
434   \param theRoot - new root object
435 */
436 void OB_Browser::setRootObject( SUIT_DataObject* theRoot )
437 {
438   DataObjectKey curKey;
439   DataObjectMap selObjs, openObjs;
440   DataObjectKeyMap selKeys, openKeys;
441
442   int selNum = numberOfSelected();
443
444   SUIT_DataObject* curObj = 0;
445   if ( theRoot )
446     curObj = storeState( selObjs, openObjs, selKeys, openKeys, curKey );
447
448   removeConnections( myRoot );
449   if ( myRoot != theRoot && isAutoDeleteObjects() )
450     delete myRoot;
451
452   myRoot = theRoot;
453
454   createConnections( myRoot );
455
456   if ( myRoot )
457     updateView( myRoot );
458   else if ( listView() )
459   {
460     myItems.clear();
461     listView()->clear();
462   }
463
464   restoreState( selObjs, openObjs, curObj, selKeys, openKeys, curKey );
465
466   autoOpenBranches();
467
468   setModified();
469
470   if ( selNum != numberOfSelected() )
471     emit selectionChanged();
472 }
473
474 /*!
475   \return number of selected items
476 */
477 int OB_Browser::numberOfSelected() const
478 {
479   int count = 0;
480   if ( listView() )
481   {
482     for ( QListViewItemIterator it( listView() ); it.current(); ++it )
483       if ( it.current()->isSelected() ) 
484         count++;
485   }
486   return count;
487 }
488
489 /*!
490   \return list of selected objects
491 */
492 DataObjectList OB_Browser::getSelected() const
493 {
494   DataObjectList lst;
495   getSelected( lst );
496   return lst;
497 }
498
499 /*!
500   Fills list with selected objects
501 */
502 void OB_Browser::getSelected( DataObjectList& theObjList ) const
503 {
504   theObjList.clear();
505
506   if ( !listView() )
507     return;
508
509   for ( QListViewItemIterator it( listView() ); it.current(); ++it )
510   {
511     if ( it.current()->isSelected() ) 
512     {
513       SUIT_DataObject* obj = dataObject( it.current() );
514       if ( obj )
515         theObjList.append( obj );
516     }
517   }
518 }
519
520 /*!
521   Sets selected object
522   \param theObject - new selected object
523   \param append - if it is true, then other selected objects are left as selected,
524   otherwise only 'theObject' will be selected
525 */
526 void OB_Browser::setSelected( const SUIT_DataObject* theObject, const bool append )
527 {
528   DataObjectList lst;
529   lst.append( theObject );
530   setSelected( lst, append );
531 }
532
533 /*!
534   Sets selected objects
535   \param theObjLst - new selected objects
536   \param append - if it is true, then other selected objects are left as selected,
537   otherwise only 'theObjLst' will be selected
538 */
539 void OB_Browser::setSelected( const DataObjectList& theObjLst, const bool append )
540 {
541   QListView* lv = listView();
542
543   if ( !lv )
544     return;
545
546   bool changed = false;
547   bool block = lv->signalsBlocked();
548   lv->blockSignals( true );
549
550   QMap<QListViewItem*, int> map;
551   for ( DataObjectListIterator itr( theObjLst ); itr.current(); ++itr )
552     map.insert( listViewItem( itr.current() ), 0 );
553
554   for ( QListViewItemIterator it( lv ); it.current(); ++it )
555   {
556     QListViewItem* item = it.current();
557     if ( map.contains( item ) && !lv->isSelected( item ) )
558     {
559       changed = true;
560       lv->setSelected( item, true );
561     }
562     if ( !append && !map.contains( item ) && lv->isSelected( item ) )
563     {
564       changed = true;
565       lv->setSelected( item, false );
566     }
567   }
568
569   lv->blockSignals( block );
570
571   if ( changed )
572   {
573     int count = 0;
574     QListViewItem* sel = 0;
575     QListViewItem* cur = lv->currentItem();
576     for ( QListViewItemIterator iter( lv ); iter.current() && !sel; ++iter, count++ )
577     {
578       if ( iter.current()->isSelected() && cur == iter.current() )
579         sel = iter.current();
580     }
581
582     for ( QListViewItemIterator itr( lv ); itr.current() && !sel; ++itr )
583     {
584       if ( itr.current()->isSelected() )
585               sel = itr.current();
586     }
587
588     if ( sel )
589       lv->setCurrentItem( sel );
590
591     if ( sel && count == 1 )
592       lv->ensureItemVisible( sel );
593
594     emit selectionChanged();
595   }
596 }
597
598 /*!
599   \return true if item corresponding to object is opened
600   \param theObject - object to be checked
601 */
602 bool OB_Browser::isOpen( SUIT_DataObject* theObject ) const
603 {
604   bool res = false;
605   if ( listView() )
606     res = listView()->isOpen( listViewItem( theObject ) );
607   return res;
608 }
609
610 /*!
611   Sets opened state of item
612   \param theObject - object corresponding to item
613   \param theOpen - new opened state
614 */
615 void OB_Browser::setOpen( SUIT_DataObject* theObject, const bool theOpen )
616 {
617   if ( listView() )
618     listView()->setOpen( listViewItem( theObject ), theOpen );
619 }
620
621 /*!
622   \return SUIT object correspondint to item at position 'pos'
623   \param pos - position
624 */
625 SUIT_DataObject* OB_Browser::dataObjectAt( const QPoint& pos ) const
626 {
627   SUIT_DataObject* obj = 0;
628
629   QListView* lv = listView();
630   if ( lv )
631     obj = dataObject( lv->itemAt( pos ) );
632
633   return obj;
634 }
635
636 /*!
637   \return filter of list view
638 */
639 OB_Filter* OB_Browser::filter() const
640 {
641   return myView->filter();
642 }
643
644 /*!
645   Changes filter of list view
646   \param f - new filter
647 */
648 void OB_Browser::setFilter( OB_Filter* f )
649 {
650   myView->setFilter( f );
651 }
652
653 /*!
654   Adds new column to list view
655   \param label - title of column
656   \param id - id of column
657   \param width - width of column
658 */
659 int OB_Browser::addColumn( const QString& label, const int id, const int width )
660 {
661   return addColumn( QIconSet(), label, id, width );
662 }
663
664 /*!
665   Adds new column to list view
666   \param icon - icon of column
667   \param label - title of column
668   \param id - id of column
669   \param width - width of column
670 */
671 int OB_Browser::addColumn( const QIconSet& icon, const QString& label, const int id, const int width )
672 {
673   QListView* lv = listView();
674   if ( !lv )
675     return -1;
676
677   int theId = id;
678   if ( theId < 0 )
679   {
680     while ( myColumnIds.contains( theId ) )
681       theId++;
682   }
683
684   if ( myColumnIds.contains( theId ) )
685     return -1; // can not reuse id
686
687   int sec = -1;
688   if ( icon.isNull() )
689     sec = lv->addColumn( label, width );
690   else
691     sec = lv->addColumn( icon, label, width );
692
693   if ( sec == -1 )
694     return -1;
695
696   myColumnIds.insert( theId, sec );
697   updateText();
698
699   return theId;
700 }
701
702 /*!
703   Removes column
704   \param id - id of column
705 */
706 void OB_Browser::removeColumn( const int id )
707 {
708   QListView* lv = listView();
709   if ( !lv || !myColumnIds.contains( id ) )
710     return;
711
712   int sec = myColumnIds[id];
713   lv->removeColumn( sec );
714
715   // update map of column indeces
716   myColumnIds.remove( id );
717   for ( QMap<int, int>::iterator it = myColumnIds.begin(); it != myColumnIds.end(); ++it )
718   {
719     if ( it.key() > id )
720       it.data()--;
721   }
722   updateText();
723 }
724
725 /*!
726   Sets title of first column (name column)
727   \param label - new title
728 */
729 void OB_Browser::setNameTitle( const QString& label )
730 {
731   setNameTitle( QIconSet(), label );
732 }
733
734 /*!
735   Sets title and icon of first column (name column)
736   \param icon - new icon
737   \param label - new title
738 */
739 void OB_Browser::setNameTitle( const QIconSet& icon, const QString& label )
740 {
741   QListView* lv = listView();
742   if ( !lv )
743     return;
744
745   if ( icon.isNull() )
746     lv->setColumnText( 0, label );
747   else
748     lv->setColumnText( 0, icon, label );
749 }
750
751 /*!
752   Sets title of column
753   \param id - column id
754   \param label - new column title
755 */
756 void OB_Browser::setColumnTitle( const int id, const QString& label )
757 {
758   setColumnTitle( id, QIconSet(), label );
759 }
760
761 /*!
762   Sets title and icon of column
763   \param id - column id
764   \param icon - new column icon
765   \param label - new column title
766 */
767 void OB_Browser::setColumnTitle( const int id, const QIconSet& icon, const QString& label )
768 {
769   QListView* lv = listView();
770   if ( !lv || !myColumnIds.contains( id ) )
771     return;
772
773   if ( icon.isNull() )
774     lv->setColumnText( myColumnIds[id], label );
775   else
776     lv->setColumnText( myColumnIds[id], icon, label );
777 }
778
779 /*!
780   \return title of first column (name column)
781 */
782 QString OB_Browser::nameTitle() const
783 {
784   return myView->columnText( 0 );
785 }
786
787 /*!
788   \return title of first column (name column)
789   \param id - column id
790 */
791 QString OB_Browser::columnTitle( const int id ) const
792 {
793   QString txt;
794   if ( myColumnIds.contains( id ) )
795     txt = myView->columnText( myColumnIds[id] );
796   return txt;
797 }
798
799 /*!
800   \return true if column is visible
801   \param id - column id
802 */
803 bool OB_Browser::isColumnVisible( const int id ) const
804 {
805   return myColumnIds.contains( id ) && myView->isShown( myColumnIds[id] );
806 }
807
808 /*!
809   Sets visibility of column
810   \param id - column id
811   \param on - new visibility state
812 */
813 void OB_Browser::setColumnShown( const int id, const bool on )
814 {
815   if ( !myColumnIds.contains( id ) )
816     return;
817
818   myView->setShown( myColumnIds[id], on );
819   if( !on )
820     myView->setColumnWidthMode( myColumnIds[id], QListView::Manual );
821 }
822
823 /*!
824   Sets global width mode
825   \param mode - new width mode
826 */
827 void OB_Browser::setWidthMode( QListView::WidthMode mode )
828 {
829   for ( int i = 0, n = myView->columns(); i < n; i++ )
830     if( mode!=QListView::Maximum || myView->columnWidth( i )>0 )
831       myView->setColumnWidthMode( i, mode );
832 }
833
834 /*!
835   \return list of columns ids
836 */
837 QValueList<int> OB_Browser::columns() const
838 {
839   QValueList<int> lst;
840   for ( QMap<int, int>::ConstIterator it = myColumnIds.begin(); it != myColumnIds.end(); ++it )
841     lst.append( it.key() );
842   return lst;
843 }
844
845 /*!
846   \return true if it is possible to show/hide column by popup
847   \param id - column id
848 */
849 bool OB_Browser::appropriateColumn( const int id ) const
850 {
851   bool res = false;
852   if ( myColumnIds.contains( id ) )
853     res = myView->appropriate( myColumnIds[id] );
854   return res;
855 }
856
857 /*!
858   Sets "appropriate state": is it possible to show/hide column by popup
859   \param id - column id
860   \param on - new state
861 */
862 void OB_Browser::setAppropriateColumn( const int id, const bool on )
863 {
864   if ( !myColumnIds.contains( id ) )
865     return;
866
867   myView->setAppropriate( myColumnIds[id], on );
868 }
869
870 /*!
871   Updates tree
872   \param obj - start object
873   \param autoOpen - to open automatically branches of autoOpenLevel()
874   \sa autoOpenLevel()
875 */
876 void OB_Browser::updateTree( SUIT_DataObject* obj, const bool autoOpen )
877 {
878 //  QTime t1 = QTime::currentTime();
879
880   if ( !obj && !(obj = getRootObject()) )
881     return;
882
883   DataObjectKey curKey;
884   DataObjectMap selObjs, openObjs;
885   DataObjectKeyMap selKeys, openKeys;
886
887   int selNum = numberOfSelected();
888
889   SUIT_DataObject* curObj = storeState( selObjs, openObjs, selKeys, openKeys, curKey );
890
891   updateView( obj );
892
893   restoreState( selObjs, openObjs, curObj, selKeys, openKeys, curKey );
894
895   if( autoOpen )
896     autoOpenBranches();
897
898   setModified();
899
900   if ( selNum != numberOfSelected() )
901     emit selectionChanged();
902
903 //  QTime t2 = QTime::currentTime();
904 //  qDebug( QString( "update tree time = %1 msecs" ).arg( t1.msecsTo( t2 ) ) );
905 }
906
907 /*!
908   Replaces part of tree starting at object 'src' by tree starting at object 'trg'
909 */
910 void OB_Browser::replaceTree( SUIT_DataObject* src, SUIT_DataObject* trg )
911 {
912   if ( !src || !trg || src == trg || src->root() != getRootObject() )
913     return;
914
915   DataObjectKey curKey;
916   DataObjectMap selObjs, openObjs;
917   DataObjectKeyMap selKeys, openKeys;
918
919   int selNum = numberOfSelected();
920
921   SUIT_DataObject* curObj = storeState( selObjs, openObjs, selKeys, openKeys, curKey );
922
923   SUIT_DataObject* parent = src->parent();
924   int pos = parent ? parent->childPos( src ) : -1;
925
926   src->setParent( 0 );
927
928   removeConnections( src );
929   if ( isAutoDeleteObjects() )
930     delete src;
931
932   if ( parent && pos != -1 )
933     parent->insertChild( trg, pos );
934
935   trg->setParent( parent );
936
937   updateView( trg );
938   createConnections( trg );
939
940   restoreState( selObjs, openObjs, curObj, selKeys, openKeys, curKey );
941
942   autoOpenBranches();
943
944   setModified();
945
946   if ( selNum != numberOfSelected() )
947     emit selectionChanged();
948 }
949
950 /*!
951   Updates view
952   \param startObj - start object
953 */
954 void OB_Browser::updateView( SUIT_DataObject* startObj )
955 {
956   QListView* lv = listView();
957   if ( !lv )
958     return;
959
960   if ( !startObj || startObj->root() != getRootObject() )
961     return;
962
963   //qDebug( "updateView:" );
964   //startObj->dump();
965
966   if ( startObj == myRoot )
967   {
968     OB_BrowserSync sync( this );
969     synchronize<ObjPtr,ItemPtr,OB_BrowserSync>( myRoot, 0, sync );
970   }
971   else
972   {
973     OB_BrowserSync sync( this );
974     OB_ListItem* startItem = dynamic_cast<OB_ListItem*>( listViewItem( startObj ) );
975     synchronize<ObjPtr,ItemPtr,OB_BrowserSync>( startObj, startItem, sync );
976   }
977 }
978
979 /*!
980   Creates new list item
981   \return new item
982   \param o - corresponding SUIT object
983   \param parent - parent item
984   \param after - item after that new item must be added
985   \param prepend - new item must be added as first
986 */
987 QListViewItem* OB_Browser::createItem( const SUIT_DataObject* o, QListViewItem* parent,
988                                        QListViewItem* after, const bool prepend )
989 {
990   QListView* lv = listView();
991
992   if ( !lv || !o )
993     return 0;
994
995   QListViewItem* item = 0;
996   SUIT_DataObject* obj = (SUIT_DataObject*)o;
997
998   int type = -1;
999
1000   switch ( obj->checkType() )
1001   {
1002   case SUIT_DataObject::CheckBox:
1003     type = QCheckListItem::CheckBox;
1004     break;
1005   case SUIT_DataObject::RadioButton:
1006     type = QCheckListItem::RadioButton;
1007     break;
1008   }
1009
1010   if ( parent )
1011   {
1012     if ( after ) 
1013     {
1014       if ( type == -1 )
1015         item = new OB_ListItem( obj, parent, after );
1016       else
1017         item = new OB_CheckListItem( obj, parent, after, (QCheckListItem::Type)type );
1018     }
1019     else if ( prepend )
1020     {
1021       if ( type == -1 )
1022         item = new OB_ListItem( obj, parent );
1023       else
1024         item = new OB_CheckListItem( obj, parent,  (QCheckListItem::Type)type );
1025     }
1026     else // append
1027     {
1028       after = parent->firstChild();
1029       while ( after && after->nextSibling() )
1030         after = after->nextSibling();
1031       if ( type == -1 )
1032         item = new OB_ListItem( obj, parent, after );
1033       else
1034         item = new OB_CheckListItem( obj, parent, after, (QCheckListItem::Type)type );
1035     }
1036   }
1037   else
1038   {
1039     if ( after ) 
1040     {
1041       if ( type == -1 )
1042         item = new OB_ListItem( obj, lv, after );
1043       else
1044         item = new OB_CheckListItem( obj, lv, after, (QCheckListItem::Type)type );
1045     }
1046     else if ( prepend )
1047     {
1048       if ( type == -1 )
1049         item = new OB_ListItem( obj, lv );
1050       else
1051         item = new OB_CheckListItem( obj, lv,  (QCheckListItem::Type)type );
1052     }
1053     else // append
1054     {
1055       after = lv->firstChild();
1056       while ( after && after->nextSibling() )
1057         after = after->nextSibling();
1058       if ( type == -1 )
1059         item = new OB_ListItem( obj, lv, after );
1060       else
1061         item = new OB_CheckListItem( obj, lv, after, (QCheckListItem::Type)type );
1062     }
1063   }
1064
1065   myItems.insert( obj, item );
1066   obj->connect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
1067   updateText( item );
1068
1069   return item;
1070 }
1071
1072 /*!
1073   Adjusts width by root item
1074 */
1075 void OB_Browser::adjustWidth()
1076 {
1077   if ( !listView() )
1078     return;
1079
1080   listView()->setColumnWidth( 0, 0 );
1081   if ( listView()->firstChild() )
1082     adjustWidth( listView()->firstChild() );
1083 }
1084
1085 /*!
1086   Adjusts width by item
1087   \param item
1088 */
1089 void OB_Browser::adjustWidth( QListViewItem* item )
1090 {
1091   while ( item )
1092   {
1093     item->widthChanged( 0 );
1094     if ( item->isOpen() )
1095       adjustWidth( item->firstChild() );
1096     item = item->nextSibling();
1097   }
1098 }
1099
1100 /*!
1101   \return SUIT object corresponding to item
1102   \param item
1103 */
1104 SUIT_DataObject* OB_Browser::dataObject( const QListViewItem* item ) const
1105 {
1106   SUIT_DataObject* obj = 0;
1107
1108   if ( item && item->rtti() == OB_ListItem::RTTI() )
1109     obj = ((OB_ListItem*)item)->dataObject();
1110   else if ( item && item->rtti() == OB_CheckListItem::RTTI() )
1111     obj = ((OB_CheckListItem*)item)->dataObject();
1112
1113   return obj;
1114 }
1115
1116 /*!
1117   \return item corresponding to SUIT object
1118   \param obj - SUIT object
1119 */
1120 QListViewItem* OB_Browser::listViewItem( const SUIT_DataObject* obj ) const
1121 {
1122   QListViewItem* item = 0;
1123
1124   if ( myItems.contains( (SUIT_DataObject*)obj ) )
1125     item = myItems[(SUIT_DataObject*)obj];
1126
1127   return item;
1128 }
1129
1130 /*!
1131   \return list view of object browser
1132 */
1133 QListView* OB_Browser::listView() const
1134 {
1135   return myView;
1136 }
1137
1138 /*!
1139   \remove all items referencing current (through data objects)
1140 */
1141 void OB_Browser::removeReferences( QListViewItem* item )
1142 {
1143   if ( !item )
1144     return;
1145
1146   SUIT_DataObject* obj = dataObject( item );
1147   obj->disconnect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
1148   myItems.remove( obj );
1149
1150   QListViewItem* i = item->firstChild();
1151   while ( i )
1152   {
1153     removeReferences( i );
1154     i = i->nextSibling();
1155   }
1156 }
1157
1158 /*!
1159   Connects all children to SLOT onDestroyed
1160 */
1161 void OB_Browser::createConnections( SUIT_DataObject* obj )
1162 {
1163   if ( !obj )
1164     return;
1165
1166   DataObjectList childList;
1167   obj->children( childList, true );
1168
1169   childList.prepend( obj );
1170
1171   for ( DataObjectListIterator it( childList ); it.current(); ++it )
1172     it.current()->connect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
1173 }
1174
1175 /*!
1176   Disconnects all children from SLOT onDestroyed
1177 */
1178 void OB_Browser::removeConnections( SUIT_DataObject* obj )
1179 {
1180   if ( !obj )
1181     return;
1182
1183   DataObjectList childList;
1184   obj->children( childList, true );
1185
1186   childList.prepend( obj );
1187
1188   for ( DataObjectListIterator it( childList ); it.current(); ++it )
1189     it.current()->disconnect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
1190 }
1191
1192 /*!
1193   Stores states (opened, selected) of current tree items
1194   \return current item
1195   \param selObjs, selKeys - maps of selected objects
1196   \param openObjs, openKeys - maps of opened objects
1197   \param curKey - map of current objects
1198 */
1199 SUIT_DataObject* OB_Browser::storeState( DataObjectMap& selObjs, DataObjectMap& openObjs,
1200                                          DataObjectKeyMap& selKeys, DataObjectKeyMap& openKeys,
1201                                          DataObjectKey& curKey ) const
1202 {
1203   QListView* lv = listView();
1204   if ( !lv )
1205     return 0;
1206
1207   SUIT_DataObject* curObj = dataObject( lv->currentItem() );
1208
1209   curKey = objectKey( curObj );
1210
1211   for ( QListViewItemIterator it( lv ); it.current(); ++it )
1212   {
1213     SUIT_DataObject* obj = dataObject( it.current() );
1214     if ( !obj )
1215       continue;
1216
1217     selObjs.insert( obj, lv->isSelected( it.current() ) );
1218     openObjs.insert( obj, lv->isOpen( it.current() ) );
1219     if ( lv->isSelected( it.current() ) )
1220       selKeys.insert( objectKey( obj ), 0 );
1221     if ( lv->isOpen( it.current() ) )
1222       openKeys.insert( objectKey( obj ), 0 );
1223   }
1224
1225   return curObj;
1226 }
1227
1228 /*!
1229   Restores states (opened, selected) of current tree items
1230   \param selObjs, selKeys - maps of selected objects
1231   \param openObjs, openKeys - maps of opened objects
1232   \param curKey - map of current objects
1233 */
1234 void OB_Browser::restoreState( const DataObjectMap& selObjs, const DataObjectMap& openObjs,
1235                                const SUIT_DataObject* curObj, const DataObjectKeyMap& selKeys,
1236                                const DataObjectKeyMap& openKeys, const DataObjectKey& curKey )
1237 {
1238   QListView* lv = listView();
1239   if ( !lv )
1240     return;
1241
1242   bool block = lv->signalsBlocked();
1243   lv->blockSignals( true );
1244
1245   QListViewItem* curItem = 0;
1246   for ( QListViewItemIterator it( lv ); it.current(); ++it )
1247   {
1248     QListViewItem* item = it.current();
1249     SUIT_DataObject* obj = dataObject( item );
1250
1251     if ( !obj )
1252       continue;
1253
1254     DataObjectKey key = objectKey( obj );
1255
1256     if ( selObjs.contains( obj ) )
1257     {
1258       if ( selObjs[obj] && !lv->isSelected( item ) )
1259         lv->setSelected( item, true );
1260     }
1261     else if ( !key.isNull() && selKeys.contains( key ) && !lv->isSelected( item ) )
1262       lv->setSelected( item, true );
1263
1264     if ( openObjs.contains( obj ) )
1265     {
1266       bool parentOpen = true;
1267       if( item && item->parent() )
1268         parentOpen = item->parent()->isOpen();
1269         
1270       if ( openObjs[obj] && parentOpen )
1271         lv->setOpen( item, true );
1272     }
1273     else if ( !key.isNull() && openKeys.contains( key ) )
1274     {
1275       bool parentOpen = true;
1276       if( item && item->parent() )
1277         parentOpen = item->parent()->isOpen();
1278
1279       if( parentOpen )
1280         lv->setOpen( item, true );
1281     }
1282
1283     if ( !curItem && ( curObj == obj || ( !curKey.isNull() && curKey == key )) )
1284       curItem = item;
1285   }
1286
1287   if ( curItem )
1288     lv->setCurrentItem( curItem );
1289
1290   lv->blockSignals( block );
1291 }
1292
1293 /*!
1294   Creates object key by tree item
1295 */
1296 OB_Browser::DataObjectKey OB_Browser::objectKey( QListViewItem* i ) const
1297 {
1298   return objectKey( dataObject( i ) );
1299 }
1300
1301 /*!
1302   Creates object key by SUIT object
1303 */
1304 OB_Browser::DataObjectKey OB_Browser::objectKey( SUIT_DataObject* obj ) const
1305 {
1306   if ( !obj )
1307     return 0;
1308
1309   return DataObjectKey( obj->key() );
1310 }
1311
1312 /*!
1313   Custom key press event handler, updates tree by F5
1314 */
1315 void OB_Browser::keyPressEvent( QKeyEvent* e )
1316 {
1317   if ( e->key() == Qt::Key_F5 )
1318     updateTree( 0, false );
1319
1320   QFrame::keyPressEvent( e );
1321 }
1322
1323 /*!
1324   SLOT: called if action "Expand all" is activated
1325 */
1326 void OB_Browser::onExpand()
1327 {
1328   DataObjectList selected;
1329   getSelected( selected );
1330   for ( DataObjectListIterator itr( selected ); itr.current(); ++itr )
1331     expand( listViewItem( itr.current() ) );
1332 }
1333
1334 /*!
1335   SLOT: called if action "Show/hide column" is activated by popup
1336 */
1337 void OB_Browser::onColumnVisible( int id )
1338 {
1339   setColumnShown( id, !isColumnVisible( id ) );
1340 }
1341
1342 /*!
1343   SLOT: called if SUIT object is destroyed
1344 */
1345 void OB_Browser::onDestroyed( SUIT_DataObject* obj )
1346 {
1347   removeObject( obj );
1348 }
1349
1350 /*!
1351   SLOT: called on finish of drag-n-drop operation
1352   \param items - dragged items
1353   \param item - destination (item on that they were dropped)
1354   \param action - QDropEvent::Action
1355 */
1356 void OB_Browser::onDropped( QPtrList<QListViewItem> items, QListViewItem* item, int action )
1357 {
1358   SUIT_DataObject* obj = dataObject( item );
1359   if ( !obj )
1360     return;
1361
1362   DataObjectList lst;
1363   for ( QPtrListIterator<QListViewItem> it( items ); it.current(); ++it )
1364   {
1365     SUIT_DataObject* o = dataObject( it.current() );
1366     if ( o )
1367       lst.append( o );
1368   }
1369
1370   if ( !lst.isEmpty() )
1371     emit dropped( lst, obj, action );
1372 }
1373
1374 /*!
1375   Updates texts of items
1376 */
1377 void OB_Browser::updateText()
1378 {
1379   if ( myColumnIds.isEmpty() )
1380     return;
1381
1382   QListView* lv = listView();
1383   if ( !lv )
1384     return;
1385
1386   for ( QListViewItemIterator it( lv ); it.current(); ++it )
1387   {
1388     SUIT_DataObject* obj = dataObject( it.current() );
1389     if ( !obj )
1390       continue;
1391
1392     for( QMap<int, int>::iterator itr = myColumnIds.begin(); itr != myColumnIds.end(); ++itr )
1393       it.current()->setText( itr.data(), obj->text( itr.key() ) );
1394   }
1395 }
1396
1397 /*!
1398   \return true if item must be updated
1399   \param item - item to be checked
1400 */
1401 bool OB_Browser::needToUpdateTexts( QListViewItem* item ) const
1402 {
1403   SUIT_DataObject* obj = dataObject( item );
1404   if ( !obj )
1405     return false;
1406
1407   for( QMap<int, int>::const_iterator it = myColumnIds.begin(); it != myColumnIds.end(); ++it )
1408     if( item->text( it.data() ) != obj->text( it.key() ) )
1409       return true;
1410   return false;
1411 }
1412
1413 /*!
1414   Updates texts of item
1415   \param item - item to be updated
1416 */
1417 void OB_Browser::updateText( QListViewItem* item )
1418 {
1419   SUIT_DataObject* obj = dataObject( item );
1420   if ( !obj )
1421     return;
1422
1423   for( QMap<int, int>::iterator it = myColumnIds.begin(); it != myColumnIds.end(); ++it )
1424     item->setText( it.data(), obj->text( it.key() ) );
1425 }
1426
1427 /*!
1428   Custom event filter
1429 */
1430 bool OB_Browser::eventFilter( QObject* o, QEvent* e )
1431 {
1432   if ( o == myView && e->type() == QEvent::ContextMenu )
1433   {
1434     QContextMenuEvent* ce = (QContextMenuEvent*)e;
1435     if ( ce->reason() != QContextMenuEvent::Mouse )
1436       contextMenuRequest( ce );
1437     return true;
1438   }
1439   if ( o == myView->viewport() && e->type() == QEvent::MouseButtonRelease )
1440   {
1441     QMouseEvent* me = (QMouseEvent*)e;
1442     if ( me->button() == RightButton )
1443     {
1444       QContextMenuEvent ce( QContextMenuEvent::Mouse, me->pos(), me->globalPos(), me->state() );
1445       contextMenuRequest( &ce );
1446       return true;
1447     }
1448   }
1449
1450   return QFrame::eventFilter( o, e );
1451 }
1452
1453 /*!
1454   Adds custom actions to popup
1455   \param menu - popup menu
1456 */
1457 void OB_Browser::contextMenuPopup( QPopupMenu* menu )
1458 {
1459 /*  QValueList<int> cols;
1460   for ( QMap<int, int>::ConstIterator it = myColumnIds.begin(); it != myColumnIds.end(); ++it )
1461   {
1462     if ( appropriateColumn( it.key() ) )
1463       cols.append( it.key() );
1464   }
1465
1466   uint num = menu->count();
1467   menu->setCheckable( true );
1468   for ( QValueList<int>::const_iterator iter = cols.begin(); iter != cols.end(); ++iter )
1469   {
1470     QString name = columnTitle( *iter );
1471     if ( name.isEmpty() )
1472       continue;
1473
1474     int id = menu->insertItem( name, this, SLOT( onColumnVisible( int ) ) );
1475     menu->setItemChecked( id, isColumnVisible( *iter ) );
1476     menu->setItemParameter( id, *iter );
1477   }
1478   if ( menu->count() != num )
1479     menu->insertSeparator();*/
1480
1481   DataObjectList selected;
1482   getSelected( selected );
1483
1484   bool closed = false;
1485   for ( DataObjectListIterator itr( selected ); itr.current() && !closed; ++itr )
1486     closed = hasClosed( listViewItem( itr.current() ) );
1487
1488   if ( closed )
1489   {
1490     menu->insertItem( tr( "MEN_EXPAND_ALL" ), this, SLOT( onExpand() ) );
1491     menu->insertSeparator();
1492   }
1493 }
1494
1495 /*!
1496   Expands item with all it's children
1497 */
1498 void OB_Browser::expand( QListViewItem* item )
1499 {
1500   if ( !item )
1501     return;
1502
1503   item->setOpen( true );
1504   for ( QListViewItem* child = item->firstChild(); child; child = child->nextSibling() )
1505     expand( child );
1506 }
1507
1508 /*!
1509   \return true if item or one of it's children isn't opened
1510 */
1511 bool OB_Browser::hasClosed( QListViewItem* item ) const
1512 {
1513   if ( !item )
1514     return false;
1515
1516   if ( item->childCount() && !item->isOpen() )
1517     return true;
1518
1519   bool has = false;
1520   for ( QListViewItem* child = item->firstChild(); child && !has; child = child->nextSibling() )
1521     has = hasClosed( child );
1522
1523   return has;
1524 }
1525
1526 /*!
1527   Removes SUIT object
1528   \param obj - SUIT object to be removed
1529   \param autoUpd - auto tree updating
1530 */
1531 void OB_Browser::removeObject( SUIT_DataObject* obj, const bool autoUpd )
1532 {
1533   if ( !obj )
1534     return;
1535
1536   // Removing list view items from <myItems> recursively for all children.
1537   // Otherwise, "delete item" line will destroy all item's children,
1538   // and <myItems> will contain invalid pointers (see ~QListViewItem() description in Qt docs)
1539   DataObjectList childList;
1540   obj->children( childList, true );
1541   for ( DataObjectListIterator it( childList ); it.current(); ++it )
1542   {
1543     it.current()->disconnect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
1544     myItems.remove( it.current() );
1545   }
1546
1547   QListViewItem* item = listViewItem( obj );
1548
1549   obj->disconnect( this, SLOT( onDestroyed( SUIT_DataObject* ) ) );
1550   myItems.remove( obj );
1551
1552   if ( obj == myRoot )
1553   {
1554     // remove all child list view items
1555     setRootObject( 0 );
1556     return;
1557   }
1558
1559   if( !autoUpd )
1560     return;
1561
1562   if ( isAutoUpdate() )
1563   {
1564     SUIT_DataObject* pObj = item && item->parent() ? dataObject( item->parent() ) : 0;
1565     updateTree( pObj, false );
1566   }
1567
1568   delete item;
1569 }
1570
1571 /*!
1572   Opens branches from 1 to autoOpenLevel()
1573   \sa autoOpenLevel()
1574 */
1575 void OB_Browser::autoOpenBranches()
1576 {
1577   int level = autoOpenLevel();
1578   QListView* lv = listView();
1579   if ( !lv || level < 1 )
1580     return;
1581
1582   QListViewItem* item = lv->firstChild();
1583   while ( item )
1584   {
1585     openBranch( item, level );
1586     item = item->nextSibling();
1587   }
1588 }
1589
1590 /*!
1591   Opens branch
1592   \param item
1593   \param level
1594 */
1595 void OB_Browser::openBranch( QListViewItem* item, const int level )
1596 {
1597   if ( level < 1 )
1598     return;
1599
1600   while ( item )
1601   {
1602     item->setOpen( true );
1603     openBranch( item->firstChild(), level - 1 );
1604     item = item->nextSibling();
1605   }
1606 }
1607
1608 /*!
1609   SLOT: called on double click on item, emits signal
1610 */
1611 void OB_Browser::onDoubleClicked( QListViewItem* item )
1612 {
1613   if ( item )
1614     emit doubleClicked( dataObject( item ) );
1615 }
1616
1617 /*!
1618   Stores time of last modification
1619 */
1620 void OB_Browser::setModified()
1621 {
1622   myModifiedTime = clock();
1623 }
1624