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