Salome HOME
fd3955e4787c4e59b76c40a22017f2d1a14645fd
[modules/gui.git] / src / Qtx / QtxWorkstack.cxx
1 // File:      QtxWorkstack.cxx
2 // Author:    Sergey TELKOV
3
4 #include "QtxWorkstack.h"
5
6 #include <qstyle.h>
7 #include <qlayout.h>
8 #include <qpixmap.h>
9 #include <qiconset.h>
10 #include <qpainter.h>
11 #include <qsplitter.h>
12 #include <qpopupmenu.h>
13 #include <qobjectlist.h>
14 #include <qpushbutton.h>
15 #include <qwidgetstack.h>
16 #include <qapplication.h>
17
18 /*!
19     Class: QtxWorkstack [Public]
20     Descr:
21 */
22
23 QtxWorkstack::QtxWorkstack( QWidget* parent )
24 : QWidget( parent ),
25 myWin( 0 ),
26 myArea( 0 )
27 {
28   QVBoxLayout* base = new QVBoxLayout( this );
29   mySplit = new QSplitter( this );
30   mySplit->setChildrenCollapsible( false );
31   base->addWidget( mySplit );
32 }
33
34 QtxWorkstack::~QtxWorkstack()
35 {
36 }
37
38 QWidgetList QtxWorkstack::windowList() const
39 {
40   QPtrList<QtxWorkstackArea> lst;
41   areas( mySplit, lst, true );
42
43   QWidgetList widList;
44   for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current(); ++it )
45   {
46     QWidgetList wids = it.current()->widgetList();
47     for ( QWidgetListIt itr( wids ); itr.current(); ++itr )
48       widList.append( itr.current() );
49   }
50
51   return widList;
52 }
53
54 QWidgetList QtxWorkstack::splitWindowList() const
55 {
56   return myArea ? myArea->widgetList() : QWidgetList();
57 }
58
59 QWidget* QtxWorkstack::activeWindow() const
60 {
61   return myWin;
62 }
63
64 void QtxWorkstack::split( const int o )
65 {
66   QtxWorkstackArea* area = activeArea();
67   if ( !area )
68     return;
69
70   if ( area->widgetList().count() < 2 )
71     return;
72
73   QWidget* curWid = area->activeWidget();
74   if ( !curWid )
75     return;
76
77   QSplitter* s = splitter( area );
78   QPtrList<QtxWorkstackArea> areaList;
79   areas( s, areaList );
80
81   QPtrList<QSplitter> splitList;
82   splitters( s, splitList );
83
84   QSplitter* trg = 0;
85   if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
86     trg = s;
87
88   if ( !trg )
89     trg = wrapSplitter( area );
90
91   if ( !trg )
92     return;
93
94   trg->setOrientation( (Orientation)o );
95
96   QtxWorkstackArea* newArea = createArea( 0 );
97   insertWidget( newArea, trg, area );
98
99   area->removeWidget( curWid );
100   newArea->insertWidget( curWid );
101
102   curWid->show();
103   curWid->setFocus();
104 }
105
106 void QtxWorkstack::splitVertical()
107 {
108   split( Qt::Vertical );
109 }
110
111 void QtxWorkstack::splitHorizontal()
112 {
113   split( Qt::Horizontal );
114 }
115
116 QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
117 {
118   if ( !area )
119     return 0;
120
121   QSplitter* pSplit = splitter( area );
122   if ( !pSplit )
123     return 0;
124
125   bool upd = pSplit->isUpdatesEnabled();
126   pSplit->setUpdatesEnabled( false );
127
128   QIntList szList = pSplit->sizes();
129
130   QSplitter* wrap = new QSplitter( 0 );
131 #if defined QT_VERSION && QT_VERSION >= 0x30200
132   wrap->setChildrenCollapsible( false );
133 #endif
134   insertWidget( wrap, pSplit, area );
135   area->reparent( wrap, QPoint( 0, 0 ), true );
136
137   pSplit->setSizes( szList );
138
139   pSplit->setUpdatesEnabled( upd );
140
141   return wrap;
142 }
143
144 void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
145 {
146   if ( !wid || !pWid )
147     return;
148
149   QWidgetList moveList;
150   const QObjectList* lst = pWid->children();
151   if ( lst )
152   {
153     bool found = false;
154     for ( QObjectListIt it( *lst ); it.current(); ++it )
155     {
156       if ( found && ( it.current()->inherits( "QSplitter" ) ||
157                       it.current()->inherits( "QtxWorkstackArea" ) ) )
158         moveList.append( (QWidget*)it.current() );
159       if ( it.current() == after )
160         found = true;
161     }
162   }
163
164   QMap<QWidget*, bool> map;
165   for ( QWidgetListIt it( moveList ); it.current(); ++it )
166   {
167     map.insert( it.current(), it.current()->isVisibleTo( it.current()->parentWidget() ) );
168     it.current()->reparent( 0, QPoint( 0, 0 ), false );
169   }
170
171   wid->reparent( pWid, QPoint( 0, 0 ), true );
172
173   for ( QWidgetListIt itr( moveList ); itr.current(); ++itr )
174     itr.current()->reparent( pWid, QPoint( 0, 0 ), map.contains( itr.current() ) ? map[itr.current()] : false );
175 }
176
177 void QtxWorkstack::onPopupActivated( int id )
178 {
179   switch ( id )
180   {
181   case SplitVertical:
182     splitVertical();
183     break;
184   case SplitHorizontal:
185     splitHorizontal();
186     break;
187   case Close:
188     if ( activeWindow() )
189       activeWindow()->close();
190     break;
191   }
192 }
193
194 void QtxWorkstack::onDestroyed( QObject* obj )
195 {
196   QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
197
198   if ( area == myArea )
199     myArea = 0;
200
201   if ( !myArea )
202   {
203     QtxWorkstackArea* cur = neighbourArea( area );
204     if ( cur )
205       cur->setFocus();
206   }
207
208   QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
209 }
210
211 void QtxWorkstack::onWindowActivated( QWidget* wid )
212 {
213   const QObject* obj = sender();
214   if ( !obj->inherits( "QtxWorkstackArea" ) )
215     return;
216
217   setActiveArea( (QtxWorkstackArea*)obj );
218 }
219
220 void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
221 {
222   if ( myArea != area )
223     return;
224
225   QPtrList<QtxWorkstackArea> lst;
226   areas( mySplit, lst, true );
227
228   int idx = lst.find( area );
229   if ( idx == -1 )
230     return;
231
232   QtxWorkstackArea* newArea = neighbourArea( area );
233   if ( newArea )
234     newArea->setFocus();
235
236   QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
237 }
238
239 void QtxWorkstack::onContextMenuRequested( QPoint p )
240 {
241   if ( !activeArea() )
242     return;
243
244   QWidgetList lst = activeArea()->widgetList();
245   if ( lst.isEmpty() )
246     return;
247
248   QPopupMenu* pm = new QPopupMenu();
249   connect( pm, SIGNAL( activated( int ) ), this, SLOT( onPopupActivated( int ) ) );
250
251   if ( lst.count() > 1 )
252   {
253     pm->insertItem( tr( "Split vertically" ), SplitVertical );
254     pm->insertItem( tr( "Split horizontally" ), SplitHorizontal );
255     pm->insertSeparator();
256   }
257   pm->insertItem( tr( "Close" ), Close );
258
259   pm->exec( p );
260
261   delete pm;
262 }
263
264 void QtxWorkstack::childEvent( QChildEvent* e )
265 {
266   if ( e->inserted() && e->child()->isWidgetType() )
267   {
268           QWidget* w = (QWidget*)e->child();
269           if ( w && w != mySplit )
270     {
271       targetArea()->insertWidget( w );
272       return;
273     }
274   }
275   QWidget::childEvent( e );
276 }
277
278 void QtxWorkstack::customEvent( QCustomEvent* e )
279 {
280   updateState();
281 }
282
283 QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
284 {
285   if ( !area )
286     return 0;
287
288   QSplitter* split = 0;
289
290   QWidget* wid = area->parentWidget();
291   if ( wid && wid->inherits( "QSplitter" ) )
292     split = (QSplitter*)wid;
293
294   return split;
295 }
296
297 void QtxWorkstack::splitters( QSplitter* split, QPtrList<QSplitter>& splitList, const bool rec ) const
298 {
299   if ( !split )
300     return;
301
302   const QObjectList* objs = split->children();
303   if ( objs )
304   {
305     for ( QObjectListIt it( *objs ); it.current(); ++it )
306     {
307       if ( rec )
308         splitters( (QSplitter*)it.current(), splitList, rec );
309       if ( it.current()->inherits( "QSplitter" ) )
310         splitList.append( (QSplitter*)it.current() );
311     }
312   }
313 }
314
315 void QtxWorkstack::areas( QSplitter* split, QPtrList<QtxWorkstackArea>& areaList, const bool rec ) const
316 {
317   if ( !split )
318     return;
319
320   const QObjectList* objs = split->children();
321   if ( objs )
322   {
323     for ( QObjectListIt it( *objs ); it.current(); ++it )
324     {
325       if ( it.current()->inherits( "QtxWorkstackArea" ) )
326         areaList.append( (QtxWorkstackArea*)it.current() );
327       else if ( rec && it.current()->inherits( "QSplitter" ) )
328         areas( (QSplitter*)it.current(), areaList, rec );
329     }
330   }
331 }
332
333 QtxWorkstackArea* QtxWorkstack::activeArea() const
334 {
335   return myArea;
336 }
337
338 QtxWorkstackArea* QtxWorkstack::targetArea()
339 {
340   QtxWorkstackArea* area = activeArea();
341   if ( !area )
342     area = currentArea();
343   if ( !area )
344   {
345     QPtrList<QtxWorkstackArea> lst;
346     areas( mySplit, lst );
347     if ( !lst.isEmpty() )
348       area = lst.first();
349   }
350
351   if ( !area )
352     area = createArea( mySplit );
353
354   return area;
355 }
356
357 QtxWorkstackArea* QtxWorkstack::currentArea() const
358 {
359   QtxWorkstackArea* area = 0;
360   QWidget* wid = focusWidget();
361   while ( wid && !area )
362   {
363     if ( wid->inherits( "QtxWorkstackArea" ) )
364       area = (QtxWorkstackArea*)wid;
365     wid = wid->parentWidget();
366   }
367
368   return area;
369 }
370
371 QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
372 {
373   QtxWorkstackArea* area = new QtxWorkstackArea( parent );
374
375   connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
376   connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
377   connect( area, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
378   connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
379
380   return area;
381 }
382
383 void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
384 {
385   QWidget* oldCur = myWin;
386
387   QtxWorkstackArea* oldArea = myArea;
388
389   myArea = area;
390
391   if ( myArea != oldArea )
392   {
393     if ( oldArea )
394       oldArea->updateActiveState();
395     if ( myArea )
396       myArea->updateActiveState();
397   }
398
399   if ( myArea )
400     myWin = myArea->activeWidget();
401
402   if ( myWin && oldCur != myWin )
403     emit windowActivated( myWin );
404 }
405
406 QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
407 {
408   QPtrList<QtxWorkstackArea> lst;
409   areas( mySplit, lst, true );
410   int pos = lst.find( area );
411   if ( pos < 0 )
412     return 0;
413
414   QtxWorkstackArea* na = 0;
415   for ( int i = pos - 1; i >= 0 && !na; i-- )
416   {
417     if ( !lst.at( i )->isEmpty() )
418       na = lst.at( i );
419   }
420
421   for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
422   {
423     if ( !lst.at( j )->isEmpty() )
424         na = lst.at( j );
425   }
426   return na;
427 }
428
429 QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
430 {
431   QtxWorkstackArea* area = 0;
432   QPtrList<QtxWorkstackArea> lst;
433   areas( mySplit, lst, true );
434   for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current() && !area; ++it )
435   {
436     QtxWorkstackArea* cur = it.current();
437     QRect r = cur->geometry();
438     if ( cur->parentWidget() )
439       r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
440     if ( r.contains( p ) )
441       area = cur;
442   }
443   return area;
444 }
445
446 void QtxWorkstack::updateState()
447 {
448   updateState( mySplit );
449 }
450
451 void QtxWorkstack::updateState( QSplitter* split )
452 {
453   QPtrList<QSplitter> recList;
454   splitters( split, recList, false );
455   for ( QPtrListIterator<QSplitter> itr( recList ); itr.current(); ++itr )
456     updateState( itr.current() );
457
458   QPtrList<QSplitter> splitList;
459   splitters( split, splitList, false );
460
461   QPtrList<QtxWorkstackArea> areaList;
462   areas( split, areaList, false );
463
464   bool vis = false;
465   for ( QPtrListIterator<QtxWorkstackArea> it( areaList ); it.current(); ++it )
466   {
467     if ( it.current()->isEmpty() )
468       it.current()->hide();
469     else
470     {
471       it.current()->show();
472       vis = true;
473     }
474   }
475
476   if ( split == mySplit )
477     return;
478
479   if ( areaList.isEmpty() && splitList.isEmpty() )
480     delete split;
481   else if ( vis )
482     split->show();
483   else
484     split->hide();
485 }
486
487 /*!
488     Class: QtxWorkstackArea [Internal]
489     Descr:
490 */
491
492 QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
493 : QWidget( parent )
494 {
495   QVBoxLayout* base = new QVBoxLayout( this );
496
497   QHBox* top = new QHBox( this );
498   base->addWidget( top );
499
500   myBar = new QtxWorkstackTabBar( top );
501
502   QPushButton* close = new QPushButton( top );
503   close->setPixmap( style().stylePixmap( QStyle::SP_TitleBarCloseButton ) );
504   close->setAutoDefault( true );
505   close->setFlat( true );
506   myClose = close;
507
508   top->setStretchFactor( myBar, 1 );
509
510   myStack = new QWidgetStack( this );
511
512   base->addWidget( myStack, 1 );
513
514   connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
515   connect( myBar, SIGNAL( selected( int ) ), this, SLOT( onSelected( int ) ) );
516   connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
517   connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SIGNAL( contextMenuRequested( QPoint ) ) );
518
519   updateState();
520
521   updateActiveState();
522
523   qApp->installEventFilter( this );
524 }
525
526 QtxWorkstackArea::~QtxWorkstackArea()
527 {
528   qApp->removeEventFilter( this );
529 }
530
531 bool QtxWorkstackArea::isEmpty() const
532 {
533   bool res = false;
534   for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !res; ++it )
535     res = it.data().vis;
536   return !res;
537 }
538
539 void QtxWorkstackArea::insertWidget( QWidget* wid, const int idx )
540 {
541   if ( !wid )
542     return;
543
544   int pos = myList.find( wid );
545   if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
546     return;
547
548   myList.removeRef( wid );
549   pos = idx < 0 ? myList.count() : idx;
550   myList.insert( QMIN( pos, (int)myList.count() ), wid );
551   if ( !myInfo.contains( wid ) )
552   {
553     QtxWorkstackChild* child = new QtxWorkstackChild( wid, myStack );
554     myChild.insert( wid, child );
555     myInfo.insert( wid, WidgetInfo() );
556     myInfo[wid].id = generateId();
557     myInfo[wid].vis = wid->isVisibleTo( wid->parentWidget() );
558
559     connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
560     connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
561     connect( child, SIGNAL( hided( QtxWorkstackChild* ) ), this, SLOT( onChildHided( QtxWorkstackChild* ) ) );
562     connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
563     connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
564   }
565
566   updateState();
567
568   setWidgetActive( wid );
569 }
570
571 void QtxWorkstackArea::removeWidget( QWidget* wid )
572 {
573   if ( !myList.contains( wid ) )
574     return;
575
576   if ( myBar->tab( widgetId( wid ) ) )
577     myBar->removeTab( myBar->tab( widgetId( wid ) ) );
578   myStack->removeWidget( child( wid ) );
579
580   myList.remove( wid );
581   myInfo.remove( wid );
582   myChild.remove( wid );
583
584   delete child( wid );
585
586   if ( myList.isEmpty() )
587     delete this;
588   else
589     updateState();
590 }
591
592 QWidgetList QtxWorkstackArea::widgetList() const
593 {
594   QWidgetList lst;
595   for ( QWidgetListIt it( myList ); it.current(); ++it )
596   {
597     if ( widgetVisibility( it.current() ) )
598       lst.append( it.current() );
599   }
600   return lst;
601 }
602
603 QWidget* QtxWorkstackArea::activeWidget() const
604 {
605   return widget( myBar->currentTab() );
606 }
607
608 void QtxWorkstackArea::setActiveWidget( QWidget* wid )
609 {
610   myBar->setCurrentTab( widgetId( wid ) );
611 }
612
613 bool QtxWorkstackArea::contains( QWidget* wid ) const
614 {
615   return myList.contains( wid );
616 }
617
618 void QtxWorkstackArea::show()
619 {
620   QMap<QWidget*, bool> map;
621   for ( QWidgetListIt it( myList ); it.current(); ++it )
622   {
623     map.insert( it.current(), isBlocked( it.current() ) );
624     setBlocked( it.current(), true );
625   }
626
627   QWidget::show();
628
629   for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
630     setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
631 }
632
633 void QtxWorkstackArea::hide()
634 {
635   QMap<QWidget*, bool> map;
636   for ( QWidgetListIt it( myList ); it.current(); ++it )
637   {
638     map.insert( it.current(), isBlocked( it.current() ) );
639     setBlocked( it.current(), true );
640   }
641
642   QWidget::hide();
643
644   for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
645     setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
646 }
647
648 bool QtxWorkstackArea::isActive() const
649 {
650   QtxWorkstack* ws = workstack();
651   if ( !ws )
652     return false;
653
654   return ws->activeArea() == this;
655 }
656
657 void QtxWorkstackArea::updateActiveState()
658 {
659   myBar->setActive( isActive() );
660 }
661
662 QtxWorkstack* QtxWorkstackArea::workstack() const
663 {
664   QtxWorkstack* ws = 0;
665   QWidget* wid = parentWidget();
666   while ( wid && !ws )
667   {
668     if ( wid->inherits( "QtxWorkstack" ) )
669       ws = (QtxWorkstack*)wid;
670     wid = wid->parentWidget();
671   }
672   return ws;
673 }
674
675 bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
676 {
677   if ( o->isWidgetType() )
678   {
679     QWidget* wid = (QWidget*)o;
680     if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
681     {
682       bool ok = false;
683       while ( !ok && wid && wid != myClose )
684       {
685         ok = wid == this;
686         wid = wid->parentWidget();
687       }
688       if ( ok )
689         QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateEvent : FocusEvent ) ) );
690     }
691   }
692   return false;
693 }
694
695 QRect QtxWorkstackArea::floatRect() const
696 {
697   QRect r = myStack->geometry();
698   return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
699 }
700
701 QRect QtxWorkstackArea::floatTab( const int idx ) const
702 {
703   return myBar->tabRect( idx );
704 }
705
706 int QtxWorkstackArea::tabAt( const QPoint& p ) const
707 {
708   int idx = -1;
709   for ( int i = 0; i < myBar->count() && idx == -1; i++ )
710   {
711     QRect r = myBar->tabRect( i );
712     if ( r.isValid() && r.contains( p ) )
713       idx = i;
714   }
715   return idx;
716 }
717
718 void QtxWorkstackArea::customEvent( QCustomEvent* e )
719 {
720   switch ( e->type() )
721   {
722   case ActivateEvent:
723     emit activated( activeWidget() );
724     break;
725   case FocusEvent:
726     if ( activeWidget() )
727       activeWidget()->setFocus();
728     break;
729   case RemoveEvent:
730     removeWidget( (QWidget*)e->data() );
731     break;
732   }
733 }
734
735 void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
736 {
737   QWidget::focusInEvent( e );
738
739   emit activated( activeWidget() );
740 }
741
742 void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
743 {
744   QWidget::mousePressEvent( e );
745
746   emit activated( activeWidget() );
747 }
748
749 void QtxWorkstackArea::onClose()
750 {
751   QWidget* wid = activeWidget();
752   if ( wid )
753     wid->close();
754 }
755
756 void QtxWorkstackArea::onSelected( int id )
757 {
758   updateCurrent();
759
760   emit activated( activeWidget() );
761 }
762
763 void QtxWorkstackArea::onDragActiveTab()
764 {
765   QtxWorkstackChild* c = child( activeWidget() );
766   if ( !c )
767     return;
768
769   new QtxWorkstackDrag( workstack(), c );
770 }
771
772 void QtxWorkstackArea::onChildDestroyed( QObject* obj )
773 {
774   QtxWorkstackChild* child = (QtxWorkstackChild*)obj;
775   myStack->removeWidget( child );
776
777   QWidget* wid = 0;
778   for ( ChildMap::ConstIterator it = myChild.begin(); it != myChild.end() && !wid; ++it )
779   {
780     if ( it.data() == child )
781       wid = it.key();
782   }
783
784   myChild.remove( wid );
785
786   QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)RemoveEvent, wid ) );
787 }
788
789 void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
790 {
791   setWidgetShown( c->widget(), true );
792 }
793
794 void QtxWorkstackArea::onChildHided( QtxWorkstackChild* c )
795 {
796   setWidgetShown( c->widget(), false );
797 }
798
799 void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
800 {
801   setWidgetActive( c->widget() );
802 }
803
804 void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
805 {
806   updateTab( c->widget() );
807 }
808
809 void QtxWorkstackArea::updateCurrent()
810 {
811   QMap<QWidget*, bool> map;
812   for ( QWidgetListIt it( myList ); it.current(); ++it )
813   {
814     map.insert( it.current(), isBlocked( it.current() ) );
815     setBlocked( it.current(), true );
816   }
817
818   myStack->raiseWidget( myBar->currentTab() );
819
820   for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
821     setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
822 }
823
824 void QtxWorkstackArea::updateTab( QWidget* wid )
825 {
826   QTab* tab = myBar->tab( widgetId( wid ) );
827   if ( !tab )
828     return;
829
830   QIconSet icoSet;
831   if ( wid->icon() )
832     icoSet = QIconSet( *wid->icon() );
833
834   tab->setIconSet( icoSet );
835   tab->setText( wid->caption() );
836 }
837
838 QWidget* QtxWorkstackArea::widget( const int id ) const
839 {
840   QWidget* wid = 0;
841   for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !wid; ++it )
842   {
843     if ( it.data().id == id )
844       wid = it.key();
845   }
846   return wid;
847 }
848
849 int QtxWorkstackArea::widgetId( QWidget* wid ) const
850 {
851   int id = -1;
852   if ( myInfo.contains( wid ) )
853     id = myInfo[wid].id;
854   return id;
855 }
856
857 bool QtxWorkstackArea::widgetVisibility( QWidget* wid ) const
858 {
859   bool res = false;
860   if ( myInfo.contains( wid ) )
861     res = myInfo[wid].vis;
862   return res;
863 }
864
865 void QtxWorkstackArea::setWidgetActive( QWidget* wid )
866 {
867   int id = widgetId( wid );
868   if ( id < 0 )
869     return;
870
871   myBar->setCurrentTab( id );
872 }
873
874 void QtxWorkstackArea::setWidgetShown( QWidget* wid, const bool on )
875 {
876   if ( isBlocked( wid ) || !myInfo.contains( wid ) || myInfo[wid].vis == on )
877     return;
878
879   myInfo[wid].vis = on;
880   updateState();
881 }
882
883 void QtxWorkstackArea::updateState()
884 {
885   bool updBar = myBar->isUpdatesEnabled();
886   bool updStk = myStack->isUpdatesEnabled();
887   myBar->setUpdatesEnabled( false );
888   myStack->setUpdatesEnabled( false );
889
890   bool block = myBar->signalsBlocked();
891   myBar->blockSignals( true );
892
893   QWidget* prev = activeWidget();
894
895   int idx = 0;
896   for ( QWidgetListIt it( myList ); it.current(); ++it )
897   {
898     QWidget* wid = it.current();
899     int id = widgetId( wid );
900
901     if ( id < 0 )
902       continue;
903
904     bool vis = widgetVisibility( wid );
905
906     if ( myBar->tab( id ) && ( !vis || myBar->indexOf( id ) != idx ) )
907       myBar->removeTab( myBar->tab( id ) );
908
909     if ( !myBar->tab( id ) && vis )
910     {
911       QTab* tab = new QTab( wid->caption() );
912       myBar->insertTab( tab, idx );
913       tab->setIdentifier( id );
914     }
915
916     updateTab( wid );
917
918     bool block = isBlocked( wid );
919     setBlocked( wid, true );
920
921     QtxWorkstackChild* cont = child( wid );
922
923     if ( !vis )
924       myStack->removeWidget( cont );
925     else if ( !myStack->widget( id ) )
926       myStack->addWidget( cont, id );
927
928     if ( vis )
929       idx++;
930
931     setBlocked( wid, block );
932   }
933
934   int curId = widgetId( prev );
935   if ( !myBar->tab( curId ) )
936   {
937     QWidget* wid = 0;
938     int pos = myList.find( prev );
939     for ( int i = pos - 1; i >= 0 && !wid; i-- )
940     {
941       if ( widgetVisibility( myList.at( i ) ) )
942         wid = myList.at( i );
943     }
944
945     for ( int j = pos + 1; j < (int)myList.count() && !wid; j++ )
946     {
947       if ( widgetVisibility( myList.at( j ) ) )
948         wid = myList.at( j );
949     }
950
951     if ( wid )
952       curId = widgetId( wid );
953   }
954
955   myBar->setCurrentTab( curId );
956
957   myBar->blockSignals( block );
958
959   updateCurrent();
960
961   myBar->setUpdatesEnabled( updBar );
962   myStack->setUpdatesEnabled( updStk );
963   if ( updBar )
964     myBar->update();
965   if ( updStk )
966     myStack->update();
967
968   QResizeEvent re( myBar->size(), myBar->size() );
969   QApplication::sendEvent( myBar, &re );
970
971   if ( isEmpty() )
972   {
973     hide();
974     emit deactivated( this );
975   }
976   else
977   {
978     show();
979     if ( prev != activeWidget() )
980       emit activated( activeWidget() );
981   }
982 }
983
984 int QtxWorkstackArea::generateId() const
985 {
986   QMap<int, int> map;
987
988   for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end(); ++it )
989     map.insert( it.data().id, 0 );
990
991   int id = 0;
992   while ( map.contains( id ) )
993     id++;
994
995   return id;
996 }
997
998 bool QtxWorkstackArea::isBlocked( QWidget* wid ) const
999 {
1000   return myBlock.contains( wid );
1001 }
1002
1003 void QtxWorkstackArea::setBlocked( QWidget* wid, const bool on )
1004 {
1005   if ( on )
1006     myBlock.insert( wid, 0 );
1007   else
1008     myBlock.remove( wid );
1009 }
1010
1011 QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
1012 {
1013   QtxWorkstackChild* res = 0;
1014   if ( myChild.contains( wid ) )
1015     res = myChild[wid];
1016   return res;
1017 }
1018
1019 /*!
1020     Class: QtxWorkstackChild [Internal]
1021     Descr:
1022 */
1023
1024 QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent )
1025 : QHBox( parent ),
1026 myWidget( wid )
1027 {
1028   myWidget->reparent( this, QPoint( 0, 0 ), myWidget->isVisibleTo( myWidget->parentWidget() ) );
1029   myWidget->installEventFilter( this );
1030
1031   connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1032 }
1033
1034 QtxWorkstackChild::~QtxWorkstackChild()
1035 {
1036   qApp->removeEventFilter( this );
1037
1038   if ( !widget() )
1039     return;
1040
1041   widget()->removeEventFilter( this );
1042   widget()->reparent( 0, QPoint( 0, 0 ), false );
1043   disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1044 }
1045
1046 QWidget* QtxWorkstackChild::widget() const
1047 {
1048   return myWidget;
1049 }
1050
1051 bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
1052 {
1053   if ( o->isWidgetType() )
1054   {
1055     if ( e->type() == QEvent::CaptionChange || e->type() == QEvent::IconChange )
1056       emit captionChanged( this );
1057
1058     if ( !e->spontaneous() && ( e->type() == QEvent::Show || e->type() == QEvent::ShowToParent ) )
1059       emit shown( this );
1060
1061     if ( !e->spontaneous() && ( e->type() == QEvent::Hide || e->type() == QEvent::HideToParent ) )
1062       emit hided( this );
1063
1064     if ( e->type() == QEvent::FocusIn )
1065       emit activated( this );
1066   }
1067   return QHBox::eventFilter( o, e );
1068 }
1069
1070 void QtxWorkstackChild::onDestroyed( QObject* obj )
1071 {
1072   if ( obj != widget() )
1073     return;
1074
1075   myWidget = 0;
1076   deleteLater();
1077 }
1078
1079 void QtxWorkstackChild::childEvent( QChildEvent* e )
1080 {
1081   if ( e->type() == QEvent::ChildRemoved && e->child() == widget() )
1082   {
1083     myWidget = 0;
1084     deleteLater();
1085   }
1086   QHBox::childEvent( e );
1087 }
1088
1089 /*!
1090     Class: QtxWorkstackTabBar [Internal]
1091     Descr:
1092 */
1093
1094 QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
1095 : QTabBar( parent ),
1096 myId( -1 )
1097 {
1098 }
1099
1100 QtxWorkstackTabBar::~QtxWorkstackTabBar()
1101 {
1102 }
1103
1104 void QtxWorkstackTabBar::setActive( const bool on )
1105 {
1106   QFont aFont = font();
1107   aFont.setUnderline( on );
1108   setFont( aFont );
1109
1110   update();
1111 }
1112
1113 QRect QtxWorkstackTabBar::tabRect( const int idx ) const
1114 {
1115   QRect r;
1116   QTab* t = tabAt( idx );
1117   if ( t )
1118   {
1119     r = t->rect();
1120     r.setLeft( QMAX( r.left(), 0 ) );
1121
1122     int x1 = tabAt( 0 )->rect().left();
1123     int x2 = tabAt( count() - 1 )->rect().right();
1124
1125     int bw = 0;
1126     if ( QABS( x2 - x1 ) > width() )
1127 #if defined QT_VERSION && QT_VERSION >= 0x30300
1128       bw = 2 * style().pixelMetric( QStyle::PM_TabBarScrollButtonWidth, this );
1129 #else
1130       bw = 2 * 16;
1131 #endif
1132
1133     int limit = width() - bw;
1134     r.setRight( QMIN( r.right(), limit ) );
1135
1136     r = QRect( mapToGlobal( r.topLeft() ), r.size() );
1137   }
1138   return r;
1139 }
1140
1141 void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
1142 {
1143   if ( myId != -1 && !tab( myId )->rect().contains( e->pos() ) )
1144   {
1145     myId = -1;
1146     emit dragActiveTab();
1147   }
1148
1149   QTabBar::mouseMoveEvent( e );
1150 }
1151
1152 void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
1153 {
1154   QTabBar::mousePressEvent( e );
1155
1156   if ( e->button() == LeftButton )
1157     myId = currentTab();
1158 }
1159
1160 void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
1161 {
1162   QTabBar::mouseReleaseEvent( e );
1163
1164   myId = -1;
1165
1166   if ( e->button() == RightButton )
1167     emit contextMenuRequested( e->globalPos() );
1168 }
1169
1170 void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
1171 {
1172   if ( e->reason() != QContextMenuEvent::Mouse )
1173     emit contextMenuRequested( e->globalPos() );
1174 }
1175
1176 void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
1177 {
1178   if ( currentTab() != t->identifier() )
1179   {
1180     QFont fnt = p->font();
1181     fnt.setUnderline( false );
1182     p->setFont( fnt );
1183   }
1184   QTabBar::paintLabel( p, br, t, has_focus );
1185 }
1186
1187 /*!
1188     Class: QtxWorkstackDrag [Internal]
1189     Descr:
1190 */
1191
1192 QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
1193 : QObject( 0 ),
1194 myWS( ws ),
1195 myTab( -1 ),
1196 myArea( 0 ),
1197 myPainter( 0 ),
1198 myChild( child )
1199 {
1200   qApp->installEventFilter( this );
1201 }
1202
1203 QtxWorkstackDrag::~QtxWorkstackDrag()
1204 {
1205   qApp->removeEventFilter( this );
1206
1207   endDrawRect();
1208 }
1209
1210 bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
1211 {
1212   switch ( e->type() )
1213   {
1214   case QEvent::MouseMove:
1215     updateTarget( ((QMouseEvent*)e)->globalPos() );
1216     break;
1217   case QEvent::MouseButtonRelease:
1218     drawRect();
1219     endDrawRect();
1220     dropWidget();
1221     deleteLater();
1222     break;
1223   default:
1224     return false;
1225   }
1226   return true;
1227 }
1228
1229 void QtxWorkstackDrag::updateTarget( const QPoint& p )
1230 {
1231   int tab = -1;
1232   QtxWorkstackArea* area = detectTarget( p, tab );
1233   setTarget( area, tab );
1234 }
1235
1236 QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
1237 {
1238   if ( p.isNull() )
1239     return 0;
1240
1241   QtxWorkstackArea* area = myWS->areaAt( p );
1242   if ( area )
1243     tab = area->tabAt( p );
1244   return area;
1245 }
1246
1247 void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
1248 {
1249   if ( !area || ( myArea == area && tab == myTab ) )
1250     return;
1251
1252   startDrawRect();
1253
1254   if ( myArea )
1255     drawRect();
1256
1257   myTab = tab;
1258   myArea = area;
1259
1260   if ( myArea )
1261     drawRect();
1262 }
1263
1264 void QtxWorkstackDrag::dropWidget()
1265 {
1266   if ( myArea )
1267     myArea->insertWidget( myChild->widget(), myTab );
1268 }
1269
1270 void QtxWorkstackDrag::drawRect()
1271 {
1272   if ( !myPainter || !myArea )
1273     return;
1274
1275   QRect r = myArea->floatRect();
1276   int m = myPainter->pen().width();
1277
1278   r.setTop( r.top() + m + 2 );
1279   r.setLeft( r.left() + m + 2 );
1280   r.setRight( r.right() - m - 2 );
1281   r.setBottom( r.bottom() - m - 2 );
1282
1283   myPainter->drawRect( r );
1284
1285   QRect tr = myArea->floatTab( myTab );
1286   tr.setTop( tr.top() + m );
1287   tr.setLeft( tr.left() + m );
1288   tr.setRight( tr.right() - m );
1289   tr.setBottom( tr.bottom() - m );
1290
1291   myPainter->drawRect( tr );
1292 }
1293
1294 void QtxWorkstackDrag::endDrawRect()
1295 {
1296   delete myPainter;
1297   myPainter = 0;
1298 }
1299
1300 void QtxWorkstackDrag::startDrawRect()
1301 {
1302   if ( myPainter )
1303     return;
1304
1305   int scr = QApplication::desktop()->screenNumber( (QWidget*)this );
1306   QWidget* paint_on = QApplication::desktop()->screen( scr );
1307
1308   myPainter = new QPainter( paint_on, true );
1309   myPainter->setPen( QPen( gray, 3 ) );
1310   myPainter->setRasterOp( XorROP );
1311 }