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