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