Salome HOME
Add methods for view parameters management from program code
[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 #include <stream.h>
19
20 /*!
21     Class: QtxWorkstack [Public]
22     Descr:
23 */
24
25 QtxWorkstack::QtxWorkstack( QWidget* parent )
26 : QWidget( parent ),
27 myWin( 0 ),
28 myArea( 0 )
29 {
30   QVBoxLayout* base = new QVBoxLayout( this );
31   mySplit = new QSplitter( this );
32   mySplit->setChildrenCollapsible( false );
33   base->addWidget( mySplit );
34 }
35
36 QtxWorkstack::~QtxWorkstack()
37 {
38 }
39
40 QWidgetList QtxWorkstack::windowList() const
41 {
42   QPtrList<QtxWorkstackArea> lst;
43   areas( mySplit, lst, true );
44
45   QWidgetList widList;
46   for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current(); ++it )
47   {
48     QWidgetList wids = it.current()->widgetList();
49     for ( QWidgetListIt itr( wids ); itr.current(); ++itr )
50       widList.append( itr.current() );
51   }
52
53   return widList;
54 }
55
56 QWidgetList QtxWorkstack::splitWindowList() const
57 {
58   return myArea ? myArea->widgetList() : QWidgetList();
59 }
60
61 QWidget* QtxWorkstack::activeWindow() const
62 {
63   return myWin;
64 }
65
66 void QtxWorkstack::split( const int o )
67 {
68   QtxWorkstackArea* area = activeArea();
69   if ( !area )
70     return;
71
72   if ( area->widgetList().count() < 2 )
73     return;
74
75   QWidget* curWid = area->activeWidget();
76   if ( !curWid )
77     return;
78
79   QSplitter* s = splitter( area );
80   QPtrList<QtxWorkstackArea> areaList;
81   areas( s, areaList );
82
83   QPtrList<QSplitter> splitList;
84   splitters( s, splitList );
85
86   QSplitter* trg = 0;
87   if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
88     trg = s;
89
90   if ( !trg )
91     trg = wrapSplitter( area );
92
93   if ( !trg )
94     return;
95
96   trg->setOrientation( (Orientation)o );
97
98   QtxWorkstackArea* newArea = createArea( 0 );
99   insertWidget( newArea, trg, area );
100
101   area->removeWidget( curWid );
102   newArea->insertWidget( curWid );
103
104   distributeSpace( trg );
105
106   curWid->show();
107   curWid->setFocus();
108 }
109
110 // begin: jfa 06.07.2005
111 void QtxWorkstack::Split (QWidget* wid, const Qt::Orientation o, const SplitType type)
112 {
113   if (!wid) return;
114
115   // find area of the given widget
116   QtxWorkstackArea* area = NULL;
117   QPtrList<QtxWorkstackArea> allAreas;
118   areas(mySplit, allAreas, true);
119
120   QPtrListIterator<QtxWorkstackArea> it (allAreas);
121   for (; it.current() && !area; ++it) {
122     if (it.current()->contains(wid))
123       area = it.current();
124   }
125   if (!area) return;
126
127   QWidgetList wids = area->widgetList();
128   if (wids.count() < 2)
129     return;
130
131   QSplitter* s = splitter(area);
132   QPtrList<QtxWorkstackArea> areaList;
133   areas(s, areaList);
134
135   QPtrList<QSplitter> splitList;
136   splitters(s, splitList);
137
138   QSplitter* trg = 0;
139   if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
140     trg = s;
141
142   if (!trg) trg = wrapSplitter(area);
143   if (!trg) return;
144
145   trg->setOrientation(o);
146
147   QtxWorkstackArea* newArea = createArea(0);
148   insertWidget(newArea, trg, area);
149
150   switch (type) {
151   case SPLIT_STAY:
152     {
153       QWidgetListIt itr (wids);
154       for (; itr.current(); ++itr)
155       {
156         QWidget* wid_i = itr.current();
157         if (wid_i != wid) {
158           area->removeWidget(wid_i);
159           newArea->insertWidget(wid_i);
160         }
161       }
162     }
163     break;
164   case SPLIT_AT:
165     {
166       QWidgetListIt itr (wids);
167       for (; itr.current() && itr.current() != wid; ++itr) {
168       }
169       for (; itr.current(); ++itr) {
170         area->removeWidget(itr.current());
171         newArea->insertWidget(itr.current());
172       }
173     }
174     break;
175   case SPLIT_MOVE:
176     area->removeWidget(wid);
177     newArea->insertWidget(wid);
178     break;
179   }
180
181   distributeSpace(trg);
182 }
183
184 void QtxWorkstack::OnTop (QWidget* wid)
185 {
186   if (!wid) return;
187
188   // find area of the given widget
189   QtxWorkstackArea* area = NULL;
190   QPtrList<QtxWorkstackArea> allAreas;
191   areas(mySplit, allAreas, true);
192   QPtrListIterator<QtxWorkstackArea> it (allAreas);
193   for (; it.current() && !area; ++it) {
194     if (it.current()->contains(wid))
195       area = it.current();
196   }
197   if (!area) return;
198
199   area->setActiveWidget(wid);
200 }
201
202 void QtxWorkstack::Attract (QWidget* wid1, QWidget* wid2, const bool all)
203 {
204   if (!wid1 || !wid2) return;
205
206   // find area of the widgets
207   QtxWorkstackArea *area1 = NULL, *area2 = NULL;
208   QPtrList<QtxWorkstackArea> allAreas;
209   areas(mySplit, allAreas, true);
210   QPtrListIterator<QtxWorkstackArea> it (allAreas);
211   for (; it.current() && !(area1 && area2); ++it) {
212     if (it.current()->contains(wid1))
213       area1 = it.current();
214     if (it.current()->contains(wid2))
215       area2 = it.current();
216   }
217   if (!area1 || !area2) return;
218
219   QWidget* curWid = area1->activeWidget();
220   if (!curWid) return;
221
222   if (area1 == area2) {
223     if (all) {
224       // Set wid1 at first position, wid2 at second
225       area1->insertWidget(wid1);
226       area1->insertWidget(wid2, 1);
227     } else {
228       // Set wid2 right after wid1
229       area1->removeWidget(wid2);
230       int wid1_ind = 0;
231       QWidgetList wids1 = area1->widgetList();
232       QWidgetListIt itr1 (wids1);
233       for (; itr1.current() && itr1.current() != wid1; ++itr1, ++wid1_ind);
234       area1->insertWidget(wid2, wid1_ind + 1);
235     }
236   } else {
237     int wid1_ind = 0;
238     QWidgetList wids1 = area1->widgetList();
239     QWidgetListIt itr1 (wids1);
240     for (; itr1.current() && itr1.current() != wid1; ++itr1, ++wid1_ind);
241
242     if (all) {
243       // Set wid2 right after wid1, other widgets from area2 right after wid2
244       QWidgetList wids2 = area2->widgetList();
245       QWidgetListIt itr2 (wids2);
246       for (int ind = wid1_ind + 1; itr2.current(); ++itr2, ++ind)
247       {
248         area2->removeWidget(itr2.current());
249         if (itr2.current() == wid2) {
250           area1->insertWidget(itr2.current(), wid1_ind + 1);
251         } else {
252           area1->insertWidget(itr2.current(), ind);
253         }
254       }
255     } else {
256       // Set wid2 right after wid1
257       area2->removeWidget(wid2);
258       area1->insertWidget(wid2, wid1_ind + 1);
259     }
260   }
261
262   /*area2->removeWidget(wid2);
263   area1->insertWidget(wid2);
264
265   // Reorder widgets
266   QWidgetListIt itr( wids1 );
267   for ( ; itr.current() && itr.current() != wid1; ++itr )
268   {
269   }
270   ++itr;
271   for ( ; itr.current(); ++itr )
272   {
273     if (itr.current() != wid2) {
274       area1->removeWidget( itr.current() );
275       area1->insertWidget( itr.current() );
276     }
277   }*/
278
279   area1->setActiveWidget(curWid);
280 }
281
282 static void setSizes (QIntList& szList, const int item_ind,
283                       const int new_near, const int new_this, const int new_farr)
284 {
285   // set size to all items before an item # <item_ind>
286   int cur_pos = 0;
287   QIntList::iterator its = szList.begin();
288   for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
289     *its = new_near;
290   }
291   if (its == szList.end()) return;
292   // set size to item # <item_ind>
293   *its = new_this;
294   ++its;
295   // set size to all items after an item # <item_ind>
296   for (; its != szList.end(); ++its) {
297     *its = new_farr;
298   }
299 }
300
301 void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
302 {
303   if ( position < 0.0 || 1.0 < position)
304     return;
305   if ( !wid )
306     return;
307
308   // find area of the given widget
309   QtxWorkstackArea* area = NULL;
310   QPtrList<QtxWorkstackArea> allAreas;
311   areas(mySplit, allAreas, true);
312   for ( QPtrListIterator<QtxWorkstackArea> it( allAreas );
313        it.current() && !area;
314        ++it )
315   {
316     if (it.current()->contains(wid))
317       area = it.current();
318   }
319
320   if ( !area )
321     return;
322
323   QSplitter* split = splitter( area );
324   if ( !split )
325     return;
326
327   // find index of the area in its splitter
328   int item_ind = -1;
329   bool isFound = false;
330   const QObjectList* was = split->children();
331   for (QObjectListIt ito (*was); ito.current() && !isFound; ++ito, ++item_ind)
332   {
333     if (ito.current() == area)
334       isFound = true;
335   }
336   if (!isFound || item_ind == 0)
337     return;
338
339   QIntList szList = split->sizes();
340   int splitter_size = (split->orientation() == Horizontal ?
341                        split->width() : split->height());
342   int nb = szList.count();
343
344   int new_prev = int(splitter_size * position / item_ind);
345   int new_next  = int(splitter_size * (1.0 - position) / (nb - item_ind));
346   setSizes (szList, item_ind, new_prev, new_next, new_next);
347   split->setSizes(szList);
348 }
349
350 void QtxWorkstack::SetRelativePosition (QWidget* wid,
351                                         const Qt::Orientation o,
352                                         const double position)
353 {
354   if (position < 0.0 || 1.0 < position)
355     return;
356   if (!wid)
357     return;
358
359   int splitter_size = (o == Horizontal ? mySplit->width() : mySplit->height());
360   int need_pos = int(position * splitter_size);
361   int splitter_pos = 0;
362
363   if (setPosition(wid, mySplit, o, need_pos, splitter_pos) != 0) {
364     // impossible to set required position
365   }
366 }
367
368 static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
369                            const int item_ind, const int item_rel_pos,
370                            const int need_pos, const int splitter_pos)
371 {
372   if (item_ind == 0) { // cannot move in this splitter
373     return (need_pos - splitter_pos);
374   }
375
376   int delta = 0;
377   int new_prev = 0;
378   int new_this = szList[item_ind];
379   int new_next = 0;
380
381   bool isToCheck = false;
382
383   if (need_pos < splitter_pos) {
384     // Set size of all previous workareas to zero <--
385     if (item_ind == nb - 1) {
386       // item iz last in the splitter, it will occupy all the splitter
387       new_this = splitter_size;
388     } else {
389       // recompute size of next items in splitter
390       new_next = (splitter_size - new_this) / (nb - item_ind - 1);
391     }
392     delta = need_pos - splitter_pos;
393
394   } else if (need_pos > (splitter_pos + splitter_size)) {
395     // Set size of all next workareas to zero -->
396     // recompute size of previous items in splitter
397     new_this = 0;
398     new_prev = (splitter_size - new_this) / item_ind;
399     delta = need_pos - (splitter_pos + splitter_size - new_this);
400
401   } else { // required position lays inside this splitter
402     // Move workarea inside splitter into required position <->
403     int new_item_rel_pos = need_pos - splitter_pos;
404     new_prev = new_item_rel_pos / item_ind;
405     if (need_pos < (splitter_pos + item_rel_pos)) {
406       // Make previous workareas smaller, next - bigger
407       // No problem to keep old size of the widget
408     } else {
409       // Make previous workareas bigger, next - smaller
410       if (new_this > splitter_size - new_item_rel_pos) {
411         new_this = splitter_size - new_item_rel_pos;
412       }
413       // jfa to do: in this case fixed size of next widgets could prevent right resizing
414       isToCheck = true;
415     }
416     if (item_ind == nb - 1) {
417       new_this = splitter_size - new_item_rel_pos;
418     } else {
419       new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
420     }
421     delta = 0;
422   }
423
424   setSizes (szList, item_ind, new_prev, new_this, new_next);
425   return delta;
426 }
427
428 int QtxWorkstack::setPosition (QWidget* wid, QSplitter* split,
429                                const Qt::Orientation o, const int need_pos,
430                                const int splitter_pos)
431 {
432   if (!wid || !split)
433     return need_pos - splitter_pos;
434
435   // Find corresponding sub-splitter.
436   // Find also index of appropriate item in current splitter.
437   int cur_ind = 0, item_ind = 0;
438   bool isBottom = false, isFound = false;
439   QSplitter* sub_split = NULL;
440   const QObjectList* objs = split->children();
441   if (objs)
442   {
443     for (QObjectListIt it (*objs); it.current() && !isFound; ++it)
444     {
445       if (it.current()->inherits( "QtxWorkstackArea")) {
446         if (((QtxWorkstackArea*)it.current())->contains(wid)) {
447           item_ind = cur_ind;
448           isBottom = true;
449           isFound = true;
450         }
451         cur_ind++;
452       } else if (it.current()->inherits("QSplitter")) {
453         QPtrList<QtxWorkstackArea> areaList;
454         areas((QSplitter*)it.current(), areaList, true);
455         for (QPtrListIterator<QtxWorkstackArea> ita (areaList);
456              ita.current() && !isFound;
457              ++ita)
458         {
459           if (ita.current()->contains(wid)) {
460             item_ind = cur_ind;
461             isFound = true;
462             sub_split = (QSplitter*)it.current();
463           }
464         }
465         cur_ind++;
466       }
467     }
468   }
469   if (!isFound)
470     return (need_pos - splitter_pos);
471
472   if (split->orientation() == o) {
473     // Find coordinates of near and far sides of the appropriate item relatively current splitter
474     int splitter_size = (o == Horizontal ? split->width() : split->height());
475     QIntList szList = split->sizes();
476     int nb = szList.count();
477     int item_rel_pos = 0; // position of near side of item relatively this splitter
478     for (int i = 0; i < item_ind; i++) {
479       item_rel_pos += szList[i];
480     }
481     int item_size = szList[item_ind]; // size of item
482     int item_pos = splitter_pos + item_rel_pos;
483
484     // Resize splitter items to complete the conditions
485     if (isBottom) {
486       // I. Bottom of splitters stack reached
487
488       int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
489       split->setSizes(szList);
490       // Recompute delta, as some windows can reject given size
491       int new_item_rel_pos = 0;
492       QIntList szList1 = split->sizes();
493       for (int i = 0; i < item_ind; i++) {
494         new_item_rel_pos += szList1[i];
495       }
496       delta = need_pos - (splitter_pos + new_item_rel_pos);
497       return delta;
498
499     } else {
500       // II. Bottom of splitters stack is not yet reached
501
502       if (item_ind == 0) { // cannot move in this splitter
503         // Process in sub-splitter
504         return setPosition(wid, sub_split, o, need_pos, splitter_pos);
505       }
506
507       int new_prev = 0;
508       int new_this = szList[item_ind];
509       int new_next = 0;
510
511       if (need_pos < splitter_pos) {
512         // Set size of all previous workareas to zero <--
513         if (item_ind == nb - 1) {
514           new_this = splitter_size;
515         } else {
516           new_next = (splitter_size - new_this) / (nb - item_ind - 1);
517         }
518         setSizes (szList, item_ind, new_prev, new_this, new_next);
519         split->setSizes(szList);
520         // Recompute splitter_pos, as some windows can reject given size
521         int new_item_rel_pos = 0;
522         QIntList szList1 = split->sizes();
523         for (int i = 0; i < item_ind; i++) {
524           new_item_rel_pos += szList1[i];
525         }
526         // Process in sub-splitter
527         return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
528       } else if (need_pos > (splitter_pos + splitter_size)) {
529         // Set size of all next workareas to zero -->
530         new_prev = (splitter_size - new_this) / item_ind;
531         setSizes (szList, item_ind, new_prev, new_this, new_next);
532         split->setSizes(szList);
533         // Recompute splitter_pos, as some windows can reject given size
534         int new_item_rel_pos = 0;
535         QIntList szList1 = split->sizes();
536         for (int i = 0; i < item_ind; i++) {
537           new_item_rel_pos += szList1[i];
538         }
539         // Process in sub-splitter
540         return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
541       } else {
542         // Set appropriate size of all previous/next items <->
543         int new_item_rel_pos = item_rel_pos;
544         if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
545           // Move item inside splitter into required position <->
546           int new_this = szList[item_ind];
547           int new_next = 0;
548           new_item_rel_pos = need_pos - splitter_pos;
549           if ((item_pos + item_size) < need_pos) {
550             //new_item_rel_pos = need_pos - (item_pos + item_size);
551             new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
552           }
553           int new_prev = new_item_rel_pos / item_ind;
554           if (need_pos < (splitter_pos + item_rel_pos)) {
555             // Make previous workareas smaller, next - bigger
556             // No problem to keep old size of the widget
557           } else {
558             // Make previous workareas bigger, next - smaller
559             if (new_this > splitter_size - new_item_rel_pos) {
560               new_this = splitter_size - new_item_rel_pos;
561             }
562           }
563           if (item_ind == nb - 1) {
564             new_this = splitter_size - new_item_rel_pos;
565           } else {
566             new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
567           }
568           setSizes (szList, item_ind, new_prev, new_this, new_next);
569           split->setSizes(szList);
570           // Recompute new_item_rel_pos, as some windows can reject given size
571           new_item_rel_pos = 0;
572           QIntList szList1 = split->sizes();
573           for (int i = 0; i < item_ind; i++) {
574             new_item_rel_pos += szList1[i];
575           }
576         } else {
577           // Do nothing
578         }
579         // Process in sub-splitter
580         int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
581         if (add_pos == 0)
582           return 0;
583
584         // this can be if corresponding workarea is first in sub-splitter
585         // or sub-splitter has another orientation
586
587         // Resize ones again to reach precize position <->
588         int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
589
590         // Move workarea inside splitter into required position <->
591         int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
592                                      new_item_rel_pos, need_pos_1, splitter_pos);
593         split->setSizes(szList);
594         // Recompute new_item_rel_pos, as some windows can reject given size
595         new_item_rel_pos = 0;
596         QIntList szList1 = split->sizes();
597         for (int i = 0; i < item_ind; i++) {
598           new_item_rel_pos += szList1[i];
599         }
600         delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
601         return delta_1;
602       }
603     }
604   } else {
605     return setPosition(wid, sub_split, o, need_pos, splitter_pos);
606   }
607
608   return 0;
609 }
610 // end: jfa 06.07.2005
611
612 void QtxWorkstack::distributeSpace( QSplitter* split ) const
613 {
614   if ( !split )
615     return;
616
617   QIntList szList = split->sizes();
618   int size = ( split->orientation() == Horizontal ?
619                split->width() : split->height() ) / szList.count();
620   for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
621     *it = size;
622   split->setSizes( szList );
623 }
624
625 void QtxWorkstack::splitVertical()
626 {
627   split( Qt::Vertical );
628 }
629
630 void QtxWorkstack::splitHorizontal()
631 {
632   split( Qt::Horizontal );
633 }
634
635 QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
636 {
637   if ( !area )
638     return 0;
639
640   QSplitter* pSplit = splitter( area );
641   if ( !pSplit )
642     return 0;
643
644   bool upd = pSplit->isUpdatesEnabled();
645   pSplit->setUpdatesEnabled( false );
646
647   QIntList szList = pSplit->sizes();
648
649   QSplitter* wrap = new QSplitter( 0 );
650 #if defined QT_VERSION && QT_VERSION >= 0x30200
651   wrap->setChildrenCollapsible( false );
652 #endif
653   insertWidget( wrap, pSplit, area );
654   area->reparent( wrap, QPoint( 0, 0 ), true );
655
656   pSplit->setSizes( szList );
657
658   pSplit->setUpdatesEnabled( upd );
659
660   return wrap;
661 }
662
663 void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
664 {
665   if ( !wid || !pWid )
666     return;
667
668   QWidgetList moveList;
669   const QObjectList* lst = pWid->children();
670   if ( lst )
671   {
672     bool found = false;
673     for ( QObjectListIt it( *lst ); it.current(); ++it )
674     {
675       if ( found && ( it.current()->inherits( "QSplitter" ) ||
676                       it.current()->inherits( "QtxWorkstackArea" ) ) )
677         moveList.append( (QWidget*)it.current() );
678       if ( it.current() == after )
679         found = true;
680     }
681   }
682
683   QMap<QWidget*, bool> map;
684   for ( QWidgetListIt it( moveList ); it.current(); ++it )
685   {
686     map.insert( it.current(), it.current()->isVisibleTo( it.current()->parentWidget() ) );
687     it.current()->reparent( 0, QPoint( 0, 0 ), false );
688   }
689
690   wid->reparent( pWid, QPoint( 0, 0 ), true );
691
692   for ( QWidgetListIt itr( moveList ); itr.current(); ++itr )
693     itr.current()->reparent( pWid, QPoint( 0, 0 ), map.contains( itr.current() ) ? map[itr.current()] : false );
694 }
695
696 void QtxWorkstack::onPopupActivated( int id )
697 {
698   switch ( id )
699   {
700   case SplitVertical:
701     splitVertical();
702     break;
703   case SplitHorizontal:
704     splitHorizontal();
705     break;
706   case Close:
707     if ( activeWindow() )
708       activeWindow()->close();
709     break;
710   }
711 }
712
713 void QtxWorkstack::onDestroyed( QObject* obj )
714 {
715   QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
716
717   if ( area == myArea )
718     myArea = 0;
719
720   if ( !myArea )
721   {
722     QtxWorkstackArea* cur = neighbourArea( area );
723     if ( cur )
724       cur->setFocus();
725   }
726
727   QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
728 }
729
730 void QtxWorkstack::onWindowActivated( QWidget* wid )
731 {
732   const QObject* obj = sender();
733   if ( !obj->inherits( "QtxWorkstackArea" ) )
734     return;
735
736   setActiveArea( (QtxWorkstackArea*)obj );
737 }
738
739 void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
740 {
741   if ( myArea != area )
742     return;
743
744   QPtrList<QtxWorkstackArea> lst;
745   areas( mySplit, lst, true );
746
747   int idx = lst.find( area );
748   if ( idx == -1 )
749     return;
750
751   myWin = 0;
752   myArea = 0;
753
754   QtxWorkstackArea* newArea = neighbourArea( area );
755   if ( newArea && newArea->activeWidget() )
756     newArea->activeWidget()->setFocus();
757
758   QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
759 }
760
761 void QtxWorkstack::onContextMenuRequested( QPoint p )
762 {
763   if ( !activeArea() )
764     return;
765
766   QWidgetList lst = activeArea()->widgetList();
767   if ( lst.isEmpty() )
768     return;
769
770   QPopupMenu* pm = new QPopupMenu();
771   connect( pm, SIGNAL( activated( int ) ), this, SLOT( onPopupActivated( int ) ) );
772
773   if ( lst.count() > 1 )
774   {
775     pm->insertItem( tr( "Split vertically" ), SplitVertical );
776     pm->insertItem( tr( "Split horizontally" ), SplitHorizontal );
777     pm->insertSeparator();
778   }
779   pm->insertItem( tr( "Close" ), Close );
780
781   pm->exec( p );
782
783   delete pm;
784 }
785
786 void QtxWorkstack::childEvent( QChildEvent* e )
787 {
788   if ( e->inserted() && e->child()->isWidgetType() )
789   {
790           QWidget* w = (QWidget*)e->child();
791           if ( w && w != mySplit )
792     {
793       targetArea()->insertWidget( w );
794       return;
795     }
796   }
797   QWidget::childEvent( e );
798 }
799
800 void QtxWorkstack::customEvent( QCustomEvent* e )
801 {
802   updateState();
803 }
804
805 QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
806 {
807   if ( !area )
808     return 0;
809
810   QSplitter* split = 0;
811
812   QWidget* wid = area->parentWidget();
813   if ( wid && wid->inherits( "QSplitter" ) )
814     split = (QSplitter*)wid;
815
816   return split;
817 }
818
819 void QtxWorkstack::splitters( QSplitter* split, QPtrList<QSplitter>& splitList, const bool rec ) const
820 {
821   if ( !split )
822     return;
823
824   const QObjectList* objs = split->children();
825   if ( objs )
826   {
827     for ( QObjectListIt it( *objs ); it.current(); ++it )
828     {
829       if ( rec )
830         splitters( (QSplitter*)it.current(), splitList, rec );
831       if ( it.current()->inherits( "QSplitter" ) )
832         splitList.append( (QSplitter*)it.current() );
833     }
834   }
835 }
836
837 void QtxWorkstack::areas( QSplitter* split, QPtrList<QtxWorkstackArea>& areaList, const bool rec ) const
838 {
839   if ( !split )
840     return;
841
842   const QObjectList* objs = split->children();
843   if ( objs )
844   {
845     for ( QObjectListIt it( *objs ); it.current(); ++it )
846     {
847       if ( it.current()->inherits( "QtxWorkstackArea" ) )
848         areaList.append( (QtxWorkstackArea*)it.current() );
849       else if ( rec && it.current()->inherits( "QSplitter" ) )
850         areas( (QSplitter*)it.current(), areaList, rec );
851     }
852   }
853 }
854
855 QtxWorkstackArea* QtxWorkstack::activeArea() const
856 {
857   return myArea;
858 }
859
860 QtxWorkstackArea* QtxWorkstack::targetArea()
861 {
862   QtxWorkstackArea* area = activeArea();
863   if ( !area )
864     area = currentArea();
865   if ( !area )
866   {
867     QPtrList<QtxWorkstackArea> lst;
868     areas( mySplit, lst );
869     if ( !lst.isEmpty() )
870       area = lst.first();
871   }
872
873   if ( !area )
874     area = createArea( mySplit );
875
876   return area;
877 }
878
879 QtxWorkstackArea* QtxWorkstack::currentArea() const
880 {
881   QtxWorkstackArea* area = 0;
882   QWidget* wid = focusWidget();
883   while ( wid && !area )
884   {
885     if ( wid->inherits( "QtxWorkstackArea" ) )
886       area = (QtxWorkstackArea*)wid;
887     wid = wid->parentWidget();
888   }
889
890   return area;
891 }
892
893 QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
894 {
895   QtxWorkstackArea* area = new QtxWorkstackArea( parent );
896
897   connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
898   connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
899   connect( area, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
900   connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
901
902   return area;
903 }
904
905 void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
906 {
907   QWidget* oldCur = myWin;
908
909   QtxWorkstackArea* oldArea = myArea;
910
911   myArea = area;
912
913   if ( myArea != oldArea )
914   {
915     if ( oldArea )
916       oldArea->updateActiveState();
917     if ( myArea )
918       myArea->updateActiveState();
919   }
920
921   if ( myArea )
922     myWin = myArea->activeWidget();
923
924   if ( myWin && oldCur != myWin )
925     emit windowActivated( myWin );
926 }
927
928 QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
929 {
930   QPtrList<QtxWorkstackArea> lst;
931   areas( mySplit, lst, true );
932   int pos = lst.find( area );
933   if ( pos < 0 )
934     return 0;
935
936   QtxWorkstackArea* na = 0;
937   for ( int i = pos - 1; i >= 0 && !na; i-- )
938   {
939     if ( !lst.at( i )->isEmpty() )
940       na = lst.at( i );
941   }
942
943   for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
944   {
945     if ( !lst.at( j )->isEmpty() )
946         na = lst.at( j );
947   }
948   return na;
949 }
950
951 QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
952 {
953   QtxWorkstackArea* area = 0;
954   QPtrList<QtxWorkstackArea> lst;
955   areas( mySplit, lst, true );
956   for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current() && !area; ++it )
957   {
958     QtxWorkstackArea* cur = it.current();
959     QRect r = cur->geometry();
960     if ( cur->parentWidget() )
961       r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
962     if ( r.contains( p ) )
963       area = cur;
964   }
965   return area;
966 }
967
968 void QtxWorkstack::updateState()
969 {
970   updateState( mySplit );
971 }
972
973 void QtxWorkstack::updateState( QSplitter* split )
974 {
975   QPtrList<QSplitter> recList;
976   splitters( split, recList, false );
977   for ( QPtrListIterator<QSplitter> itr( recList ); itr.current(); ++itr )
978     updateState( itr.current() );
979
980   QPtrList<QSplitter> splitList;
981   splitters( split, splitList, false );
982
983   QPtrList<QtxWorkstackArea> areaList;
984   areas( split, areaList, false );
985
986   bool vis = false;
987   for ( QPtrListIterator<QtxWorkstackArea> it( areaList ); it.current(); ++it )
988   {
989     if ( it.current()->isEmpty() )
990       it.current()->hide();
991     else
992     {
993       it.current()->show();
994       vis = true;
995     }
996   }
997
998   if ( split == mySplit )
999     return;
1000
1001   for ( QPtrListIterator<QSplitter> iter( splitList ); iter.current() && !vis; ++iter )
1002     vis = iter.current()->isVisibleTo( iter.current()->parentWidget() );
1003
1004   if ( areaList.isEmpty() && splitList.isEmpty() )
1005     delete split;
1006   else if ( vis )
1007     split->show();
1008   else
1009     split->hide();
1010 }
1011
1012 /*!
1013     Class: QtxWorkstackArea [Internal]
1014     Descr:
1015 */
1016
1017 QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
1018 : QWidget( parent )
1019 {
1020   QVBoxLayout* base = new QVBoxLayout( this );
1021
1022   QHBox* top = new QHBox( this );
1023   base->addWidget( top );
1024
1025   myBar = new QtxWorkstackTabBar( top );
1026
1027   QPushButton* close = new QPushButton( top );
1028   close->setPixmap( style().stylePixmap( QStyle::SP_TitleBarCloseButton ) );
1029   close->setAutoDefault( true );
1030   close->setFlat( true );
1031   myClose = close;
1032
1033   top->setStretchFactor( myBar, 1 );
1034
1035   myStack = new QWidgetStack( this );
1036
1037   base->addWidget( myStack, 1 );
1038
1039   connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
1040   connect( myBar, SIGNAL( selected( int ) ), this, SLOT( onSelected( int ) ) );
1041   connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
1042   connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SIGNAL( contextMenuRequested( QPoint ) ) );
1043
1044   updateState();
1045
1046   updateActiveState();
1047
1048   qApp->installEventFilter( this );
1049 }
1050
1051 QtxWorkstackArea::~QtxWorkstackArea()
1052 {
1053   qApp->removeEventFilter( this );
1054 }
1055
1056 bool QtxWorkstackArea::isEmpty() const
1057 {
1058   bool res = false;
1059   for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !res; ++it )
1060     res = it.data().vis;
1061   return !res;
1062 }
1063
1064 void QtxWorkstackArea::insertWidget( QWidget* wid, const int idx )
1065 {
1066   if ( !wid )
1067     return;
1068
1069   int pos = myList.find( wid );
1070   if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
1071     return;
1072
1073   myList.removeRef( wid );
1074   pos = idx < 0 ? myList.count() : idx;
1075   myList.insert( QMIN( pos, (int)myList.count() ), wid );
1076   if ( !myInfo.contains( wid ) )
1077   {
1078     QtxWorkstackChild* child = new QtxWorkstackChild( wid, myStack );
1079     myChild.insert( wid, child );
1080     myInfo.insert( wid, WidgetInfo() );
1081     myInfo[wid].id = generateId();
1082     myInfo[wid].vis = wid->isVisibleTo( wid->parentWidget() );
1083
1084     connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
1085     connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
1086     connect( child, SIGNAL( hided( QtxWorkstackChild* ) ), this, SLOT( onChildHided( QtxWorkstackChild* ) ) );
1087     connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
1088     connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
1089   }
1090
1091   updateState();
1092
1093   setWidgetActive( wid );
1094 }
1095
1096 void QtxWorkstackArea::removeWidget( QWidget* wid )
1097 {
1098   if ( !myList.contains( wid ) )
1099     return;
1100
1101   if ( myBar->tab( widgetId( wid ) ) )
1102     myBar->removeTab( myBar->tab( widgetId( wid ) ) );
1103   myStack->removeWidget( child( wid ) );
1104
1105   myList.remove( wid );
1106   myInfo.remove( wid );
1107   myChild.remove( wid );
1108
1109   delete child( wid );
1110
1111   if ( myList.isEmpty() )
1112     delete this;
1113   else
1114     updateState();
1115 }
1116
1117 QWidgetList QtxWorkstackArea::widgetList() const
1118 {
1119   QWidgetList lst;
1120   for ( QWidgetListIt it( myList ); it.current(); ++it )
1121   {
1122     if ( widgetVisibility( it.current() ) )
1123       lst.append( it.current() );
1124   }
1125   return lst;
1126 }
1127
1128 QWidget* QtxWorkstackArea::activeWidget() const
1129 {
1130   return widget( myBar->currentTab() );
1131 }
1132
1133 void QtxWorkstackArea::setActiveWidget( QWidget* wid )
1134 {
1135   myBar->setCurrentTab( widgetId( wid ) );
1136 }
1137
1138 bool QtxWorkstackArea::contains( QWidget* wid ) const
1139 {
1140   return myList.contains( wid );
1141 }
1142
1143 void QtxWorkstackArea::show()
1144 {
1145   QMap<QWidget*, bool> map;
1146   for ( QWidgetListIt it( myList ); it.current(); ++it )
1147   {
1148     map.insert( it.current(), isBlocked( it.current() ) );
1149     setBlocked( it.current(), true );
1150   }
1151
1152   QWidget::show();
1153
1154   for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1155     setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1156 }
1157
1158 void QtxWorkstackArea::hide()
1159 {
1160   QMap<QWidget*, bool> map;
1161   for ( QWidgetListIt it( myList ); it.current(); ++it )
1162   {
1163     map.insert( it.current(), isBlocked( it.current() ) );
1164     setBlocked( it.current(), true );
1165   }
1166
1167   QWidget::hide();
1168
1169   for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1170     setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1171 }
1172
1173 bool QtxWorkstackArea::isActive() const
1174 {
1175   QtxWorkstack* ws = workstack();
1176   if ( !ws )
1177     return false;
1178
1179   return ws->activeArea() == this;
1180 }
1181
1182 void QtxWorkstackArea::updateActiveState()
1183 {
1184   myBar->setActive( isActive() );
1185 }
1186
1187 QtxWorkstack* QtxWorkstackArea::workstack() const
1188 {
1189   QtxWorkstack* ws = 0;
1190   QWidget* wid = parentWidget();
1191   while ( wid && !ws )
1192   {
1193     if ( wid->inherits( "QtxWorkstack" ) )
1194       ws = (QtxWorkstack*)wid;
1195     wid = wid->parentWidget();
1196   }
1197   return ws;
1198 }
1199
1200 bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
1201 {
1202   if ( o->isWidgetType() )
1203   {
1204     QWidget* wid = (QWidget*)o;
1205     if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
1206     {
1207       bool ok = false;
1208       while ( !ok && wid && wid != myClose )
1209       {
1210         ok = wid == this;
1211         wid = wid->parentWidget();
1212       }
1213       if ( ok )
1214         QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
1215     }
1216   }
1217   return false;
1218 }
1219
1220 QRect QtxWorkstackArea::floatRect() const
1221 {
1222   QRect r = myStack->geometry();
1223   return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
1224 }
1225
1226 QRect QtxWorkstackArea::floatTab( const int idx ) const
1227 {
1228   return myBar->tabRect( idx );
1229 }
1230
1231 int QtxWorkstackArea::tabAt( const QPoint& p ) const
1232 {
1233   int idx = -1;
1234   for ( int i = 0; i < myBar->count() && idx == -1; i++ )
1235   {
1236     QRect r = myBar->tabRect( i );
1237     if ( r.isValid() && r.contains( p ) )
1238       idx = i;
1239   }
1240   return idx;
1241 }
1242
1243 void QtxWorkstackArea::customEvent( QCustomEvent* e )
1244 {
1245   switch ( e->type() )
1246   {
1247   case ActivateWidget:
1248     emit activated( activeWidget() );
1249     break;
1250   case FocusWidget:
1251     if ( activeWidget() && !activeWidget()->focusWidget() )
1252       activeWidget()->setFocus();
1253     break;
1254   case RemoveWidget:
1255     removeWidget( (QWidget*)e->data() );
1256     break;
1257   }
1258 }
1259
1260 void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
1261 {
1262   QWidget::focusInEvent( e );
1263
1264   emit activated( activeWidget() );
1265 }
1266
1267 void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
1268 {
1269   QWidget::mousePressEvent( e );
1270
1271   emit activated( activeWidget() );
1272 }
1273
1274 void QtxWorkstackArea::onClose()
1275 {
1276   QWidget* wid = activeWidget();
1277   if ( wid )
1278     wid->close();
1279 }
1280
1281 void QtxWorkstackArea::onSelected( int id )
1282 {
1283   updateCurrent();
1284
1285   emit activated( activeWidget() );
1286 }
1287
1288 void QtxWorkstackArea::onDragActiveTab()
1289 {
1290   QtxWorkstackChild* c = child( activeWidget() );
1291   if ( !c )
1292     return;
1293
1294   new QtxWorkstackDrag( workstack(), c );
1295 }
1296
1297 void QtxWorkstackArea::onChildDestroyed( QObject* obj )
1298 {
1299   QtxWorkstackChild* child = (QtxWorkstackChild*)obj;
1300   myStack->removeWidget( child );
1301
1302   QWidget* wid = 0;
1303   for ( ChildMap::ConstIterator it = myChild.begin(); it != myChild.end() && !wid; ++it )
1304   {
1305     if ( it.data() == child )
1306       wid = it.key();
1307   }
1308
1309   myChild.remove( wid );
1310
1311   QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)RemoveWidget, wid ) );
1312 }
1313
1314 void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
1315 {
1316   setWidgetShown( c->widget(), true );
1317 }
1318
1319 void QtxWorkstackArea::onChildHided( QtxWorkstackChild* c )
1320 {
1321   setWidgetShown( c->widget(), false );
1322 }
1323
1324 void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
1325 {
1326   setWidgetActive( c->widget() );
1327 }
1328
1329 void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
1330 {
1331   updateTab( c->widget() );
1332 }
1333
1334 void QtxWorkstackArea::updateCurrent()
1335 {
1336   QMap<QWidget*, bool> map;
1337   for ( QWidgetListIt it( myList ); it.current(); ++it )
1338   {
1339     map.insert( it.current(), isBlocked( it.current() ) );
1340     setBlocked( it.current(), true );
1341   }
1342
1343   myStack->raiseWidget( myBar->currentTab() );
1344
1345   for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1346     setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1347 }
1348
1349 void QtxWorkstackArea::updateTab( QWidget* wid )
1350 {
1351   QTab* tab = myBar->tab( widgetId( wid ) );
1352   if ( !tab )
1353     return;
1354
1355   QIconSet icoSet;
1356   if ( wid->icon() )
1357     icoSet = QIconSet( *wid->icon() );
1358
1359   tab->setIconSet( icoSet );
1360   tab->setText( wid->caption() );
1361 }
1362
1363 QWidget* QtxWorkstackArea::widget( const int id ) const
1364 {
1365   QWidget* wid = 0;
1366   for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !wid; ++it )
1367   {
1368     if ( it.data().id == id )
1369       wid = it.key();
1370   }
1371   return wid;
1372 }
1373
1374 int QtxWorkstackArea::widgetId( QWidget* wid ) const
1375 {
1376   int id = -1;
1377   if ( myInfo.contains( wid ) )
1378     id = myInfo[wid].id;
1379   return id;
1380 }
1381
1382 bool QtxWorkstackArea::widgetVisibility( QWidget* wid ) const
1383 {
1384   bool res = false;
1385   if ( myInfo.contains( wid ) )
1386     res = myInfo[wid].vis;
1387   return res;
1388 }
1389
1390 void QtxWorkstackArea::setWidgetActive( QWidget* wid )
1391 {
1392   int id = widgetId( wid );
1393   if ( id < 0 )
1394     return;
1395
1396   myBar->setCurrentTab( id );
1397 }
1398
1399 void QtxWorkstackArea::setWidgetShown( QWidget* wid, const bool on )
1400 {
1401   if ( isBlocked( wid ) || !myInfo.contains( wid ) || myInfo[wid].vis == on )
1402     return;
1403
1404   myInfo[wid].vis = on;
1405   updateState();
1406 }
1407
1408 void QtxWorkstackArea::updateState()
1409 {
1410   bool updBar = myBar->isUpdatesEnabled();
1411   bool updStk = myStack->isUpdatesEnabled();
1412   myBar->setUpdatesEnabled( false );
1413   myStack->setUpdatesEnabled( false );
1414
1415   bool block = myBar->signalsBlocked();
1416   myBar->blockSignals( true );
1417
1418   QWidget* prev = activeWidget();
1419
1420   int idx = 0;
1421   for ( QWidgetListIt it( myList ); it.current(); ++it )
1422   {
1423     QWidget* wid = it.current();
1424     int id = widgetId( wid );
1425
1426     if ( id < 0 )
1427       continue;
1428
1429     bool vis = widgetVisibility( wid );
1430
1431     if ( myBar->tab( id ) && ( !vis || myBar->indexOf( id ) != idx ) )
1432       myBar->removeTab( myBar->tab( id ) );
1433
1434     if ( !myBar->tab( id ) && vis )
1435     {
1436       QTab* tab = new QTab( wid->caption() );
1437       myBar->insertTab( tab, idx );
1438       tab->setIdentifier( id );
1439     }
1440
1441     updateTab( wid );
1442
1443     bool block = isBlocked( wid );
1444     setBlocked( wid, true );
1445
1446     QtxWorkstackChild* cont = child( wid );
1447
1448     if ( !vis )
1449       myStack->removeWidget( cont );
1450     else if ( !myStack->widget( id ) )
1451       myStack->addWidget( cont, id );
1452
1453     if ( vis )
1454       idx++;
1455
1456     setBlocked( wid, block );
1457   }
1458
1459   int curId = widgetId( prev );
1460   if ( !myBar->tab( curId ) )
1461   {
1462     QWidget* wid = 0;
1463     int pos = myList.find( prev );
1464     for ( int i = pos - 1; i >= 0 && !wid; i-- )
1465     {
1466       if ( widgetVisibility( myList.at( i ) ) )
1467         wid = myList.at( i );
1468     }
1469
1470     for ( int j = pos + 1; j < (int)myList.count() && !wid; j++ )
1471     {
1472       if ( widgetVisibility( myList.at( j ) ) )
1473         wid = myList.at( j );
1474     }
1475
1476     if ( wid )
1477       curId = widgetId( wid );
1478   }
1479
1480   myBar->setCurrentTab( curId );
1481
1482   myBar->blockSignals( block );
1483
1484   updateCurrent();
1485
1486   myBar->setUpdatesEnabled( updBar );
1487   myStack->setUpdatesEnabled( updStk );
1488   if ( updBar )
1489     myBar->update();
1490   if ( updStk )
1491     myStack->update();
1492
1493   QResizeEvent re( myBar->size(), myBar->size() );
1494   QApplication::sendEvent( myBar, &re );
1495
1496   if ( isEmpty() )
1497   {
1498     hide();
1499     emit deactivated( this );
1500   }
1501   else
1502   {
1503     show();
1504     if ( prev != activeWidget() )
1505       emit activated( activeWidget() );
1506   }
1507 }
1508
1509 int QtxWorkstackArea::generateId() const
1510 {
1511   QMap<int, int> map;
1512
1513   for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end(); ++it )
1514     map.insert( it.data().id, 0 );
1515
1516   int id = 0;
1517   while ( map.contains( id ) )
1518     id++;
1519
1520   return id;
1521 }
1522
1523 bool QtxWorkstackArea::isBlocked( QWidget* wid ) const
1524 {
1525   return myBlock.contains( wid );
1526 }
1527
1528 void QtxWorkstackArea::setBlocked( QWidget* wid, const bool on )
1529 {
1530   if ( on )
1531     myBlock.insert( wid, 0 );
1532   else
1533     myBlock.remove( wid );
1534 }
1535
1536 QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
1537 {
1538   QtxWorkstackChild* res = 0;
1539   if ( myChild.contains( wid ) )
1540     res = myChild[wid];
1541   return res;
1542 }
1543
1544 /*!
1545     Class: QtxWorkstackChild [Internal]
1546     Descr:
1547 */
1548
1549 QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent )
1550 : QHBox( parent ),
1551 myWidget( wid )
1552 {
1553   myWidget->reparent( this, QPoint( 0, 0 ), myWidget->isVisibleTo( myWidget->parentWidget() ) );
1554   myWidget->installEventFilter( this );
1555
1556   connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1557 }
1558
1559 QtxWorkstackChild::~QtxWorkstackChild()
1560 {
1561   qApp->removeEventFilter( this );
1562
1563   if ( !widget() )
1564     return;
1565
1566   widget()->removeEventFilter( this );
1567   widget()->reparent( 0, QPoint( 0, 0 ), false );
1568   disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1569 }
1570
1571 QWidget* QtxWorkstackChild::widget() const
1572 {
1573   return myWidget;
1574 }
1575
1576 bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
1577 {
1578   if ( o->isWidgetType() )
1579   {
1580     if ( e->type() == QEvent::CaptionChange || e->type() == QEvent::IconChange )
1581       emit captionChanged( this );
1582
1583     if ( !e->spontaneous() && ( e->type() == QEvent::Show || e->type() == QEvent::ShowToParent ) )
1584       emit shown( this );
1585
1586     if ( !e->spontaneous() && ( e->type() == QEvent::Hide || e->type() == QEvent::HideToParent ) )
1587       emit hided( this );
1588
1589     if ( e->type() == QEvent::FocusIn )
1590       emit activated( this );
1591   }
1592   return QHBox::eventFilter( o, e );
1593 }
1594
1595 void QtxWorkstackChild::onDestroyed( QObject* obj )
1596 {
1597   if ( obj != widget() )
1598     return;
1599
1600   myWidget = 0;
1601   deleteLater();
1602 }
1603
1604 void QtxWorkstackChild::childEvent( QChildEvent* e )
1605 {
1606   if ( e->type() == QEvent::ChildRemoved && e->child() == widget() )
1607   {
1608     myWidget = 0;
1609     deleteLater();
1610   }
1611   QHBox::childEvent( e );
1612 }
1613
1614 /*!
1615     Class: QtxWorkstackTabBar [Internal]
1616     Descr:
1617 */
1618
1619 QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
1620 : QTabBar( parent ),
1621 myId( -1 )
1622 {
1623 }
1624
1625 QtxWorkstackTabBar::~QtxWorkstackTabBar()
1626 {
1627 }
1628
1629 void QtxWorkstackTabBar::setActive( const bool on )
1630 {
1631   QFont aFont = font();
1632   aFont.setUnderline( on );
1633   setFont( aFont );
1634
1635   update();
1636 }
1637
1638 QRect QtxWorkstackTabBar::tabRect( const int idx ) const
1639 {
1640   QRect r;
1641   QTab* t = tabAt( idx );
1642   if ( t )
1643   {
1644     r = t->rect();
1645     r.setLeft( QMAX( r.left(), 0 ) );
1646
1647     int x1 = tabAt( 0 )->rect().left();
1648     int x2 = tabAt( count() - 1 )->rect().right();
1649
1650     int bw = 0;
1651     if ( QABS( x2 - x1 ) > width() )
1652 #if defined QT_VERSION && QT_VERSION >= 0x30300
1653       bw = 2 * style().pixelMetric( QStyle::PM_TabBarScrollButtonWidth, this );
1654 #else
1655       bw = 2 * 16;
1656 #endif
1657
1658     int limit = width() - bw;
1659     r.setRight( QMIN( r.right(), limit ) );
1660
1661     r = QRect( mapToGlobal( r.topLeft() ), r.size() );
1662   }
1663   return r;
1664 }
1665
1666 void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
1667 {
1668   if ( myId != -1 && !tab( myId )->rect().contains( e->pos() ) )
1669   {
1670     myId = -1;
1671     emit dragActiveTab();
1672   }
1673
1674   QTabBar::mouseMoveEvent( e );
1675 }
1676
1677 void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
1678 {
1679   QTabBar::mousePressEvent( e );
1680
1681   if ( e->button() == LeftButton )
1682     myId = currentTab();
1683 }
1684
1685 void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
1686 {
1687   QTabBar::mouseReleaseEvent( e );
1688
1689   myId = -1;
1690
1691   if ( e->button() == RightButton )
1692     emit contextMenuRequested( e->globalPos() );
1693 }
1694
1695 void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
1696 {
1697   if ( e->reason() != QContextMenuEvent::Mouse )
1698     emit contextMenuRequested( e->globalPos() );
1699 }
1700
1701 void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
1702 {
1703   if ( currentTab() != t->identifier() )
1704   {
1705     QFont fnt = p->font();
1706     fnt.setUnderline( false );
1707     p->setFont( fnt );
1708   }
1709   QTabBar::paintLabel( p, br, t, has_focus );
1710 }
1711
1712 /*!
1713     Class: QtxWorkstackDrag [Internal]
1714     Descr:
1715 */
1716
1717 QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
1718 : QObject( 0 ),
1719 myWS( ws ),
1720 myTab( -1 ),
1721 myArea( 0 ),
1722 myPainter( 0 ),
1723 myChild( child )
1724 {
1725   qApp->installEventFilter( this );
1726 }
1727
1728 QtxWorkstackDrag::~QtxWorkstackDrag()
1729 {
1730   qApp->removeEventFilter( this );
1731
1732   endDrawRect();
1733 }
1734
1735 bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
1736 {
1737   switch ( e->type() )
1738   {
1739   case QEvent::MouseMove:
1740     updateTarget( ((QMouseEvent*)e)->globalPos() );
1741     break;
1742   case QEvent::MouseButtonRelease:
1743     drawRect();
1744     endDrawRect();
1745     dropWidget();
1746     deleteLater();
1747     break;
1748   default:
1749     return false;
1750   }
1751   return true;
1752 }
1753
1754 void QtxWorkstackDrag::updateTarget( const QPoint& p )
1755 {
1756   int tab = -1;
1757   QtxWorkstackArea* area = detectTarget( p, tab );
1758   setTarget( area, tab );
1759 }
1760
1761 QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
1762 {
1763   if ( p.isNull() )
1764     return 0;
1765
1766   QtxWorkstackArea* area = myWS->areaAt( p );
1767   if ( area )
1768     tab = area->tabAt( p );
1769   return area;
1770 }
1771
1772 void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
1773 {
1774   if ( !area || ( myArea == area && tab == myTab ) )
1775     return;
1776
1777   startDrawRect();
1778
1779   if ( myArea )
1780     drawRect();
1781
1782   myTab = tab;
1783   myArea = area;
1784
1785   if ( myArea )
1786     drawRect();
1787 }
1788
1789 void QtxWorkstackDrag::dropWidget()
1790 {
1791   if ( myArea )
1792     myArea->insertWidget( myChild->widget(), myTab );
1793 }
1794
1795 void QtxWorkstackDrag::drawRect()
1796 {
1797   if ( !myPainter || !myArea )
1798     return;
1799
1800   QRect r = myArea->floatRect();
1801   int m = myPainter->pen().width();
1802
1803   r.setTop( r.top() + m + 2 );
1804   r.setLeft( r.left() + m + 2 );
1805   r.setRight( r.right() - m - 2 );
1806   r.setBottom( r.bottom() - m - 2 );
1807
1808   myPainter->drawRect( r );
1809
1810   QRect tr = myArea->floatTab( myTab );
1811   tr.setTop( tr.top() + m );
1812   tr.setLeft( tr.left() + m );
1813   tr.setRight( tr.right() - m );
1814   tr.setBottom( tr.bottom() - m );
1815
1816   myPainter->drawRect( tr );
1817 }
1818
1819 void QtxWorkstackDrag::endDrawRect()
1820 {
1821   delete myPainter;
1822   myPainter = 0;
1823 }
1824
1825 void QtxWorkstackDrag::startDrawRect()
1826 {
1827   if ( myPainter )
1828     return;
1829
1830   int scr = QApplication::desktop()->screenNumber( (QWidget*)this );
1831   QWidget* paint_on = QApplication::desktop()->screen( scr );
1832
1833   myPainter = new QPainter( paint_on, true );
1834   myPainter->setPen( QPen( gray, 3 ) );
1835   myPainter->setRasterOp( XorROP );
1836 }