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