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