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