]> SALOME platform Git repositories - modules/gui.git/blob - src/Qtx/QtxWorkstack.cxx
Salome HOME
no message
[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   if ( areaList.isEmpty() && splitList.isEmpty() )
495     delete split;
496   else if ( vis )
497     split->show();
498   else
499     split->hide();
500 }
501
502 /*!
503     Class: QtxWorkstackArea [Internal]
504     Descr:
505 */
506
507 QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
508 : QWidget( parent )
509 {
510   QVBoxLayout* base = new QVBoxLayout( this );
511
512   QHBox* top = new QHBox( this );
513   base->addWidget( top );
514
515   myBar = new QtxWorkstackTabBar( top );
516
517   QPushButton* close = new QPushButton( top );
518   close->setPixmap( style().stylePixmap( QStyle::SP_TitleBarCloseButton ) );
519   close->setAutoDefault( true );
520   close->setFlat( true );
521   myClose = close;
522
523   top->setStretchFactor( myBar, 1 );
524
525   myStack = new QWidgetStack( this );
526
527   base->addWidget( myStack, 1 );
528
529   connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
530   connect( myBar, SIGNAL( selected( int ) ), this, SLOT( onSelected( int ) ) );
531   connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
532   connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SIGNAL( contextMenuRequested( QPoint ) ) );
533
534   updateState();
535
536   updateActiveState();
537
538   qApp->installEventFilter( this );
539 }
540
541 QtxWorkstackArea::~QtxWorkstackArea()
542 {
543   qApp->removeEventFilter( this );
544 }
545
546 bool QtxWorkstackArea::isEmpty() const
547 {
548   bool res = false;
549   for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !res; ++it )
550     res = it.data().vis;
551   return !res;
552 }
553
554 void QtxWorkstackArea::insertWidget( QWidget* wid, const int idx )
555 {
556   if ( !wid )
557     return;
558
559   int pos = myList.find( wid );
560   if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
561     return;
562
563   myList.removeRef( wid );
564   pos = idx < 0 ? myList.count() : idx;
565   myList.insert( QMIN( pos, (int)myList.count() ), wid );
566   if ( !myInfo.contains( wid ) )
567   {
568     QtxWorkstackChild* child = new QtxWorkstackChild( wid, myStack );
569     myChild.insert( wid, child );
570     myInfo.insert( wid, WidgetInfo() );
571     myInfo[wid].id = generateId();
572     myInfo[wid].vis = wid->isVisibleTo( wid->parentWidget() );
573
574     connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
575     connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
576     connect( child, SIGNAL( hided( QtxWorkstackChild* ) ), this, SLOT( onChildHided( QtxWorkstackChild* ) ) );
577     connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
578     connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
579   }
580
581   updateState();
582
583   setWidgetActive( wid );
584 }
585
586 void QtxWorkstackArea::removeWidget( QWidget* wid )
587 {
588   if ( !myList.contains( wid ) )
589     return;
590
591   if ( myBar->tab( widgetId( wid ) ) )
592     myBar->removeTab( myBar->tab( widgetId( wid ) ) );
593   myStack->removeWidget( child( wid ) );
594
595   myList.remove( wid );
596   myInfo.remove( wid );
597   myChild.remove( wid );
598
599   delete child( wid );
600
601   if ( myList.isEmpty() )
602     delete this;
603   else
604     updateState();
605 }
606
607 QWidgetList QtxWorkstackArea::widgetList() const
608 {
609   QWidgetList lst;
610   for ( QWidgetListIt it( myList ); it.current(); ++it )
611   {
612     if ( widgetVisibility( it.current() ) )
613       lst.append( it.current() );
614   }
615   return lst;
616 }
617
618 QWidget* QtxWorkstackArea::activeWidget() const
619 {
620   return widget( myBar->currentTab() );
621 }
622
623 void QtxWorkstackArea::setActiveWidget( QWidget* wid )
624 {
625   myBar->setCurrentTab( widgetId( wid ) );
626 }
627
628 bool QtxWorkstackArea::contains( QWidget* wid ) const
629 {
630   return myList.contains( wid );
631 }
632
633 void QtxWorkstackArea::show()
634 {
635   QMap<QWidget*, bool> map;
636   for ( QWidgetListIt it( myList ); it.current(); ++it )
637   {
638     map.insert( it.current(), isBlocked( it.current() ) );
639     setBlocked( it.current(), true );
640   }
641
642   QWidget::show();
643
644   for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
645     setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
646 }
647
648 void QtxWorkstackArea::hide()
649 {
650   QMap<QWidget*, bool> map;
651   for ( QWidgetListIt it( myList ); it.current(); ++it )
652   {
653     map.insert( it.current(), isBlocked( it.current() ) );
654     setBlocked( it.current(), true );
655   }
656
657   QWidget::hide();
658
659   for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
660     setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
661 }
662
663 bool QtxWorkstackArea::isActive() const
664 {
665   QtxWorkstack* ws = workstack();
666   if ( !ws )
667     return false;
668
669   return ws->activeArea() == this;
670 }
671
672 void QtxWorkstackArea::updateActiveState()
673 {
674   myBar->setActive( isActive() );
675 }
676
677 QtxWorkstack* QtxWorkstackArea::workstack() const
678 {
679   QtxWorkstack* ws = 0;
680   QWidget* wid = parentWidget();
681   while ( wid && !ws )
682   {
683     if ( wid->inherits( "QtxWorkstack" ) )
684       ws = (QtxWorkstack*)wid;
685     wid = wid->parentWidget();
686   }
687   return ws;
688 }
689
690 bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
691 {
692   if ( o->isWidgetType() )
693   {
694     QWidget* wid = (QWidget*)o;
695     if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
696     {
697       bool ok = false;
698       while ( !ok && wid && wid != myClose )
699       {
700         ok = wid == this;
701         wid = wid->parentWidget();
702       }
703       if ( ok )
704         QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateEvent : FocusEvent ) ) );
705     }
706   }
707   return false;
708 }
709
710 QRect QtxWorkstackArea::floatRect() const
711 {
712   QRect r = myStack->geometry();
713   return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
714 }
715
716 QRect QtxWorkstackArea::floatTab( const int idx ) const
717 {
718   return myBar->tabRect( idx );
719 }
720
721 int QtxWorkstackArea::tabAt( const QPoint& p ) const
722 {
723   int idx = -1;
724   for ( int i = 0; i < myBar->count() && idx == -1; i++ )
725   {
726     QRect r = myBar->tabRect( i );
727     if ( r.isValid() && r.contains( p ) )
728       idx = i;
729   }
730   return idx;
731 }
732
733 void QtxWorkstackArea::customEvent( QCustomEvent* e )
734 {
735   switch ( e->type() )
736   {
737   case ActivateEvent:
738     emit activated( activeWidget() );
739     break;
740   case FocusEvent:
741     if ( activeWidget() )
742       activeWidget()->setFocus();
743     break;
744   case RemoveEvent:
745     removeWidget( (QWidget*)e->data() );
746     break;
747   }
748 }
749
750 void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
751 {
752   QWidget::focusInEvent( e );
753
754   emit activated( activeWidget() );
755 }
756
757 void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
758 {
759   QWidget::mousePressEvent( e );
760
761   emit activated( activeWidget() );
762 }
763
764 void QtxWorkstackArea::onClose()
765 {
766   QWidget* wid = activeWidget();
767   if ( wid )
768     wid->close();
769 }
770
771 void QtxWorkstackArea::onSelected( int id )
772 {
773   updateCurrent();
774
775   emit activated( activeWidget() );
776 }
777
778 void QtxWorkstackArea::onDragActiveTab()
779 {
780   QtxWorkstackChild* c = child( activeWidget() );
781   if ( !c )
782     return;
783
784   new QtxWorkstackDrag( workstack(), c );
785 }
786
787 void QtxWorkstackArea::onChildDestroyed( QObject* obj )
788 {
789   QtxWorkstackChild* child = (QtxWorkstackChild*)obj;
790   myStack->removeWidget( child );
791
792   QWidget* wid = 0;
793   for ( ChildMap::ConstIterator it = myChild.begin(); it != myChild.end() && !wid; ++it )
794   {
795     if ( it.data() == child )
796       wid = it.key();
797   }
798
799   myChild.remove( wid );
800
801   QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)RemoveEvent, wid ) );
802 }
803
804 void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
805 {
806   setWidgetShown( c->widget(), true );
807 }
808
809 void QtxWorkstackArea::onChildHided( QtxWorkstackChild* c )
810 {
811   setWidgetShown( c->widget(), false );
812 }
813
814 void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
815 {
816   setWidgetActive( c->widget() );
817 }
818
819 void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
820 {
821   updateTab( c->widget() );
822 }
823
824 void QtxWorkstackArea::updateCurrent()
825 {
826   QMap<QWidget*, bool> map;
827   for ( QWidgetListIt it( myList ); it.current(); ++it )
828   {
829     map.insert( it.current(), isBlocked( it.current() ) );
830     setBlocked( it.current(), true );
831   }
832
833   myStack->raiseWidget( myBar->currentTab() );
834
835   for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
836     setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
837 }
838
839 void QtxWorkstackArea::updateTab( QWidget* wid )
840 {
841   QTab* tab = myBar->tab( widgetId( wid ) );
842   if ( !tab )
843     return;
844
845   QIconSet icoSet;
846   if ( wid->icon() )
847     icoSet = QIconSet( *wid->icon() );
848
849   tab->setIconSet( icoSet );
850   tab->setText( wid->caption() );
851 }
852
853 QWidget* QtxWorkstackArea::widget( const int id ) const
854 {
855   QWidget* wid = 0;
856   for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !wid; ++it )
857   {
858     if ( it.data().id == id )
859       wid = it.key();
860   }
861   return wid;
862 }
863
864 int QtxWorkstackArea::widgetId( QWidget* wid ) const
865 {
866   int id = -1;
867   if ( myInfo.contains( wid ) )
868     id = myInfo[wid].id;
869   return id;
870 }
871
872 bool QtxWorkstackArea::widgetVisibility( QWidget* wid ) const
873 {
874   bool res = false;
875   if ( myInfo.contains( wid ) )
876     res = myInfo[wid].vis;
877   return res;
878 }
879
880 void QtxWorkstackArea::setWidgetActive( QWidget* wid )
881 {
882   int id = widgetId( wid );
883   if ( id < 0 )
884     return;
885
886   myBar->setCurrentTab( id );
887 }
888
889 void QtxWorkstackArea::setWidgetShown( QWidget* wid, const bool on )
890 {
891   if ( isBlocked( wid ) || !myInfo.contains( wid ) || myInfo[wid].vis == on )
892     return;
893
894   myInfo[wid].vis = on;
895   updateState();
896 }
897
898 void QtxWorkstackArea::updateState()
899 {
900   bool updBar = myBar->isUpdatesEnabled();
901   bool updStk = myStack->isUpdatesEnabled();
902   myBar->setUpdatesEnabled( false );
903   myStack->setUpdatesEnabled( false );
904
905   bool block = myBar->signalsBlocked();
906   myBar->blockSignals( true );
907
908   QWidget* prev = activeWidget();
909
910   int idx = 0;
911   for ( QWidgetListIt it( myList ); it.current(); ++it )
912   {
913     QWidget* wid = it.current();
914     int id = widgetId( wid );
915
916     if ( id < 0 )
917       continue;
918
919     bool vis = widgetVisibility( wid );
920
921     if ( myBar->tab( id ) && ( !vis || myBar->indexOf( id ) != idx ) )
922       myBar->removeTab( myBar->tab( id ) );
923
924     if ( !myBar->tab( id ) && vis )
925     {
926       QTab* tab = new QTab( wid->caption() );
927       myBar->insertTab( tab, idx );
928       tab->setIdentifier( id );
929     }
930
931     updateTab( wid );
932
933     bool block = isBlocked( wid );
934     setBlocked( wid, true );
935
936     QtxWorkstackChild* cont = child( wid );
937
938     if ( !vis )
939       myStack->removeWidget( cont );
940     else if ( !myStack->widget( id ) )
941       myStack->addWidget( cont, id );
942
943     if ( vis )
944       idx++;
945
946     setBlocked( wid, block );
947   }
948
949   int curId = widgetId( prev );
950   if ( !myBar->tab( curId ) )
951   {
952     QWidget* wid = 0;
953     int pos = myList.find( prev );
954     for ( int i = pos - 1; i >= 0 && !wid; i-- )
955     {
956       if ( widgetVisibility( myList.at( i ) ) )
957         wid = myList.at( i );
958     }
959
960     for ( int j = pos + 1; j < (int)myList.count() && !wid; j++ )
961     {
962       if ( widgetVisibility( myList.at( j ) ) )
963         wid = myList.at( j );
964     }
965
966     if ( wid )
967       curId = widgetId( wid );
968   }
969
970   myBar->setCurrentTab( curId );
971
972   myBar->blockSignals( block );
973
974   updateCurrent();
975
976   myBar->setUpdatesEnabled( updBar );
977   myStack->setUpdatesEnabled( updStk );
978   if ( updBar )
979     myBar->update();
980   if ( updStk )
981     myStack->update();
982
983   QResizeEvent re( myBar->size(), myBar->size() );
984   QApplication::sendEvent( myBar, &re );
985
986   if ( isEmpty() )
987   {
988     hide();
989     emit deactivated( this );
990   }
991   else
992   {
993     show();
994     if ( prev != activeWidget() )
995       emit activated( activeWidget() );
996   }
997 }
998
999 int QtxWorkstackArea::generateId() const
1000 {
1001   QMap<int, int> map;
1002
1003   for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end(); ++it )
1004     map.insert( it.data().id, 0 );
1005
1006   int id = 0;
1007   while ( map.contains( id ) )
1008     id++;
1009
1010   return id;
1011 }
1012
1013 bool QtxWorkstackArea::isBlocked( QWidget* wid ) const
1014 {
1015   return myBlock.contains( wid );
1016 }
1017
1018 void QtxWorkstackArea::setBlocked( QWidget* wid, const bool on )
1019 {
1020   if ( on )
1021     myBlock.insert( wid, 0 );
1022   else
1023     myBlock.remove( wid );
1024 }
1025
1026 QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
1027 {
1028   QtxWorkstackChild* res = 0;
1029   if ( myChild.contains( wid ) )
1030     res = myChild[wid];
1031   return res;
1032 }
1033
1034 /*!
1035     Class: QtxWorkstackChild [Internal]
1036     Descr:
1037 */
1038
1039 QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent )
1040 : QHBox( parent ),
1041 myWidget( wid )
1042 {
1043   myWidget->reparent( this, QPoint( 0, 0 ), myWidget->isVisibleTo( myWidget->parentWidget() ) );
1044   myWidget->installEventFilter( this );
1045
1046   connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1047 }
1048
1049 QtxWorkstackChild::~QtxWorkstackChild()
1050 {
1051   qApp->removeEventFilter( this );
1052
1053   if ( !widget() )
1054     return;
1055
1056   widget()->removeEventFilter( this );
1057   widget()->reparent( 0, QPoint( 0, 0 ), false );
1058   disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1059 }
1060
1061 QWidget* QtxWorkstackChild::widget() const
1062 {
1063   return myWidget;
1064 }
1065
1066 bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
1067 {
1068   if ( o->isWidgetType() )
1069   {
1070     if ( e->type() == QEvent::CaptionChange || e->type() == QEvent::IconChange )
1071       emit captionChanged( this );
1072
1073     if ( !e->spontaneous() && ( e->type() == QEvent::Show || e->type() == QEvent::ShowToParent ) )
1074       emit shown( this );
1075
1076     if ( !e->spontaneous() && ( e->type() == QEvent::Hide || e->type() == QEvent::HideToParent ) )
1077       emit hided( this );
1078
1079     if ( e->type() == QEvent::FocusIn )
1080       emit activated( this );
1081   }
1082   return QHBox::eventFilter( o, e );
1083 }
1084
1085 void QtxWorkstackChild::onDestroyed( QObject* obj )
1086 {
1087   if ( obj != widget() )
1088     return;
1089
1090   myWidget = 0;
1091   deleteLater();
1092 }
1093
1094 void QtxWorkstackChild::childEvent( QChildEvent* e )
1095 {
1096   if ( e->type() == QEvent::ChildRemoved && e->child() == widget() )
1097   {
1098     myWidget = 0;
1099     deleteLater();
1100   }
1101   QHBox::childEvent( e );
1102 }
1103
1104 /*!
1105     Class: QtxWorkstackTabBar [Internal]
1106     Descr:
1107 */
1108
1109 QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
1110 : QTabBar( parent ),
1111 myId( -1 )
1112 {
1113 }
1114
1115 QtxWorkstackTabBar::~QtxWorkstackTabBar()
1116 {
1117 }
1118
1119 void QtxWorkstackTabBar::setActive( const bool on )
1120 {
1121   QFont aFont = font();
1122   aFont.setUnderline( on );
1123   setFont( aFont );
1124
1125   update();
1126 }
1127
1128 QRect QtxWorkstackTabBar::tabRect( const int idx ) const
1129 {
1130   QRect r;
1131   QTab* t = tabAt( idx );
1132   if ( t )
1133   {
1134     r = t->rect();
1135     r.setLeft( QMAX( r.left(), 0 ) );
1136
1137     int x1 = tabAt( 0 )->rect().left();
1138     int x2 = tabAt( count() - 1 )->rect().right();
1139
1140     int bw = 0;
1141     if ( QABS( x2 - x1 ) > width() )
1142 #if defined QT_VERSION && QT_VERSION >= 0x30300
1143       bw = 2 * style().pixelMetric( QStyle::PM_TabBarScrollButtonWidth, this );
1144 #else
1145       bw = 2 * 16;
1146 #endif
1147
1148     int limit = width() - bw;
1149     r.setRight( QMIN( r.right(), limit ) );
1150
1151     r = QRect( mapToGlobal( r.topLeft() ), r.size() );
1152   }
1153   return r;
1154 }
1155
1156 void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
1157 {
1158   if ( myId != -1 && !tab( myId )->rect().contains( e->pos() ) )
1159   {
1160     myId = -1;
1161     emit dragActiveTab();
1162   }
1163
1164   QTabBar::mouseMoveEvent( e );
1165 }
1166
1167 void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
1168 {
1169   QTabBar::mousePressEvent( e );
1170
1171   if ( e->button() == LeftButton )
1172     myId = currentTab();
1173 }
1174
1175 void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
1176 {
1177   QTabBar::mouseReleaseEvent( e );
1178
1179   myId = -1;
1180
1181   if ( e->button() == RightButton )
1182     emit contextMenuRequested( e->globalPos() );
1183 }
1184
1185 void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
1186 {
1187   if ( e->reason() != QContextMenuEvent::Mouse )
1188     emit contextMenuRequested( e->globalPos() );
1189 }
1190
1191 void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
1192 {
1193   if ( currentTab() != t->identifier() )
1194   {
1195     QFont fnt = p->font();
1196     fnt.setUnderline( false );
1197     p->setFont( fnt );
1198   }
1199   QTabBar::paintLabel( p, br, t, has_focus );
1200 }
1201
1202 /*!
1203     Class: QtxWorkstackDrag [Internal]
1204     Descr:
1205 */
1206
1207 QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
1208 : QObject( 0 ),
1209 myWS( ws ),
1210 myTab( -1 ),
1211 myArea( 0 ),
1212 myPainter( 0 ),
1213 myChild( child )
1214 {
1215   qApp->installEventFilter( this );
1216 }
1217
1218 QtxWorkstackDrag::~QtxWorkstackDrag()
1219 {
1220   qApp->removeEventFilter( this );
1221
1222   endDrawRect();
1223 }
1224
1225 bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
1226 {
1227   switch ( e->type() )
1228   {
1229   case QEvent::MouseMove:
1230     updateTarget( ((QMouseEvent*)e)->globalPos() );
1231     break;
1232   case QEvent::MouseButtonRelease:
1233     drawRect();
1234     endDrawRect();
1235     dropWidget();
1236     deleteLater();
1237     break;
1238   default:
1239     return false;
1240   }
1241   return true;
1242 }
1243
1244 void QtxWorkstackDrag::updateTarget( const QPoint& p )
1245 {
1246   int tab = -1;
1247   QtxWorkstackArea* area = detectTarget( p, tab );
1248   setTarget( area, tab );
1249 }
1250
1251 QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
1252 {
1253   if ( p.isNull() )
1254     return 0;
1255
1256   QtxWorkstackArea* area = myWS->areaAt( p );
1257   if ( area )
1258     tab = area->tabAt( p );
1259   return area;
1260 }
1261
1262 void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
1263 {
1264   if ( !area || ( myArea == area && tab == myTab ) )
1265     return;
1266
1267   startDrawRect();
1268
1269   if ( myArea )
1270     drawRect();
1271
1272   myTab = tab;
1273   myArea = area;
1274
1275   if ( myArea )
1276     drawRect();
1277 }
1278
1279 void QtxWorkstackDrag::dropWidget()
1280 {
1281   if ( myArea )
1282     myArea->insertWidget( myChild->widget(), myTab );
1283 }
1284
1285 void QtxWorkstackDrag::drawRect()
1286 {
1287   if ( !myPainter || !myArea )
1288     return;
1289
1290   QRect r = myArea->floatRect();
1291   int m = myPainter->pen().width();
1292
1293   r.setTop( r.top() + m + 2 );
1294   r.setLeft( r.left() + m + 2 );
1295   r.setRight( r.right() - m - 2 );
1296   r.setBottom( r.bottom() - m - 2 );
1297
1298   myPainter->drawRect( r );
1299
1300   QRect tr = myArea->floatTab( myTab );
1301   tr.setTop( tr.top() + m );
1302   tr.setLeft( tr.left() + m );
1303   tr.setRight( tr.right() - m );
1304   tr.setBottom( tr.bottom() - m );
1305
1306   myPainter->drawRect( tr );
1307 }
1308
1309 void QtxWorkstackDrag::endDrawRect()
1310 {
1311   delete myPainter;
1312   myPainter = 0;
1313 }
1314
1315 void QtxWorkstackDrag::startDrawRect()
1316 {
1317   if ( myPainter )
1318     return;
1319
1320   int scr = QApplication::desktop()->screenNumber( (QWidget*)this );
1321   QWidget* paint_on = QApplication::desktop()->screen( scr );
1322
1323   myPainter = new QPainter( paint_on, true );
1324   myPainter->setPen( QPen( gray, 3 ) );
1325   myPainter->setRasterOp( XorROP );
1326 }