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