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