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