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