Salome HOME
Update from BR_V5_DEV 13Feb2009
[modules/gui.git] / src / Qtx / QtxWorkstack.cxx
1 //  Copyright (C) 2007-2008  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 //  Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 //  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 //  This library is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU Lesser General Public
8 //  License as published by the Free Software Foundation; either
9 //  version 2.1 of the License.
10 //
11 //  This library is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 //  Lesser General Public License for more details.
15 //
16 //  You should have received a copy of the GNU Lesser General Public
17 //  License along with this library; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 //  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 // File:      QtxWorkstack.cxx
23 // Author:    Sergey TELKOV
24 //
25 #include "QtxWorkstack.h"
26
27 #include "QtxAction.h"
28
29 #include <QMenu>
30 #include <QStyle>
31 #include <QRegExp>
32 #include <QLayout>
33 #include <QPainter>
34 #include <QSplitter>
35 #include <QFocusEvent>
36 #include <QMouseEvent>
37 #include <QRubberBand>
38 #include <QApplication>
39 #include <QStyleOption>
40 #include <QInputDialog>
41 #include <QStackedWidget>
42 #include <QAbstractButton>
43
44 /*!
45   \class QtxWorkstackArea::WidgetEvent
46   \internal
47   \brief Internal class used to forward child widgets events to the workarea
48 */
49
50 class QtxWorkstackArea::WidgetEvent : public QEvent
51 {
52 public:
53   WidgetEvent( Type t, QWidget* w = 0 ) : QEvent( t ), myWidget( w ) {};
54
55   QWidget* widget() const { return myWidget; }
56
57 private:
58   QWidget* myWidget;   // event receiver widget
59 };
60
61 /*!
62   \class QtxWorkstackDrag
63   \internal
64   \brief Workstack drag object
65 */
66
67 /*!
68   \brief Constructor.
69   \param ws parent workstack
70   \param child child widget container
71 */
72 QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
73 : QObject( 0 ),
74   myWS( ws ),
75   myChild( child ),
76   myTab( -1 ),
77   myArea( 0 ),
78   myTabRect( 0 ),
79   myAreaRect( 0 )
80 {
81   QApplication::instance()->installEventFilter( this );
82 }
83
84 /*!
85   \brief Destructor.
86 */
87 QtxWorkstackDrag::~QtxWorkstackDrag()
88 {
89   QApplication::instance()->removeEventFilter( this );
90
91   endDrawRect();
92 }
93
94 /*!
95   \brief Custom event filter.
96   \param o event receiver widget
97   \param e event
98   \return \c true if event should be filtered (stop further processing)
99 */
100 bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
101 {
102   switch ( e->type() )
103   {
104   case QEvent::MouseMove:
105     updateTarget( ((QMouseEvent*)e)->globalPos() );
106     break;
107   case QEvent::MouseButtonRelease:
108     drawRect();
109     endDrawRect();
110     dropWidget();
111     deleteLater();
112     break;
113   default:
114     return false;
115   }
116   return true;
117 }
118
119 /*!
120   \brief Detect and set dropping target widget.
121   \param p current dragging position
122 */
123 void QtxWorkstackDrag::updateTarget( const QPoint& p )
124 {
125   int tab = -1;
126   QtxWorkstackArea* area = detectTarget( p, tab );
127   setTarget( area, tab );
128 }
129
130 /*!
131   \brief Detect dropping target.
132   \param p current dragging position
133   \param tab resulting target tab page index
134   \return target workarea or 0 if there is no correct drop target
135 */
136 QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
137 {
138   if ( p.isNull() )
139     return 0;
140
141   QtxWorkstackArea* area = myWS->areaAt( p );
142   if ( area )
143     tab = area->tabAt( p );
144   return area;
145 }
146
147 /*!
148   \brief Set dropping target.
149   \param area new target workarea
150   \param tab target workarea's tab page index
151 */
152 void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
153 {
154   if ( !area || ( myArea == area && tab == myTab ) )
155     return;
156
157   startDrawRect();
158
159   if ( myArea )
160     drawRect();
161
162   myTab = tab;
163   myArea = area;
164
165   if ( myArea )
166     drawRect();
167 }
168
169 /*!
170   \brief Called when drop operation is finished.
171   
172   Inserts dropped widget to the target workarea.
173 */
174 void QtxWorkstackDrag::dropWidget()
175 {
176   if ( myArea )
177     myArea->insertWidget( myChild->widget(), myTab );
178 }
179
180 /*!
181   \brief Draw floating rectangle.
182 */
183 void QtxWorkstackDrag::drawRect()
184 {
185   if ( !myArea )
186     return;
187
188   QRect r = myArea->floatRect();
189   int m = 2;
190
191   r.setTop( r.top() + m + 2 );
192   r.setLeft( r.left() + m + 2 );
193   r.setRight( r.right() - m - 2 );
194   r.setBottom( r.bottom() - m - 2 );
195
196   if ( myAreaRect )
197   {
198     myAreaRect->setGeometry( r );
199     myAreaRect->setVisible( r.isValid() );
200   }
201
202   QRect tr = myArea->floatTab( myTab );
203
204   tr.setTop( tr.top() + m );
205   tr.setLeft( tr.left() + m );
206   tr.setRight( tr.right() - m );
207   tr.setBottom( tr.bottom() - m );
208
209   if ( myTabRect )
210   {
211     myTabRect->setGeometry( tr );
212     myTabRect->setVisible( tr.isValid() );
213   }
214 }
215
216 /*!
217   \brief Delete rubber band on the end on the dragging operation.
218 */
219 void QtxWorkstackDrag::endDrawRect()
220 {
221   delete myAreaRect;
222   myAreaRect = 0;
223
224   delete myTabRect;
225   myTabRect = 0;
226 }
227
228 /*!
229   \brief Create rubber band to be drawn on the dragging operation.
230 */
231 void QtxWorkstackDrag::startDrawRect()
232 {
233   if ( !myTabRect )
234     myTabRect = new QRubberBand( QRubberBand::Rectangle );
235
236   myTabRect->hide();
237
238   if ( !myAreaRect )
239     myAreaRect = new QRubberBand( QRubberBand::Rectangle );
240
241   myAreaRect->hide();
242 }
243
244
245 /*
246   \class CloseButton
247   \brief Workstack area close button.
248   \internal
249 */
250 class CloseButton : public QAbstractButton
251 {
252 public:
253   CloseButton( QWidget* );
254
255   QSize        sizeHint() const;
256   QSize        minimumSizeHint() const;
257
258   void enterEvent( QEvent* );
259   void leaveEvent( QEvent* );
260   void paintEvent( QPaintEvent* );
261 };
262
263 /*!
264   \brief Constructor
265   \internal
266   \param parent parent widget
267 */
268 CloseButton::CloseButton( QWidget* parent )
269 : QAbstractButton( parent )
270 {
271  setFocusPolicy( Qt::NoFocus );
272 }
273
274 /*!
275   \brief Get appropriate size for the button.
276   \internal
277   \return size value
278 */
279 QSize CloseButton::sizeHint() const
280 {
281   ensurePolished();
282   int dim = 0;
283   if( !icon().isNull() ) 
284   {
285     const QPixmap pm = icon().pixmap( style()->pixelMetric( QStyle::PM_SmallIconSize ),
286                                       QIcon::Normal );
287     dim = qMax( pm.width(), pm.height() );
288   }
289   return QSize( dim + 4, dim + 4 );
290 }
291
292 /*!
293   \brief Get minimum appropriate size for the button.
294   \internal
295   \return minimum size value
296 */
297 QSize CloseButton::minimumSizeHint() const
298
299   return sizeHint(); 
300 }
301
302 /*!
303   \brief Process mouse enter event.
304   \internal
305   \param event mouse enter event
306 */
307 void CloseButton::enterEvent( QEvent *event )
308 {
309   if ( isEnabled() )
310     update();
311   QAbstractButton::enterEvent( event );
312 }
313
314 /*!
315   \brief Process mouse leave event.
316   \internal
317   \param event mouse leave event
318 */
319 void CloseButton::leaveEvent( QEvent *event )
320 {
321   if( isEnabled() )
322     update();
323   QAbstractButton::leaveEvent( event );
324 }
325
326 /*!
327   \brief Process paint event.
328   \internal
329   \param event paint event
330 */
331 void CloseButton::paintEvent( QPaintEvent* )
332 {
333   QPainter p( this );
334
335   QRect r = rect();
336   QStyleOption opt;
337   opt.init( this );
338   opt.state |= QStyle::State_AutoRaise;
339   if ( isEnabled() && underMouse() && !isChecked() && !isDown() )
340     opt.state |= QStyle::State_Raised;
341   if ( isChecked() )
342     opt.state |= QStyle::State_On;
343   if ( isDown() )
344     opt.state |= QStyle::State_Sunken;
345   style()->drawPrimitive( QStyle::PE_PanelButtonTool, &opt, &p, this );
346
347   int shiftHorizontal = opt.state & QStyle::State_Sunken ? style()->pixelMetric( QStyle::PM_ButtonShiftHorizontal, &opt, this ) : 0;
348   int shiftVertical = opt.state & QStyle::State_Sunken ? style()->pixelMetric( QStyle::PM_ButtonShiftVertical, &opt, this ) : 0;
349
350   r.adjust( 2, 2, -2, -2 );
351   r.translate( shiftHorizontal, shiftVertical );
352
353   QPixmap pm = icon().pixmap( style()->pixelMetric( QStyle::PM_SmallIconSize ), isEnabled() ?
354                               underMouse() ? QIcon::Active : QIcon::Normal
355                               : QIcon::Disabled,
356                               isDown() ? QIcon::On : QIcon::Off );
357   style()->drawItemPixmap( &p, r, Qt::AlignCenter, pm );
358 }
359
360 /*!
361   \class QtxWorkstackArea
362   \internal
363   \brief Workstack widget workarea.
364 */
365
366 /*!
367   \brief Constructor.
368   \param parent parent widget
369 */
370 QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
371 : QFrame( parent )
372 {
373   setFrameStyle( QFrame::Panel | QFrame::Sunken );
374
375   QVBoxLayout* base = new QVBoxLayout( this );
376   base->setMargin( frameWidth() );
377   base->setSpacing( 0 );
378
379   QWidget* top = new QWidget( this );
380   base->addWidget( top );
381
382   QHBoxLayout* tl = new QHBoxLayout( top );
383   tl->setMargin( 0 );
384
385   myBar = new QtxWorkstackTabBar( top );
386   tl->addWidget( myBar, 1 );
387
388   CloseButton* close = new CloseButton( top );
389   close->setIcon( style()->standardIcon( QStyle::SP_TitleBarCloseButton ) );
390   myClose = close;
391   tl->addWidget( myClose );
392
393   myStack = new QStackedWidget( this );
394
395   base->addWidget( myStack, 1 );
396
397   connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
398   connect( myBar, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
399   connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
400   connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
401
402   updateState();
403
404   updateActiveState();
405
406   QApplication::instance()->installEventFilter( this );
407 }
408
409 /*!
410   \brief Destructor.
411 */
412 QtxWorkstackArea::~QtxWorkstackArea()
413 {
414   QApplication::instance()->removeEventFilter( this );
415 }
416
417 /*!
418   \brief Check if workarea contains any widgets.
419   \return \c true if area is null (havn't any child widgets)
420 */
421 bool QtxWorkstackArea::isNull() const
422 {
423   return myList.isEmpty();
424 }
425
426 /*!
427   \brief Check if workarea contains visible widgets.
428   \return \c true if area is empty (all child widgets are removed or now shown)
429 */
430 bool QtxWorkstackArea::isEmpty() const
431 {
432   bool res = false;
433   for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !res; ++it )
434     res = it.value().vis;
435   return !res;
436 }
437
438 /*!
439   \brief Add widget to the workarea.
440   \param wid widget to be added
441   \param idx position in the area widget to be added to
442   \param f widget flags
443   \return child widget container object (or 0 if index is invalid)
444 */
445 QWidget* QtxWorkstackArea::insertWidget( QWidget* wid, const int idx, Qt::WindowFlags f )
446 {
447   if ( !wid )
448     return 0;
449
450   int pos = myList.indexOf( wid );
451   if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
452     return 0;
453
454   myList.removeAll( wid );
455   pos = idx < 0 ? myList.count() : idx;
456   myList.insert( qMin( pos, (int)myList.count() ), wid );
457   if ( !myInfo.contains( wid ) )
458   {
459     QtxWorkstackChild* child = new QtxWorkstackChild( wid, myStack, f );
460     myChild.insert( wid, child );
461     myInfo.insert( wid, WidgetInfo() );
462     myInfo[wid].id = generateId();
463     myInfo[wid].vis = wid->isVisibleTo( wid->parentWidget() );
464
465     connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
466     connect( wid, SIGNAL( destroyed() ), this, SLOT( onWidgetDestroyed() ) );
467     connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
468     connect( child, SIGNAL( hidden( QtxWorkstackChild* ) ), this, SLOT( onChildHidden( QtxWorkstackChild* ) ) );
469     connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
470     connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
471   }
472
473   updateState();
474
475   setWidgetActive( wid );
476   wid->setFocus();
477
478   return myChild[wid];
479 }
480
481 /*!
482   \brief Create and show popup menu for the area.
483   \param p mouse pointer position at which popup menu should be shown
484 */
485 void QtxWorkstackArea::onContextMenuRequested( QPoint p )
486 {
487   const QtxWorkstackTabBar* bar = ::qobject_cast<const QtxWorkstackTabBar*>( sender() );
488   if ( !bar )
489     return;
490
491   QWidget* wid = 0;
492   int idx = tabAt( p );
493   if ( idx != -1 )
494     wid = widget( myBar->tabId( idx ) );
495
496   emit contextMenuRequested( wid, p );
497 }
498
499 /*!
500   \brief Called when area's child widget is destroyed.
501
502   Removes child widget from the area.
503 */
504 void QtxWorkstackArea::onWidgetDestroyed()
505 {
506   if ( sender() )
507     removeWidget( (QWidget*)sender(), false );
508 }
509
510 /*!
511   \brief Remove widget from workarea.
512   \param wid widget to be removed
513   \param del if \c true the widget should be also deleted
514 */
515 void QtxWorkstackArea::removeWidget( QWidget* wid, const bool del )
516 {
517   if ( !myList.contains( wid ) )
518     return;
519
520   if ( myBar->indexOf( widgetId( wid ) ) != -1 )
521     myBar->removeTab( myBar->indexOf( widgetId( wid ) ) );
522
523   myStack->removeWidget( child( wid ) );
524
525   myList.removeAll( wid );
526   myInfo.remove( wid );
527   myChild.remove( wid );
528
529   if ( del )
530     delete child( wid );
531
532   if ( isNull() )
533     deleteLater();
534   else
535     updateState();
536 }
537
538 /*!
539   \brief Get all visible child widgets.
540   \return list of visible child widgets
541 */
542 QWidgetList QtxWorkstackArea::widgetList() const
543 {
544   QWidgetList lst;
545   for ( QWidgetList::const_iterator it = myList.begin(); it != myList.end(); ++it )
546   {
547     if ( widgetVisibility( *it ) )
548       lst.append( *it );
549   }
550   return lst;
551 }
552
553 /*!
554   \brief Get active child widget.
555   \return active widget
556 */
557 QWidget* QtxWorkstackArea::activeWidget() const
558 {
559   return widget( myBar->tabId( myBar->currentIndex() ) );
560 }
561
562 /*!
563   \brief Set active widget.
564   \param wid widget to be made active
565 */
566 void QtxWorkstackArea::setActiveWidget( QWidget* wid )
567 {
568   myBar->setCurrentIndex( myBar->indexOf( widgetId( wid ) ) );
569 }
570
571 /*!
572   \brief Check if area owns the specified widget.
573   \param wid widget to be checked
574   \return \c true if area contains widget
575 */
576 bool QtxWorkstackArea::contains( QWidget* wid ) const
577 {
578   return myList.contains( wid );
579 }
580
581 /*!
582   \brief Show/hide workarea.
583   \param on new visibility state
584 */
585 void QtxWorkstackArea::setVisible( bool on )
586 {
587   QMap<QWidget*, bool> map;
588   for ( QWidgetList::iterator it = myList.begin(); it != myList.end(); ++it )
589   {
590     map.insert( *it, isBlocked( *it ) );
591     setBlocked( *it, true );
592   }
593
594   QFrame::setVisible( on );
595
596   for ( QWidgetList::iterator itr = myList.begin(); itr != myList.end(); ++itr )
597     setBlocked( *itr, map.contains( *itr ) ? map[*itr] : false );
598 }
599
600 /*!
601   \brief Check if workarea is active.
602   \return \c true if area is active
603 */
604 bool QtxWorkstackArea::isActive() const
605 {
606   QtxWorkstack* ws = workstack();
607   if ( !ws )
608     return false;
609
610   return ws->activeArea() == this;
611 }
612
613 /*!
614   \brief Update active tab bar state (active/inactive).
615 */
616 void QtxWorkstackArea::updateActiveState()
617 {
618   myBar->setActive( isActive() );
619 }
620
621 /*!
622   \brief Get parent workstack
623   \return workstack owning this workarea
624 */
625 QtxWorkstack* QtxWorkstackArea::workstack() const
626 {
627   QtxWorkstack* ws = 0;
628   QWidget* wid = parentWidget();
629   while ( wid && !ws )
630   {
631     if ( wid->inherits( "QtxWorkstack" ) )
632       ws = (QtxWorkstack*)wid;
633     wid = wid->parentWidget();
634   }
635   return ws;
636 }
637
638 /*!
639   \brief Custom event filter.
640
641   Process events from child widgets.
642
643   \param o event receiver widget
644   \param e event
645   \return \c true if event should be filtered (stop further processing)
646 */
647 bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
648 {
649   if ( o->isWidgetType() )
650   {
651     QWidget* wid = (QWidget*)o;
652     if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
653     {
654       bool ok = false;
655       while ( !ok && wid && wid != myClose )
656       {
657         ok = wid == this;
658         wid = wid->parentWidget();
659       }
660       if ( ok )
661         QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
662     }
663   }
664   return false;
665 }
666
667 /*!
668   \brief Get rectangle to be drawn when highlighting drop area.
669   \return area drop rectangle
670 */
671 QRect QtxWorkstackArea::floatRect() const
672 {
673   QRect r = myStack->geometry();
674   return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
675 }
676
677 /*!
678   \brief Get rectangle to be drawn when highlighting drop area on tab bar.
679   \param idx tab index
680   \return tab bar drop rectrangle
681 */
682 QRect QtxWorkstackArea::floatTab( const int idx ) const
683 {
684   QRect r = myBar->tabRect( idx );
685   return QRect( myBar->mapToGlobal( r.topLeft() ), r.size() );
686 }
687
688 /*!
689   \brief Get tab index by point.
690   \param p point
691   \return tab covering point or -1 if there is no tab covering point
692 */
693 int QtxWorkstackArea::tabAt( const QPoint& pnt ) const
694 {
695   int idx = -1;
696   QPoint p = myBar->mapFromGlobal( pnt );
697   for ( int i = 0; i < myBar->count() && idx == -1; i++ )
698   {
699     QRect r = myBar->tabRect( i );
700     if ( r.isValid() && r.contains( p ) )
701       idx = i;
702   }
703   return idx;
704 }
705
706 /*!
707   \brief Event handler for custom events.
708   \param e custom event
709 */
710 void QtxWorkstackArea::customEvent( QEvent* e )
711 {
712   WidgetEvent* we = (WidgetEvent*)e;
713
714   switch ( we->type() )
715   {
716   case ActivateWidget:
717     myBar->updateActiveState();
718     emit activated( activeWidget() );
719     break;
720   case FocusWidget:
721     if ( activeWidget() )
722     {
723       if ( !activeWidget()->focusWidget() )
724         activeWidget()->setFocus();
725       else
726       {
727         if ( activeWidget()->focusWidget()->hasFocus() )
728         {
729           QFocusEvent in( QEvent::FocusIn );
730           QApplication::sendEvent( this, &in );
731         }
732         else {
733           activeWidget()->focusWidget()->setFocus();
734           myBar->updateActiveState();
735         }
736       }
737     }
738     break;
739   case RemoveWidget:
740     removeWidget( we->widget() );
741     break;
742   default:
743     break;
744   }
745 }
746
747 /*!
748   \brief Customize focus in event handler.
749   \param e focus in event
750 */
751 void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
752 {
753   QFrame::focusInEvent( e );
754
755   myBar->updateActiveState();
756
757   emit activated( activeWidget() );
758 }
759
760 /*!
761   \brief Customize mouse press event handler.
762   \param e mouse press event
763 */
764 void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
765 {
766   QFrame::mousePressEvent( e );
767
768   emit activated( activeWidget() );
769 }
770
771 /*!
772   \brief Called when user presses "Close" button.
773 */
774 void QtxWorkstackArea::onClose()
775 {
776   QWidget* wid = activeWidget();
777   if ( wid )
778     wid->close();
779 }
780
781 /*!
782   \brief Called when user selects any tab page.
783   \param idx tab page index (not used)
784 */
785 void QtxWorkstackArea::onCurrentChanged( int /*idx*/ )
786 {
787   updateCurrent();
788
789   emit activated( activeWidget() );
790 }
791
792 /*!
793   \brief Called when user starts tab page dragging.
794 */
795 void QtxWorkstackArea::onDragActiveTab()
796 {
797   QtxWorkstackChild* c = child( activeWidget() );
798   if ( !c )
799     return;
800
801   new QtxWorkstackDrag( workstack(), c );
802 }
803
804 /*!
805   \brief Called when area's child widget container is destroyed.
806   \param obj widget container being destroyed
807 */
808 void QtxWorkstackArea::onChildDestroyed( QObject* obj )
809 {
810   QtxWorkstackChild* child = (QtxWorkstackChild*)obj;
811   myStack->removeWidget( child );
812
813   QWidget* wid = 0;
814   for ( ChildMap::ConstIterator it = myChild.begin(); it != myChild.end() && !wid; ++it )
815   {
816     if ( it.value() == child )
817       wid = it.key();
818   }
819
820   myChild.remove( wid );
821
822   QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)RemoveWidget, wid ) );
823 }
824
825 /*!
826   \brief Called when child widget container is shown.
827   \param c child widget container being shown
828 */
829 void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
830 {
831   setWidgetShown( c->widget(), true );
832 }
833
834 /*!
835   \brief Called when child widget container is hidden.
836   \param c child widget container being hidden
837 */
838 void QtxWorkstackArea::onChildHidden( QtxWorkstackChild* c )
839 {
840   setWidgetShown( c->widget(), false );
841 }
842
843 /*!
844   \brief Called when child widget container is activated.
845   \param c child widget container being activated
846 */
847 void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
848 {
849   setWidgetActive( c->widget() );
850 }
851
852 /*!
853   \brief Called when child widget container's title is changed.
854   \param c child widget container which title is changed
855 */
856 void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
857 {
858   updateTab( c->widget() );
859 }
860
861 /*!
862   \brief Update current child widget container.
863
864   Raises widget when active tab page is changed.
865 */
866 void QtxWorkstackArea::updateCurrent()
867 {
868   QMap<QWidget*, bool> map;
869   for ( QWidgetList::iterator it = myList.begin(); it != myList.end(); ++it )
870   {
871     map.insert( *it, isBlocked( *it ) );
872     setBlocked( *it, true );
873   }
874
875   QWidget* cur = child( widget( myBar->tabId( myBar->currentIndex() ) ) );
876   if ( cur )
877     myStack->setCurrentWidget( cur );
878
879   for ( QWidgetList::iterator itr = myList.begin(); itr != myList.end(); ++itr )
880     setBlocked( *itr, map.contains( *itr ) ? map[*itr] : false );
881 }
882
883 /*!
884   \brief Update tab bar.
885   \param wid tab page widget
886 */
887 void QtxWorkstackArea::updateTab( QWidget* wid )
888 {
889   int idx = myBar->indexOf( widgetId( wid ) );
890   if ( idx < 0 )
891     return;
892
893   myBar->setTabIcon( idx, wid->windowIcon() );
894   myBar->setTabText( idx, wid->windowTitle() );
895 }
896
897 /*!
898   \brief Get child widget by specified identifier.
899   \param id widget ID
900   \return widget or 0, if identifier in invalid
901 */
902 QWidget* QtxWorkstackArea::widget( const int id ) const
903 {
904   QWidget* wid = 0;
905   for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !wid; ++it )
906   {
907     if ( it.value().id == id )
908       wid = it.key();
909   }
910   return wid;
911 }
912
913 /*!
914   \brief Get child widget identifier.
915   \param wid widget
916   \return widget ID or -1 if widget is not found
917 */
918 int QtxWorkstackArea::widgetId( QWidget* wid ) const
919 {
920   int id = -1;
921   if ( myInfo.contains( wid ) )
922     id = myInfo[wid].id;
923   return id;
924 }
925
926 /*!
927   \brief Get child widget's visibility.
928   \param wid widget
929   \return \c true if widget is visible
930 */
931 bool QtxWorkstackArea::widgetVisibility( QWidget* wid ) const
932 {
933   bool res = false;
934   if ( myInfo.contains( wid ) )
935     res = myInfo[wid].vis;
936   return res;
937 }
938
939 /*!
940   \brief Set active child widget.
941   \param wid widget to be set active
942 */
943 void QtxWorkstackArea::setWidgetActive( QWidget* wid )
944 {
945   int id = widgetId( wid );
946   if ( id < 0 )
947     return;
948
949   myBar->setCurrentIndex( myBar->indexOf( id ) );
950 }
951
952 /*!
953   \brief Show/hide child widget.
954   \param wid widget
955   \param on new visibility state
956 */
957 void QtxWorkstackArea::setWidgetShown( QWidget* wid, const bool on )
958 {
959   if ( isBlocked( wid ) || !myInfo.contains( wid ) || myInfo[wid].vis == on )
960     return;
961
962   myInfo[wid].vis = on;
963   updateState();
964 }
965
966 /*!
967   \brief Update internal state.
968 */
969 void QtxWorkstackArea::updateState()
970 {
971   bool updBar = myBar->updatesEnabled();
972   bool updStk = myStack->updatesEnabled();
973   myBar->setUpdatesEnabled( false );
974   myStack->setUpdatesEnabled( false );
975
976   bool block = myBar->signalsBlocked();
977   myBar->blockSignals( true );
978
979   QWidget* prev = activeWidget();
980
981   int idx = 0;
982   for ( QWidgetList::iterator it = myList.begin(); it != myList.end(); ++it )
983   {
984     QWidget* wid = *it;
985     int id = widgetId( wid );
986
987     if ( id < 0 )
988       continue;
989
990     bool vis = widgetVisibility( wid );
991
992     int cIdx = myBar->indexOf( id );
993     if ( cIdx != -1 && ( !vis || myBar->indexOf( id ) != idx ) )
994       myBar->removeTab( cIdx );
995
996     if ( myBar->indexOf( id ) == -1 && vis )
997       myBar->setTabId( myBar->insertTab( idx, wid->windowTitle() ), id );
998
999     updateTab( wid );
1000
1001     bool block = isBlocked( wid );
1002     setBlocked( wid, true );
1003
1004     QtxWorkstackChild* cont = child( wid );
1005
1006     if ( !vis )
1007       myStack->removeWidget( cont );
1008     else if ( myStack->indexOf( cont ) < 0 )
1009       myStack->addWidget( cont );
1010
1011     if ( vis )
1012       idx++;
1013
1014     setBlocked( wid, block );
1015   }
1016
1017   int curId = widgetId( prev );
1018   if ( myBar->indexOf( curId ) < 0 )
1019   {
1020     QWidget* wid = 0;
1021     int pos = myList.indexOf( prev );
1022     for ( int i = pos - 1; i >= 0 && !wid; i-- )
1023     {
1024       if ( widgetVisibility( myList.at( i ) ) )
1025         wid = myList.at( i );
1026     }
1027
1028     for ( int j = pos + 1; j < (int)myList.count() && !wid; j++ )
1029     {
1030       if ( widgetVisibility( myList.at( j ) ) )
1031         wid = myList.at( j );
1032     }
1033
1034     if ( wid )
1035       curId = widgetId( wid );
1036   }
1037
1038   myBar->setCurrentIndex( myBar->indexOf( curId ) );
1039
1040   myBar->blockSignals( block );
1041
1042   updateCurrent();
1043
1044   myBar->updateActiveState();
1045
1046   myBar->setUpdatesEnabled( updBar );
1047   myStack->setUpdatesEnabled( updStk );
1048   if ( updBar )
1049     myBar->update();
1050   if ( updStk )
1051     myStack->update();
1052
1053   QResizeEvent re( myBar->size(), myBar->size() );
1054   QApplication::sendEvent( myBar, &re );
1055
1056   myBar->updateGeometry();
1057
1058   if ( isEmpty() )
1059   {
1060     hide();
1061     emit deactivated( this );
1062   }
1063   else
1064   {
1065     show();
1066     if ( prev != activeWidget() )
1067       emit activated( activeWidget() );
1068   }
1069 }
1070
1071 /*!
1072   \brief Generate unique widget identifier.
1073   \return first non shared widget ID
1074 */
1075 int QtxWorkstackArea::generateId() const
1076 {
1077   QMap<int, int> map;
1078
1079   for ( WidgetInfoMap::const_iterator it = myInfo.begin(); it != myInfo.end(); ++it )
1080     map.insert( it.value().id, 0 );
1081
1082   int id = 0;
1083   while ( map.contains( id ) )
1084     id++;
1085
1086   return id;
1087 }
1088
1089 /*!
1090   \brief Check if the child wiget is blocked.
1091   \param wid widget
1092   \return \c true if the widget is blocked
1093 */
1094 bool QtxWorkstackArea::isBlocked( QWidget* wid ) const
1095 {
1096   return myBlock.contains( wid );
1097 }
1098
1099 /*!
1100   \brief Block widget.
1101   \param wid widget
1102   \param on new blocked state
1103 */
1104 void QtxWorkstackArea::setBlocked( QWidget* wid, const bool on )
1105 {
1106   if ( on )
1107     myBlock.insert( wid, 0 );
1108   else
1109     myBlock.remove( wid );
1110 }
1111
1112 /*!
1113   \brief Get child widget container.
1114   \param wid child widget
1115   \return child widget container corresponding to the \a wid
1116 */
1117 QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
1118 {
1119   QtxWorkstackChild* res = 0;
1120   if ( myChild.contains( wid ) )
1121     res = myChild[wid];
1122   return res;
1123 }
1124
1125 /*!
1126   \fn void QtxWorkstackArea::activated( QWidget* w )
1127   \brief Emitted when child widget is activated.
1128   \param w child widget being activated
1129 */
1130
1131 /*!
1132   \fn void QtxWorkstackArea::contextMenuRequested( QWidget* w, QPoint p )
1133   \brief Emitted when context popup menu is requested.
1134   \param w child widget popup menu requested for
1135   \param p point popup menu to be shown at
1136 */
1137
1138 /*!
1139   \fn void QtxWorkstackArea::deactivated( QtxWorkstackArea* wa )
1140   \brief Emitted when workarea is deactivated.
1141   \param wa workarea being deactivated
1142 */
1143
1144 /*!
1145   \class QtxWorkstackChild
1146   \internal
1147   \brief Workarea child widget container.
1148 */
1149
1150 /*!
1151   \brief Constructor.
1152   \param wid child widget
1153   \param parent parent widget
1154   \param f widget flags
1155 */
1156 QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent, Qt::WindowFlags f )
1157 : QWidget( parent ),
1158   myWidget( wid )
1159 {
1160   myWidget->setParent( this, f );
1161   myWidget->installEventFilter( this );
1162   QVBoxLayout* base = new QVBoxLayout( this );
1163   base->setMargin( 0 );
1164   base->addWidget( myWidget );
1165
1166   connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1167 }
1168
1169 /*!
1170   \brief Destructor.
1171 */
1172 QtxWorkstackChild::~QtxWorkstackChild()
1173 {
1174   QApplication::instance()->removeEventFilter( this );
1175
1176   if ( !widget() )
1177     return;
1178
1179   disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1180
1181   widget()->hide();
1182   widget()->removeEventFilter( this );
1183
1184   widget()->setParent( 0 );
1185 }
1186
1187 /*!
1188   \brief Get child widget.
1189   \return child widget
1190 */
1191 QWidget* QtxWorkstackChild::widget() const
1192 {
1193   return myWidget;
1194 }
1195
1196 /*!
1197   \brief Custom event filter.
1198
1199   Process events from child widgets.
1200
1201   \param o event receiver widget
1202   \param e event
1203   \return \c true if event should be filtered (stop further processing)
1204 */
1205 bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
1206 {
1207   if ( o->isWidgetType() )
1208   {
1209     if ( e->type() == QEvent::WindowTitleChange || e->type() == QEvent::WindowIconChange )
1210       emit captionChanged( this );
1211
1212     if ( !e->spontaneous() && e->type() == QEvent::ShowToParent )
1213       emit shown( this );
1214
1215     if ( !e->spontaneous() && e->type() == QEvent::HideToParent )
1216       emit hidden( this );
1217
1218     if ( e->type() == QEvent::FocusIn )
1219       emit activated( this );
1220   }
1221   return QWidget::eventFilter( o, e );
1222 }
1223
1224 /*!
1225   \brief Called when child widget is destroyed.
1226   \param obj child widget being destroyed
1227 */
1228 void QtxWorkstackChild::onDestroyed( QObject* obj )
1229 {
1230   if ( obj != widget() )
1231     return;
1232
1233   myWidget = 0;
1234   deleteLater();
1235 }
1236
1237 /*!
1238   \brief Customize child event handler.
1239   \param e child event
1240 */
1241 void QtxWorkstackChild::childEvent( QChildEvent* e )
1242 {
1243   if ( e->removed() && e->child() == widget() )
1244   {
1245     myWidget = 0;
1246     deleteLater();
1247   }
1248   QWidget::childEvent( e );
1249 }
1250
1251 /*!
1252   \fn void QtxWorkstackChild::shown( QtxWorkstackChild* w )
1253   \brief Emitted when child widget is shown.
1254   \param w child widget container
1255 */
1256
1257 /*!
1258   \fn void QtxWorkstackChild::hidden( QtxWorkstackChild* w )
1259   \brief Emitted when child widget is hidden.
1260   \param w child widget container
1261 */
1262
1263 /*!
1264   \fn void QtxWorkstackChild::activated( QtxWorkstackChild* w )
1265   \brief Emitted when child widget is activated.
1266   \param w child widget container
1267 */
1268
1269 /*!
1270   \fn void QtxWorkstackChild::captionChanged( QtxWorkstackChild* w )
1271   \brief Emitted when child widget's title is changed.
1272   \param w child widget container
1273 */
1274
1275 /*!
1276   \class QtxWorkstackTabBar
1277   \internal
1278   \brief Workstack tab bar widget
1279 */
1280
1281 /*!
1282   \brief Constructor.
1283   \param parent parent widget
1284 */
1285 QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
1286 : QTabBar( parent ),
1287   myId( -1 )
1288 {
1289   setDrawBase( true );
1290   setElideMode( Qt::ElideNone );
1291
1292   connect( this, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
1293 }
1294
1295 /*!
1296   \brief Destructor.
1297 */
1298 QtxWorkstackTabBar::~QtxWorkstackTabBar()
1299 {
1300 }
1301
1302 /*!
1303   \brief Get tab page identifier.
1304   \param index tab page index
1305   \return tab page ID or -1 if \a index is out of range
1306 */
1307 int QtxWorkstackTabBar::tabId( const int index ) const
1308 {
1309   QVariant v = tabData( index );
1310   if ( !v.canConvert( QVariant::Int ) )
1311     return -1;
1312   return v.toInt();
1313 }
1314
1315 /*!
1316   \brief Set tab page identifier.
1317   \param index tab page index
1318   \param id tab page ID
1319 */
1320 void QtxWorkstackTabBar::setTabId( const int index, const int id )
1321 {
1322   setTabData( index, id );
1323 }
1324
1325 /*!
1326   \brief Get tab page index by specified identifier.
1327   \param id tab page ID
1328   \return tab page index or -1 if not found
1329 */
1330 int QtxWorkstackTabBar::indexOf( const int id ) const
1331 {
1332   int index = -1;
1333   for ( int i = 0; i < (int)count() && index < 0; i++ )
1334   {
1335     if ( tabId( i ) == id )
1336       index = i;
1337   }
1338   return index;
1339 }
1340
1341 /*!
1342   \brief Check if the tab bar is active.
1343   \return \c true if tab bar is active
1344 */
1345 bool QtxWorkstackTabBar::isActive() const
1346 {
1347   return myActive;
1348 }
1349
1350 /*!
1351   \brief Set tab bar active/inactive.
1352   \param on new active state
1353 */
1354 void QtxWorkstackTabBar::setActive( const bool on )
1355 {
1356   if ( myActive == on )
1357     return;
1358
1359   myActive = on;
1360   updateActiveState();
1361 }
1362
1363 /*!
1364   \brief Update tab bar according to the 'active' state.
1365 */
1366 void QtxWorkstackTabBar::updateActiveState()
1367 {
1368   QColor bc = palette().color( QPalette::Text );
1369   QColor ac = isActive() ? palette().color( QPalette::Highlight ) : bc;
1370   for ( int i = 0; i < (int)count(); i++ )
1371     setTabTextColor( i, currentIndex() == i ? ac : bc );
1372 }
1373
1374 /*!
1375   \brief Called when current tab page is changed.
1376   \param idx tab page index (not used)
1377 */
1378 void QtxWorkstackTabBar::onCurrentChanged( int /*index*/ )
1379 {
1380   updateActiveState();
1381 }
1382
1383 /*!
1384   \brief Customize mouse move event handler.
1385   \param e mouse event
1386 */
1387 void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
1388 {
1389   if ( myId != -1 && !tabRect( indexOf( myId ) ).contains( e->pos() ) )
1390   {
1391     myId = -1;
1392     emit dragActiveTab();
1393   }
1394
1395   QTabBar::mouseMoveEvent( e );
1396 }
1397
1398 /*!
1399   \brief Customize mouse press event handler.
1400   \param e mouse event
1401 */
1402 void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
1403 {
1404   QTabBar::mousePressEvent( e );
1405
1406   if ( e->button() == Qt::LeftButton )
1407     myId = tabId( currentIndex() );
1408 }
1409
1410 /*!
1411   \brief Customize mouse release event handler.
1412   \param e mouse event
1413 */
1414 void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
1415 {
1416   QTabBar::mouseReleaseEvent( e );
1417
1418   myId = -1;
1419
1420   if ( e->button() == Qt::RightButton )
1421     emit contextMenuRequested( e->globalPos() );
1422 }
1423
1424 /*!
1425   \brief Customize context menu event handler.
1426   \param e context menu event
1427 */
1428 void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
1429 {
1430   if ( e->reason() != QContextMenuEvent::Mouse )
1431     emit contextMenuRequested( e->globalPos() );
1432 }
1433
1434 /*!
1435   \brief Process widget change state events (style, palette, enable state changing, etc).
1436   \param e change event (not used)
1437 */
1438 void QtxWorkstackTabBar::changeEvent( QEvent* /*e*/ )
1439 {
1440   updateActiveState();
1441 }
1442
1443 /*
1444 void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
1445 {
1446   if ( currentTab() != t->identifier() )
1447   {
1448     QFont fnt = p->font();
1449     fnt.setUnderline( false );
1450     p->setFont( fnt );
1451   }
1452   QTabBar::paintLabel( p, br, t, has_focus );
1453 }
1454 */
1455
1456 /*!
1457   \fn void QtxWorkstackTabBar::dragActiveTab()
1458   \brief Emitted when dragging operation is started.
1459 */
1460
1461 /*!
1462   \fn void QtxWorkstackTabBar::contextMenuRequested( QPoint p )
1463   \brief Emitted when context popup menu is requested.
1464   \param p point popup menu to be shown at
1465 */
1466
1467 /*!
1468   \class QtxWorkstack
1469   \brief Workstack widget.
1470
1471   Organizes the child widgets in the tabbed space.
1472   Allows splitting the working area to arrange the child widgets in 
1473   arbitrary way. Any widgets can be moved to another working area with
1474   drag-n-drop operation.
1475
1476   This widget can be used as workspace of the application main window, 
1477   for example, as kind of implementation of multi-document interface.
1478 */
1479
1480 /*!
1481   \brief Constructor.
1482   \param parent parent widget
1483 */
1484 QtxWorkstack::QtxWorkstack( QWidget* parent )
1485 : QWidget( parent ),
1486   myWin( 0 ),
1487   myArea( 0 ),
1488   myWorkWin( 0 ),
1489   myWorkArea( 0 )
1490 {
1491   myActionsMap.insert( SplitVertical,   new QtxAction( QString(), tr( "Split vertically" ), 0, this ) );
1492   myActionsMap.insert( SplitHorizontal, new QtxAction( QString(), tr( "Split horizontally" ), 0, this ) );
1493   myActionsMap.insert( Close,           new QtxAction( QString(), tr( "Close" ), 0, this ) );
1494   myActionsMap.insert( Rename,          new QtxAction( QString(), tr( "Rename" ), 0, this ) );
1495
1496   connect( myActionsMap[SplitVertical], SIGNAL( triggered( bool ) ), this, SLOT( splitVertical() ) );
1497   connect( myActionsMap[SplitHorizontal], SIGNAL( triggered( bool ) ), this, SLOT( splitHorizontal() ) );
1498   connect( myActionsMap[Close], SIGNAL( triggered( bool ) ), this, SLOT( onCloseWindow() ) );
1499   connect( myActionsMap[Rename], SIGNAL( triggered( bool ) ), this, SLOT( onRename() ) );
1500
1501   // Action shortcut will work when action added in any widget.
1502   for ( QMap<int, QAction*>::iterator it = myActionsMap.begin(); it != myActionsMap.end(); ++it )
1503   {
1504     addAction( it.value() );
1505     it.value()->setShortcutContext( Qt::ApplicationShortcut );
1506   }
1507
1508   QVBoxLayout* base = new QVBoxLayout( this );
1509   base->setMargin( 0 );
1510
1511   mySplit = new QSplitter( this );
1512   mySplit->setChildrenCollapsible( false );
1513   base->addWidget( mySplit );
1514 }
1515
1516 /*!
1517   \brief Destructor.
1518 */
1519 QtxWorkstack::~QtxWorkstack()
1520 {
1521 }
1522
1523 /*!
1524   \brief Get list of all widgets in all areas or in specified area which given 
1525          widget belongs to
1526   \param wid widget specifying area if it is equal to null when widgets of all 
1527          areas are retuned
1528   \return list of widgets
1529 */
1530 QWidgetList QtxWorkstack::windowList( QWidget* wid ) const
1531 {
1532   QList<QtxWorkstackArea*> lst;
1533   if ( !wid )
1534   {
1535     areas( mySplit, lst, true );
1536   }
1537   else 
1538   {
1539     QtxWorkstackArea* area = wgArea( wid );
1540     if ( area )
1541       lst.append( area );
1542   }
1543
1544   QWidgetList widList;
1545   for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end(); ++it )
1546   {
1547     QWidgetList wids = (*it)->widgetList();
1548     for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
1549       widList.append( *itr );
1550   }
1551
1552   return widList;
1553 }
1554
1555 /*!
1556   \brief Get all child widgets in the active workarea.
1557   \return list of widgets in active workarea
1558 */
1559 QWidgetList QtxWorkstack::splitWindowList() const
1560 {
1561   return myArea ? myArea->widgetList() : QWidgetList();
1562 }
1563
1564 /*!
1565   \brief Get active widget.
1566   \return active widget
1567 */
1568 QWidget* QtxWorkstack::activeWindow() const
1569 {
1570   return myWin;
1571 }
1572
1573 /*!
1574   \brief Split workstack.
1575
1576   Splitting is possible only if there are two or more widgets in the workarea.
1577   This function splits current workarea to two new ones.
1578
1579   \param o splitting orientation (Qt::Orientation)
1580 */
1581 void QtxWorkstack::split( const int o )
1582 {
1583   QtxWorkstackArea* area = myWorkArea;
1584   if ( !area )
1585     area = activeArea();
1586   if ( !area )
1587     return;
1588
1589   if ( area->widgetList().count() < 2 )
1590     return;
1591
1592   QWidget* curWid = area->activeWidget();
1593   if ( !curWid )
1594     return;
1595
1596   QSplitter* s = splitter( area );
1597   QList<QtxWorkstackArea*> areaList;
1598   areas( s, areaList );
1599
1600   QList<QSplitter*> splitList;
1601   splitters( s, splitList );
1602
1603   QSplitter* trg = 0;
1604   if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
1605     trg = s;
1606
1607   if ( !trg )
1608     trg = wrapSplitter( area );
1609
1610   if ( !trg )
1611     return;
1612
1613   trg->setOrientation( (Qt::Orientation)o );
1614
1615   QtxWorkstackArea* newArea = createArea( 0 );
1616   trg->insertWidget( trg->indexOf( area ) + 1, newArea );
1617
1618   area->removeWidget( curWid );
1619   newArea->insertWidget( curWid );
1620
1621   distributeSpace( trg );
1622
1623   curWid->show();
1624   curWid->setFocus();
1625 }
1626
1627 /*!
1628   \brief Split workarea of the given widget on two parts.
1629
1630   Splitting is possible only if there are two or more widgets in the workarea.
1631   This function splits current workarea to two new ones.
1632
1633   \param wid widget belonging to the workstack
1634   \param o splitting orientation type (Qt::Orientation)
1635   \param type splitting type (QtxWorkstack::SplitType)
1636 */
1637 void QtxWorkstack::Split( QWidget* wid, const Qt::Orientation o, const SplitType type )
1638 {
1639   if (!wid) return;
1640
1641   // find area of the given widget
1642   QtxWorkstackArea* area = NULL;
1643   QList<QtxWorkstackArea*> allAreas;
1644   areas(mySplit, allAreas, true);
1645
1646
1647   for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
1648   {
1649     if ( (*it)->contains( wid ) )
1650       area = *it;
1651   }
1652
1653   if ( !area )
1654     return;
1655
1656   QWidgetList wids = area->widgetList();
1657   if ( wids.count() < 2 )
1658     return;
1659
1660   QSplitter* s = splitter( area );
1661   QList<QtxWorkstackArea*> areaList;
1662   areas( s, areaList );
1663
1664   QList<QSplitter*> splitList;
1665   splitters(s, splitList);
1666
1667   QSplitter* trg = 0;
1668   if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
1669     trg = s;
1670
1671   if (!trg) trg = wrapSplitter(area);
1672   if (!trg) return;
1673
1674   trg->setOrientation(o);
1675
1676   QtxWorkstackArea* newArea = createArea(0);
1677   insertWidget(newArea, trg, area);
1678
1679   switch ( type )
1680   {
1681   case SplitStay:
1682     for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
1683     {
1684       QWidget* wid_i = *itr;
1685       if ( wid_i != wid )
1686       {
1687         area->removeWidget( wid_i );
1688         newArea->insertWidget( wid_i );
1689       }
1690     }
1691     break;
1692   case SplitAt:
1693     {
1694       QWidgetList::iterator itr = wids.begin();
1695       for ( ; itr != wids.end() && *itr != wid; ++itr )
1696       {
1697       }
1698       for ( ; itr != wids.end(); ++itr )
1699       {
1700         area->removeWidget( *itr );
1701         newArea->insertWidget( *itr );
1702       }
1703     }
1704     break;
1705   case SplitMove:
1706     area->removeWidget( wid );
1707     newArea->insertWidget( wid );
1708     break;
1709   }
1710
1711   distributeSpace( trg );
1712 }
1713
1714 /*!
1715  \brief Move widget(s) from the source workarea into the target workarea
1716         or reorder widgets inside one workarea.
1717
1718  Move \a wid2 in target workarea. Put it right after \a wid1.
1719  If \a all parameter is \c true, all widgets from source workarea
1720  will be moved including \a wid2 and source workarea will be deleted then.
1721
1722  If \a wid1 and \a wid2 belongs to one workarea, widgets will be just reordered
1723  in that workarea.
1724
1725  \param wid1 widget from target workarea
1726  \param wid2 widget from source workarea
1727  \param all  if \c true, all widgets from source workarea will
1728              be moved into the target one, else only the \a wid2 will be moved
1729 */
1730 void QtxWorkstack::Attract( QWidget* wid1, QWidget* wid2, const bool all )
1731 {
1732   if ( !wid1 || !wid2 )
1733     return;
1734
1735   // find area of the widgets
1736   QtxWorkstackArea *area1 = 0, *area2 = 0;
1737   QList<QtxWorkstackArea*> allAreas;
1738   areas( mySplit, allAreas, true );
1739   for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !( area1 && area2 ); ++it )
1740   {
1741     if ( (*it)->contains( wid1 ) )
1742       area1 = *it;
1743
1744     if ( (*it)->contains( wid2 ) )
1745       area2 = *it;
1746   }
1747
1748   if ( !area1 || !area2 )
1749     return;
1750
1751   QWidget* curWid = area1->activeWidget();
1752   if ( !curWid )
1753     return;
1754
1755   if ( area1 == area2 )
1756   {
1757     if ( all )
1758     {
1759       // Set wid1 at first position, wid2 at second
1760       area1->insertWidget( wid1 );
1761       area1->insertWidget( wid2, 1 );
1762     }
1763     else
1764     {
1765       // Set wid2 right after wid1
1766       area1->removeWidget( wid2 );
1767       int wid1_ind = 0;
1768       QWidgetList wids1 = area1->widgetList();
1769       for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
1770       area1->insertWidget( wid2, wid1_ind + 1 );
1771     }
1772   }
1773   else
1774   {
1775     int wid1_ind = 0;
1776     QWidgetList wids1 = area1->widgetList();
1777     for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
1778     if ( all )
1779     {
1780       // Set wid2 right after wid1, other widgets from area2 right after wid2
1781       QWidgetList wids2 = area2->widgetList();
1782       QWidgetList::iterator itr2 = wids2.begin();
1783       for ( int ind = wid1_ind + 1; itr2 != wids2.end(); ++itr2, ++ind )
1784       {
1785         area2->removeWidget( *itr2 );
1786         if ( *itr2 == wid2 )
1787           area1->insertWidget( *itr2, wid1_ind + 1 );
1788         else
1789           area1->insertWidget( *itr2, ind );
1790       }
1791     }
1792     else
1793     {
1794       // Set wid2 right after wid1
1795       area2->removeWidget( wid2 );
1796       area1->insertWidget( wid2, wid1_ind + 1 );
1797     }
1798   }
1799
1800   area1->setActiveWidget( curWid );
1801 }
1802
1803 /*!
1804   \brief Calculate sizes of the splitter widget for the workarea.
1805   \internal
1806 */
1807 static void setSizes (QIntList& szList, const int item_ind,
1808                       const int new_near, const int new_this, const int new_farr)
1809 {
1810   // set size to all items before an item # <item_ind>
1811   int cur_pos = 0;
1812   QIntList::iterator its = szList.begin();
1813   for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
1814     *its = new_near;
1815   }
1816   if (its == szList.end()) return;
1817   // set size to item # <item_ind>
1818   *its = new_this;
1819   ++its;
1820   // set size to all items after an item # <item_ind>
1821   for (; its != szList.end(); ++its) {
1822     *its = new_farr;
1823   }
1824 }
1825
1826 /*!
1827   \brief Set position of the widget relatively to its parent splitter.
1828
1829   Orientation of positioning will correspond to the splitter orientation.
1830
1831   \param wid widget
1832   \param pos position relatively to the splitter; value in the range [0..1]
1833 */
1834 void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
1835 {
1836   if ( position < 0.0 || 1.0 < position)
1837     return;
1838
1839   if ( !wid )
1840     return;
1841
1842   // find area of the given widget
1843   QtxWorkstackArea* area = NULL;
1844   QList<QtxWorkstackArea*> allAreas;
1845   areas( mySplit, allAreas, true );
1846   for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
1847   {
1848     if ( (*it)->contains( wid ) )
1849       area = *it;
1850   }
1851
1852   if ( !area )
1853     return;
1854
1855   QSplitter* split = splitter( area );
1856   if ( !split )
1857     return;
1858
1859   // find index of the area in its splitter
1860   int item_ind = -1;
1861   bool isFound = false;
1862   const QObjectList& was = split->children();
1863   for ( QObjectList::const_iterator ito = was.begin(); ito != was.end() && !isFound; ++ito, ++item_ind )
1864   {
1865     if ( *ito == area )
1866       isFound = true;
1867   }
1868
1869   if ( !isFound || item_ind == 0 )
1870     return;
1871
1872   QIntList szList = split->sizes();
1873   int splitter_size = ( split->orientation() == Qt::Horizontal ? split->width() : split->height());
1874   int nb = szList.count();
1875
1876   int new_prev = int( splitter_size * position / item_ind );
1877   if (nb == item_ind) return;
1878   int new_next = int( splitter_size * ( 1.0 - position ) / ( nb - item_ind ) );
1879   setSizes( szList, item_ind, new_prev, new_next, new_next );
1880   split->setSizes( szList );
1881 }
1882
1883 /*!
1884   \brief Set position of the widget relatively to the entire workstack.
1885
1886   If \a o is \c Qt::Horizontal, the horizontal position of \a wid will be changed.
1887   If \a o is \c Qt::Vertical, the vertical position of \a wid will be changed.
1888
1889   \param wid widget
1890   \param o   orientation of positioning (\c Qt::Horizontal or \c Qt::Vertical)
1891   \param pos position relatively to the workstack; value in range [0..1]
1892 */
1893 void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
1894                                         const double position )
1895 {
1896   if ( position < 0.0 || 1.0 < position )
1897     return;
1898
1899   if ( !wid )
1900     return;
1901
1902   int splitter_size = o == Qt::Horizontal ? mySplit->width() : mySplit->height();
1903   int need_pos = int( position * splitter_size );
1904   int splitter_pos = 0;
1905
1906   if ( setPosition( wid, mySplit, o, need_pos, splitter_pos ) != 0 )
1907   {
1908     // impossible to set required position
1909   }
1910 }
1911
1912 /*!
1913   \brief Set accelerator key-combination for the action with specified \a id.
1914   \param id action ID
1915   \param accel action accelerator
1916 */
1917 void QtxWorkstack::setAccel( const int id, const int accel )
1918 {
1919   if ( !myActionsMap.contains( id ) )
1920     return;
1921
1922   myActionsMap[id]->setShortcut( accel );
1923 }
1924
1925 /*!
1926   \brief Get the action's accelerator key-combination.
1927   \param id action ID
1928   \return action accelerator
1929 */
1930 int QtxWorkstack::accel( const int id ) const
1931 {
1932   int res = 0;
1933   if ( myActionsMap.contains( id ) )
1934     res = myActionsMap[id]->shortcut();
1935   return res;
1936 }
1937
1938 /*!
1939   \brief Get icon for the specified action.
1940
1941   If \a id is invalid, null icon is returned.
1942
1943   \param id menu action ID
1944   \return menu item icon
1945 */
1946 QIcon QtxWorkstack::icon( const int id ) const
1947 {
1948   QIcon ico;
1949   if ( myActionsMap.contains( id ) )
1950     ico = myActionsMap[id]->icon();
1951   return ico;
1952 }
1953
1954 /*!
1955   \brief Set menu item icon for the specified action.
1956   \param id menu action ID
1957   \param ico new menu item icon
1958 */
1959 void QtxWorkstack::setIcon( const int id, const QIcon& icon )
1960 {
1961   if ( !myActionsMap.contains( id ) )
1962     return;
1963
1964   myActionsMap[id]->setIcon( icon );
1965 }
1966
1967 /*!
1968   \brief Set actions to be visible in the context popup menu.
1969   
1970   Actions, which IDs are set in \a flags parameter, will be shown in the 
1971   context popup menu. Other actions will not be shown.
1972
1973   \param flags ORed together actions flags
1974 */
1975 void QtxWorkstack::setMenuActions( const int flags )
1976 {
1977   myActionsMap[SplitVertical]->setVisible( flags & SplitVertical );
1978   myActionsMap[SplitHorizontal]->setVisible( flags & SplitHorizontal );
1979   myActionsMap[Close]->setVisible( flags & Close );
1980   myActionsMap[Rename]->setVisible( flags & Rename );
1981 }
1982
1983 /*!
1984   \brief Set actions to be visible in the context popup menu.
1985   
1986   Actions, which IDs are set in \a flags parameter, will be shown in the 
1987   context popup menu. Other actions will not be shown.
1988
1989   \param flags ORed together actions flags
1990 */
1991 int QtxWorkstack::menuActions() const
1992 {
1993   int ret = 0;
1994   ret = ret | ( myActionsMap[SplitVertical]->isVisible() ? SplitVertical : 0 );
1995   ret = ret | ( myActionsMap[SplitHorizontal]->isVisible() ? SplitHorizontal : 0 );
1996   ret = ret | ( myActionsMap[Close]->isVisible() ? Close : 0 );
1997   ret = ret | ( myActionsMap[Rename]->isVisible() ? Rename : 0 );
1998   return ret;
1999 }
2000
2001 /*!
2002   \brief Calculate sizes of the splitter widget for the workarea.
2003   \internal
2004 */
2005 static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
2006                            const int item_ind, const int item_rel_pos,
2007                            const int need_pos, const int splitter_pos)
2008 {
2009   if (item_ind == 0) { // cannot move in this splitter
2010     return (need_pos - splitter_pos);
2011   }
2012
2013   int delta = 0;
2014   int new_prev = 0;
2015   int new_this = szList[item_ind];
2016   int new_next = 0;
2017
2018   bool isToCheck = false;
2019
2020   if (need_pos < splitter_pos) {
2021     // Set size of all previous workareas to zero <--
2022     if (item_ind == nb - 1) {
2023       // item iz last in the splitter, it will occupy all the splitter
2024       new_this = splitter_size;
2025     } else {
2026       // recompute size of next items in splitter
2027       new_next = (splitter_size - new_this) / (nb - item_ind - 1);
2028     }
2029     delta = need_pos - splitter_pos;
2030
2031   } else if (need_pos > (splitter_pos + splitter_size)) {
2032     // Set size of all next workareas to zero -->
2033     // recompute size of previous items in splitter
2034     new_this = 0;
2035     new_prev = (splitter_size - new_this) / item_ind;
2036     delta = need_pos - (splitter_pos + splitter_size - new_this);
2037
2038   } else { // required position lays inside this splitter
2039     // Move workarea inside splitter into required position <->
2040     int new_item_rel_pos = need_pos - splitter_pos;
2041     new_prev = new_item_rel_pos / item_ind;
2042     if (need_pos < (splitter_pos + item_rel_pos)) {
2043       // Make previous workareas smaller, next - bigger
2044       // No problem to keep old size of the widget
2045     } else {
2046       // Make previous workareas bigger, next - smaller
2047       if (new_this > splitter_size - new_item_rel_pos) {
2048         new_this = splitter_size - new_item_rel_pos;
2049       }
2050       // jfa to do: in this case fixed size of next widgets could prevent right resizing
2051       isToCheck = true;
2052     }
2053     if (item_ind == nb - 1) {
2054       new_this = splitter_size - new_item_rel_pos;
2055     } else {
2056       new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
2057     }
2058     delta = 0;
2059   }
2060
2061   setSizes (szList, item_ind, new_prev, new_this, new_next);
2062   return delta;
2063 }
2064
2065 /*!
2066   \brief Set position of the widget.
2067
2068   Called from SetRelativePosition() public method.
2069
2070   \param wid   widget to be moved
2071   \param split currently processed splitter (goes from more common
2072                to more particular splitter in recursion calls)
2073   \param o     orientation of positioning
2074   \param need_pos required position of the given widget in pixels
2075                (from top/left side of workstack area)
2076   \param splitter_pos position of the splitter \a split
2077                (from top/left side of workstack area)
2078   \return difference between a required and a distinguished position
2079 */
2080 int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
2081                                const int need_pos, const int splitter_pos )
2082 {
2083   if ( !wid || !split )
2084     return need_pos - splitter_pos;
2085
2086   // Find corresponding sub-splitter.
2087   // Find also index of appropriate item in current splitter.
2088   int cur_ind = 0, item_ind = 0;
2089   bool isBottom = false, isFound = false;
2090   QSplitter* sub_split = NULL;
2091   const QObjectList& objs = split->children();
2092   for ( QObjectList::const_iterator it = objs.begin(); it != objs.end() && !isFound; ++it )
2093   {
2094     QtxWorkstackArea* area = ::qobject_cast<QtxWorkstackArea*>( *it );
2095     if ( area )
2096     {
2097       if ( area->contains( wid ) )
2098       {
2099         item_ind = cur_ind;
2100         isBottom = true;
2101         isFound = true;
2102       }
2103       cur_ind++;
2104     }
2105     else if ( (*it)->inherits( "QSplitter" ) )
2106     {
2107       QList<QtxWorkstackArea*> areaList;
2108       areas( (QSplitter*)(*it), areaList, true );
2109       for ( QList<QtxWorkstackArea*>::iterator ita = areaList.begin(); ita != areaList.end() && !isFound; ++ita )
2110       {
2111         if ( (*ita)->contains( wid ) )
2112         {
2113           item_ind = cur_ind;
2114           isFound = true;
2115           sub_split = (QSplitter*)*it;
2116         }
2117       }
2118       cur_ind++;
2119     }
2120   }
2121
2122   if ( !isFound )
2123     return ( need_pos - splitter_pos );
2124
2125   if ( split->orientation() == o )
2126   {
2127     // Find coordinates of near and far sides of the appropriate item relatively current splitter
2128     int splitter_size = ( o == Qt::Horizontal ? split->width() : split->height() );
2129     QIntList szList = split->sizes();
2130     int nb = szList.count();
2131     int item_rel_pos = 0; // position of near side of item relatively this splitter
2132     for (int i = 0; i < item_ind; i++) {
2133       item_rel_pos += szList[i];
2134     }
2135     int item_size = szList[item_ind]; // size of item
2136     int item_pos = splitter_pos + item_rel_pos;
2137
2138     // Resize splitter items to complete the conditions
2139     if (isBottom) {
2140       // I. Bottom of splitters stack reached
2141
2142       int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
2143       split->setSizes(szList);
2144       // Recompute delta, as some windows can reject given size
2145       int new_item_rel_pos = 0;
2146       QIntList szList1 = split->sizes();
2147       for (int i = 0; i < item_ind; i++) {
2148         new_item_rel_pos += szList1[i];
2149       }
2150       delta = need_pos - (splitter_pos + new_item_rel_pos);
2151       return delta;
2152
2153     } else {
2154       // II. Bottom of splitters stack is not yet reached
2155
2156       if (item_ind == 0) { // cannot move in this splitter
2157         // Process in sub-splitter
2158         return setPosition(wid, sub_split, o, need_pos, splitter_pos);
2159       }
2160
2161       int new_prev = 0;
2162       int new_this = szList[item_ind];
2163       int new_next = 0;
2164
2165       if (need_pos < splitter_pos) {
2166         // Set size of all previous workareas to zero <--
2167         if (item_ind == nb - 1) {
2168           new_this = splitter_size;
2169         } else {
2170           new_next = (splitter_size - new_this) / (nb - item_ind - 1);
2171         }
2172         setSizes (szList, item_ind, new_prev, new_this, new_next);
2173         split->setSizes(szList);
2174         // Recompute splitter_pos, as some windows can reject given size
2175         int new_item_rel_pos = 0;
2176         QIntList szList1 = split->sizes();
2177         for (int i = 0; i < item_ind; i++) {
2178           new_item_rel_pos += szList1[i];
2179         }
2180         // Process in sub-splitter
2181         return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2182       } else if (need_pos > (splitter_pos + splitter_size)) {
2183         // Set size of all next workareas to zero -->
2184         new_prev = (splitter_size - new_this) / item_ind;
2185         setSizes (szList, item_ind, new_prev, new_this, new_next);
2186         split->setSizes(szList);
2187         // Recompute splitter_pos, as some windows can reject given size
2188         int new_item_rel_pos = 0;
2189         QIntList szList1 = split->sizes();
2190         for (int i = 0; i < item_ind; i++) {
2191           new_item_rel_pos += szList1[i];
2192         }
2193         // Process in sub-splitter
2194         return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2195       } else {
2196         // Set appropriate size of all previous/next items <->
2197         int new_item_rel_pos = item_rel_pos;
2198         if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
2199           // Move item inside splitter into required position <->
2200           int new_this = szList[item_ind];
2201           int new_next = 0;
2202           new_item_rel_pos = need_pos - splitter_pos;
2203           if ((item_pos + item_size) < need_pos) {
2204             //new_item_rel_pos = need_pos - (item_pos + item_size);
2205             new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
2206           }
2207           int new_prev = new_item_rel_pos / item_ind;
2208           if (need_pos < (splitter_pos + item_rel_pos)) {
2209             // Make previous workareas smaller, next - bigger
2210             // No problem to keep old size of the widget
2211           } else {
2212             // Make previous workareas bigger, next - smaller
2213             if (new_this > splitter_size - new_item_rel_pos) {
2214               new_this = splitter_size - new_item_rel_pos;
2215             }
2216           }
2217           if (item_ind == nb - 1) {
2218             new_this = splitter_size - new_item_rel_pos;
2219           } else {
2220             new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
2221           }
2222           setSizes (szList, item_ind, new_prev, new_this, new_next);
2223           split->setSizes(szList);
2224           // Recompute new_item_rel_pos, as some windows can reject given size
2225           new_item_rel_pos = 0;
2226           QIntList szList1 = split->sizes();
2227           for (int i = 0; i < item_ind; i++) {
2228             new_item_rel_pos += szList1[i];
2229           }
2230         } else {
2231           // Do nothing
2232         }
2233         // Process in sub-splitter
2234         int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2235         if (add_pos == 0)
2236           return 0;
2237
2238         // this can be if corresponding workarea is first in sub-splitter
2239         // or sub-splitter has another orientation
2240
2241         // Resize ones again to reach precize position <->
2242         int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
2243
2244         // Move workarea inside splitter into required position <->
2245         int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
2246                                      new_item_rel_pos, need_pos_1, splitter_pos);
2247         split->setSizes(szList);
2248         // Recompute new_item_rel_pos, as some windows can reject given size
2249         new_item_rel_pos = 0;
2250         QIntList szList1 = split->sizes();
2251         for (int i = 0; i < item_ind; i++) {
2252           new_item_rel_pos += szList1[i];
2253         }
2254         delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
2255         return delta_1;
2256       }
2257     }
2258   } else {
2259     return setPosition(wid, sub_split, o, need_pos, splitter_pos);
2260   }
2261
2262   return 0;
2263 }
2264
2265 /*!
2266   \brief Redistribute space among widgets equally.
2267   \param split splitter
2268 */
2269 void QtxWorkstack::distributeSpace( QSplitter* split ) const
2270 {
2271   if ( !split )
2272     return;
2273
2274   QIntList szList = split->sizes();
2275   int size = ( split->orientation() == Qt::Horizontal ?
2276                split->width() : split->height() ) / szList.count();
2277   for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
2278     *it = size;
2279   split->setSizes( szList );
2280 }
2281
2282 /*!
2283   \brief Split widgets vertically.
2284 */
2285 void QtxWorkstack::splitVertical()
2286 {
2287   split( Qt::Horizontal );
2288 }
2289
2290 /*!
2291   \brief Split widgets horizontally.
2292 */
2293 void QtxWorkstack::splitHorizontal()
2294 {
2295   split( Qt::Vertical );
2296 }
2297
2298 /*!
2299   \brief Called when user activates "Rename" menu item.
2300
2301   Changes widget title.
2302 */
2303 void QtxWorkstack::onRename()
2304 {
2305   if ( !myWorkWin )
2306     return;
2307
2308   bool ok = false;
2309   QString newName = QInputDialog::getText( topLevelWidget(),  tr( "Rename" ), tr( "Enter new name:" ),
2310                                            QLineEdit::Normal, myWorkWin->windowTitle(), &ok );
2311   if ( ok && !newName.isEmpty() )
2312     myWorkWin->setWindowTitle( newName );
2313 }
2314
2315 /*!
2316   \brief Wrap area into the new splitter.
2317   \param workarea
2318   \return new splitter
2319 */
2320 QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
2321 {
2322   if ( !area )
2323     return 0;
2324
2325   QSplitter* pSplit = splitter( area );
2326   if ( !pSplit )
2327     return 0;
2328
2329   bool upd = pSplit->updatesEnabled();
2330   pSplit->setUpdatesEnabled( false );
2331
2332   QIntList szList = pSplit->sizes();
2333
2334   QSplitter* wrap = new QSplitter( 0 );
2335   wrap->setChildrenCollapsible( false );
2336   pSplit->insertWidget( pSplit->indexOf( area ) + 1, wrap );
2337   wrap->setVisible( true );
2338   wrap->addWidget( area );
2339
2340   pSplit->setSizes( szList );
2341
2342   pSplit->setUpdatesEnabled( upd );
2343
2344   return wrap;
2345 }
2346
2347 /*!
2348   \brief Reparent and add widget.
2349   \param wid widget
2350   \param pWid parent widget
2351   \param after widget after which \a wid should be added
2352 */
2353 void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
2354 {
2355   if ( !wid || !pWid )
2356     return;
2357
2358   QWidgetList moveList;
2359   const QObjectList& lst = pWid->children();
2360   bool found = false;
2361   for ( QObjectList::const_iterator it = lst.begin(); it != lst.end(); ++it )
2362   {
2363     if ( found && ( (*it)->inherits( "QSplitter" ) ||
2364                     (*it)->inherits( "QtxWorkstackArea" ) ) )
2365       moveList.append( (QWidget*)(*it) );
2366     if ( *it == after )
2367       found = true;
2368   }
2369
2370   QMap<QWidget*, bool> map;
2371   for ( QWidgetList::iterator it = moveList.begin(); it != moveList.end(); ++it )
2372   {
2373     map.insert( *it, (*it)->isVisibleTo( (*it)->parentWidget() ) );
2374     (*it)->setParent( 0 );
2375     (*it)->hide();
2376   }
2377
2378   wid->setParent( pWid );
2379
2380   for ( QWidgetList::iterator itr = moveList.begin(); itr != moveList.end(); ++itr )
2381   {
2382     (*itr)->setParent( pWid );
2383     (*itr)->setShown( map.contains( *itr ) ? map[*itr] : false );
2384   }
2385 }
2386
2387 /*!
2388   \brief Close active window.
2389 */
2390 void QtxWorkstack::onCloseWindow()
2391 {
2392   if ( myWorkWin )
2393     myWorkWin->close();
2394   else if( activeWindow() )
2395     activeWindow()->close();
2396 }
2397
2398 /*!
2399   \brief Called when workarea is destroyed.
2400
2401   Set input focus to the neighbour area.
2402
2403   \param obj workarea being destroyed
2404 */
2405 void QtxWorkstack::onDestroyed( QObject* obj )
2406 {
2407   QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
2408
2409   if ( area == myArea )
2410     myArea = 0;
2411
2412   if ( !myArea )
2413   {
2414     QtxWorkstackArea* cur = neighbourArea( area );
2415     if ( cur )
2416       cur->setFocus();
2417   }
2418
2419   QApplication::postEvent( this, new QEvent( QEvent::User ) );
2420 }
2421
2422 /*!
2423   \brief Called on window activating.
2424   \param area workarea being activated (not used)
2425 */
2426 void QtxWorkstack::onWindowActivated( QWidget* /*area*/ )
2427 {
2428   const QObject* obj = sender();
2429   if ( !obj->inherits( "QtxWorkstackArea" ) )
2430     return;
2431
2432   setActiveArea( (QtxWorkstackArea*)obj );
2433 }
2434
2435 /*!
2436   \brief Called on window deactivating.
2437   \param area workarea being deactivated
2438 */
2439 void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
2440 {
2441   if ( myArea != area )
2442     return;
2443
2444   QList<QtxWorkstackArea*> lst;
2445   areas( mySplit, lst, true );
2446
2447   int idx = lst.indexOf( area );
2448   if ( idx == -1 )
2449     return;
2450
2451   myWin = 0;
2452   myArea = 0;
2453
2454   QtxWorkstackArea* newArea = neighbourArea( area );
2455   if ( newArea && newArea->activeWidget() )
2456     newArea->activeWidget()->setFocus();
2457
2458   QApplication::postEvent( this, new QEvent( QEvent::User ) );
2459 }
2460
2461 /*!
2462   \brief Create and show popup menu for workarea.
2463   \param w workarea
2464   \param p popup position
2465 */
2466 void QtxWorkstack::onContextMenuRequested( QWidget* w, QPoint p )
2467 {
2468   QtxWorkstackArea* anArea = ::qobject_cast<QtxWorkstackArea*>( (QObject*)sender() );
2469   if ( !anArea )
2470     anArea = activeArea();
2471
2472   if ( !anArea )
2473     return;
2474
2475   QWidgetList lst = anArea->widgetList();
2476   if ( lst.isEmpty() )
2477     return;
2478
2479   myWorkWin = w;
2480   myWorkArea = anArea;
2481
2482   QMenu* pm = new QMenu();
2483
2484   if ( lst.count() > 1 )
2485   {
2486     if ( myActionsMap[SplitVertical]->isEnabled() )
2487       pm->addAction( myActionsMap[SplitVertical] );
2488     if ( myActionsMap[SplitHorizontal]->isEnabled() )
2489       pm->addAction( myActionsMap[SplitHorizontal] );
2490     pm->addSeparator();
2491   }
2492
2493   if ( w )
2494   {
2495     if ( myActionsMap[Close]->isEnabled() )
2496       pm->addAction( myActionsMap[Close] );
2497     if ( myActionsMap[Rename]->isEnabled() )
2498       pm->addAction( myActionsMap[Rename] );
2499   }
2500
2501   Qtx::simplifySeparators( pm );
2502
2503   if ( !pm->actions().isEmpty() )
2504     pm->exec( p );
2505
2506   delete pm;
2507
2508   myWorkWin = 0;
2509   myWorkArea = 0;
2510 }
2511
2512 /*!
2513   \brief Add child widget.
2514   \param w widget
2515   \param f widget flags
2516   \return child widget container
2517 */
2518 QWidget* QtxWorkstack::addWindow( QWidget* w, Qt::WindowFlags f )
2519 {
2520   if ( !w )
2521     return 0;
2522
2523   return targetArea()->insertWidget( w, -1, f );
2524 }
2525
2526 /*!
2527   \brief Handle custom events.
2528   \param e custom event (not used)
2529 */
2530 void QtxWorkstack::customEvent( QEvent* /*e*/ )
2531 {
2532   updateState();
2533 }
2534
2535 /*!
2536   \brief Get splitter corresponding to the workarea.
2537   \param workarea
2538   \return splitter corresponding to the workarea
2539 */
2540 QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
2541 {
2542   if ( !area )
2543     return 0;
2544
2545   QSplitter* split = 0;
2546
2547   QWidget* wid = area->parentWidget();
2548   if ( wid && wid->inherits( "QSplitter" ) )
2549     split = (QSplitter*)wid;
2550
2551   return split;
2552 }
2553
2554 /*!
2555   \brief Get list of child splitters.
2556   \param split parent splitter
2557   \param splitList list to be filled with child splitters
2558   \param rec if \c true, perform recursive search of children
2559 */
2560 void QtxWorkstack::splitters( QSplitter* split, QList<QSplitter*>& splitList, const bool rec ) const
2561 {
2562   if ( !split )
2563     return;
2564
2565   const QObjectList& objs = split->children();
2566   for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
2567   {
2568     if ( rec )
2569       splitters( (QSplitter*)*it, splitList, rec );
2570     if ( (*it)->inherits( "QSplitter" ) )
2571       splitList.append( (QSplitter*)*it );
2572   }
2573 }
2574
2575 /*!
2576   \brief Get list of child workareas.
2577   \param split parent splitter
2578   \param areaList list to be filled with child workareas
2579   \param rec if \c true, perform recursive search of children
2580 */
2581 void QtxWorkstack::areas( QSplitter* split, QList<QtxWorkstackArea*>& areaList, const bool rec ) const
2582 {
2583   if ( !split )
2584     return;
2585
2586   const QObjectList& objs = split->children();
2587   for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
2588   {
2589     if ( (*it)->inherits( "QtxWorkstackArea" ) )
2590       areaList.append( (QtxWorkstackArea*)*it );
2591     else if ( rec && (*it)->inherits( "QSplitter" ) )
2592       areas( (QSplitter*)*it, areaList, rec );
2593   }
2594 }
2595
2596 /*!
2597   \brief Get active workarea.
2598   \return active workarea
2599 */
2600 QtxWorkstackArea* QtxWorkstack::activeArea() const
2601 {
2602   return myArea;
2603 }
2604
2605 /*!
2606   \brief Get target area (for which the current operation should be done).
2607
2608   Returns active workarea or current area (if there is no active workarea).
2609   If there are no workareas, create new workarea and return it.
2610
2611   \return workarea
2612 */
2613 QtxWorkstackArea* QtxWorkstack::targetArea()
2614 {
2615   QtxWorkstackArea* area = activeArea();
2616   if ( !area )
2617     area = currentArea();
2618   if ( !area )
2619   {
2620     QList<QtxWorkstackArea*> lst;
2621     areas( mySplit, lst );
2622     if ( !lst.isEmpty() )
2623       area = lst.first();
2624   }
2625
2626   if ( !area )
2627     area = createArea( mySplit );
2628
2629   return area;
2630 }
2631
2632 /*!
2633   \brief Get current workarea.
2634
2635   Current workarea is that one which has input focus.
2636
2637   \return current area
2638 */
2639 QtxWorkstackArea* QtxWorkstack::currentArea() const
2640 {
2641   QtxWorkstackArea* area = 0;
2642   QWidget* wid = focusWidget();
2643   while ( wid && !area )
2644   {
2645     if ( wid->inherits( "QtxWorkstackArea" ) )
2646       area = (QtxWorkstackArea*)wid;
2647     wid = wid->parentWidget();
2648   }
2649
2650   return area;
2651 }
2652
2653 /*!
2654   \brief Create new workarea.
2655   \param parent parent widget
2656   \return created workarea
2657 */
2658 QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
2659 {
2660   QtxWorkstackArea* area = new QtxWorkstackArea( parent );
2661
2662   connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
2663   connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
2664   connect( area, SIGNAL( contextMenuRequested( QWidget*, QPoint ) ),
2665            this, SLOT( onContextMenuRequested( QWidget*, QPoint ) ) );
2666   connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
2667
2668   return area;
2669 }
2670
2671 /*!
2672   \brief Set active workarea.
2673   \param workarea
2674 */
2675 void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
2676 {
2677   QWidget* oldCur = myWin;
2678
2679   QtxWorkstackArea* oldArea = myArea;
2680
2681   myArea = area;
2682
2683   if ( myArea != oldArea )
2684   {
2685     if ( oldArea )
2686       oldArea->updateActiveState();
2687     if ( myArea )
2688       myArea->updateActiveState();
2689   }
2690
2691   if ( myArea )
2692     myWin = myArea->activeWidget();
2693
2694   if ( myWin && oldCur != myWin )
2695     emit windowActivated( myWin );
2696 }
2697
2698 /*!
2699   \brief Get workarea which is nearest to \a area.
2700   \param area area for which neighbour is searched
2701   \return neighbour area (or 0 if not found)
2702 */
2703 QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
2704 {
2705   QList<QtxWorkstackArea*> lst;
2706   areas( mySplit, lst, true );
2707   int pos = lst.indexOf( area );
2708   if ( pos < 0 )
2709     return 0;
2710
2711   QtxWorkstackArea* na = 0;
2712   for ( int i = pos - 1; i >= 0 && !na; i-- )
2713   {
2714     if ( !lst.at( i )->isEmpty() )
2715       na = lst.at( i );
2716   }
2717
2718   for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
2719   {
2720     if ( !lst.at( j )->isEmpty() )
2721         na = lst.at( j );
2722   }
2723   return na;
2724 }
2725
2726 /*!
2727   \brief Get workarea covering point.
2728   \return workarea
2729   \param p point
2730 */
2731 QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
2732 {
2733   QtxWorkstackArea* area = 0;
2734   QList<QtxWorkstackArea*> lst;
2735   areas( mySplit, lst, true );
2736   for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end() && !area; ++it )
2737   {
2738     QtxWorkstackArea* cur = *it;
2739     QRect r = cur->geometry();
2740     if ( cur->parentWidget() )
2741       r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
2742     if ( r.contains( p ) )
2743       area = cur;
2744   }
2745   return area;
2746 }
2747
2748 /*!
2749   \brief Update internal state.
2750 */
2751 void QtxWorkstack::updateState()
2752 {
2753   updateState( mySplit );
2754 }
2755
2756 /*!
2757   \brief Update splitter state.
2758   \param split splitter to be updated
2759 */
2760 void QtxWorkstack::updateState( QSplitter* split )
2761 {
2762   QList<QSplitter*> recList;
2763   splitters( split, recList, false );
2764   for ( QList<QSplitter*>::iterator itr = recList.begin(); itr != recList.end(); ++itr )
2765     updateState( *itr );
2766
2767   QList<QSplitter*> splitList;
2768   splitters( split, splitList, false );
2769
2770   QList<QtxWorkstackArea*> areaList;
2771   areas( split, areaList, false );
2772
2773   bool vis = false;
2774   for ( QList<QtxWorkstackArea*>::iterator it = areaList.begin(); it != areaList.end(); ++it )
2775   {
2776     if ( (*it)->isEmpty() )
2777       (*it)->hide();
2778     else
2779     {
2780       (*it)->show();
2781       vis = true;
2782     }
2783   }
2784
2785   if ( split == mySplit )
2786     return;
2787
2788   for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end() && !vis; ++iter )
2789     vis = (*iter)->isVisibleTo( (*iter)->parentWidget() );
2790
2791   if ( areaList.isEmpty() && splitList.isEmpty() )
2792     delete split;
2793   else if ( vis )
2794     split->show();
2795   else
2796     split->hide();
2797 }
2798
2799 /*!
2800   \brief Get splitter info (for debug purposes)
2801   \param split splitter
2802   \param info string to be filled with splitter data.
2803 */
2804 void QtxWorkstack::splitterInfo( QSplitter* split, QString& info ) const
2805 {
2806   if ( !split )
2807     return;
2808
2809   /*const QObjectList& objs = */split->children(); // VSR: is it needed ???
2810
2811   QString sizesStr;
2812   QList<int> sizes = split->sizes();
2813   for ( QList<int>::iterator sIt = sizes.begin(); sIt != sizes.end(); ++sIt )
2814   {
2815     if ( *sIt > 1 ) // size 1 pixel usually means empty Workstack area, which will NOT be re-created,
2816             sizesStr += QString( ":%1" ).arg( *sIt );  // so we don't need to store its size
2817   }
2818
2819   if ( !sizesStr.isEmpty() ) // cut the first ':'
2820     sizesStr = sizesStr.right( sizesStr.length() - 1 );
2821
2822   info += QString( "(splitter orientation=%1 sizes=%3 " ).arg( split->orientation() ).arg( sizesStr );
2823
2824   for( int index = 0, count = split->count(); index < count; index++ )
2825   {
2826     QObject* obj = split->widget( index );
2827     if ( obj->inherits( "QSplitter" ) )
2828             splitterInfo( (QSplitter*)obj, info );
2829     else if ( obj->inherits( "QtxWorkstackArea" ) )
2830     {
2831             QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
2832             if ( area->isEmpty() )
2833               continue;
2834             info += QString( "(views active='%1'" ).arg( area->activeWidget()->objectName() );
2835             QWidgetList views = area->widgetList();
2836       for ( QWidgetList::iterator wIt = views.begin(); wIt != views.end(); ++wIt )
2837               info += QString( " '%1'" ).arg( (*wIt)->objectName() );
2838             info += ')';
2839     }
2840   }
2841
2842   info += ')';
2843   printf( (const char*)QString( info + '\n' ).toLatin1() );
2844 }
2845
2846 /*!
2847   \brief Remove round brackets symbols from the string.
2848   \internal
2849   \param parameters string to be processed
2850 */
2851 static void cutBrackets( QString& parameters )
2852 {
2853   QChar c1 = parameters[0];
2854   QChar c2 = parameters[int(parameters.length()-1)];
2855   if ( !parameters.isEmpty() && c1 == '(' && c2 == ')' )
2856     parameters = parameters.mid( 1, parameters.length()-2 );
2857 }
2858
2859 /*!
2860   \brief Parse string to get some parameter value.
2861   \internal
2862
2863   String \a str can contain the parameters description of kind "<param>=<value> ...".
2864   For example:
2865   \code
2866   QString s = "splitter orientation=0 children=2 sizes=332:478";
2867   QString orient_val = getValue( s, "children" ); // orient_val contains "2"
2868   QString size_val   = getValue( s, "sizes" );    // val contains "332:478"
2869   \endcode
2870
2871   \param str string to be processed
2872   \param valName parameter name
2873   \return parameter value (or null QStrinhg if parameter is not found)
2874 */
2875 static QString getValue( const QString& str, const QString& valName )
2876 {
2877   int i = str.indexOf( valName );
2878   if ( i != -1 )
2879   {
2880     int equal_i = str.indexOf( '=', i );
2881     if ( equal_i != -1 )
2882     {
2883       int space_i = str.indexOf( ' ', ++equal_i );
2884       if ( space_i != -1 )
2885               return str.mid( equal_i, space_i - equal_i );
2886     }
2887   }
2888   return QString();
2889 }
2890
2891 /*!
2892   \brief Check format of splitter parameters string.
2893   \internal
2894   \param parameters splitter parameters description
2895   \return \c true on success and \c false on error
2896 */
2897 static bool checkFormat( const QString& parameters )
2898 {
2899   QString params( parameters );
2900   // 1. begins and ends with brackets
2901   QChar c1 = params[0];
2902   QChar c2 = params[int(params.length()-1)];
2903   bool ok = ( c1 == '(' && c2 == ')' );
2904   if ( !ok ) return ok;
2905   ::cutBrackets( params );
2906   // 2. has splitter word
2907   ok = ( params.left( 8 ) == "splitter" );
2908   if ( !ok ) return ok;
2909   // 3. has children?  = '(' is found
2910   int i = params.indexOf( '(' );
2911   ok = i != -1;
2912   if ( !ok ) return ok;
2913   params = params.left( i ); // cut all children, they will be checked later
2914   // 4. has orientation word and correct value
2915   ::getValue( params, "orientation" ).toInt( &ok );
2916   if ( !ok ) return ok;
2917   // 5. has sizes word and values
2918   ok = ! ::getValue( params, "sizes" ).isEmpty();
2919   if ( !ok ) return ok;
2920   // 6. check children -> number of '(' == number of ')' in original string
2921   ok = ( parameters.contains( '(' ) == parameters.contains( ')' ) );
2922   return ok;
2923 }
2924
2925 /*!
2926   \brief Get splitter's children descriptions from the string.
2927   \internal
2928   
2929   Child widgets descriptions are separated by '(' and ')' symbols.
2930
2931   \param str string to be processed
2932   \return child widgets descriptions
2933 */
2934 static QStringList getChildren( const QString& str )
2935 {
2936   QStringList lst;
2937   if ( !str.startsWith( "(" ) )
2938     return lst;
2939
2940   int i = 1,
2941   nOpen = 1, // count brackets: '(' increments nOpen, ')' decrements
2942   start = 0;
2943   while ( i < (int)str.length() )
2944   {
2945     if ( str[i] == '(' )
2946     {
2947       nOpen++;
2948       if ( nOpen == 1 )
2949               start = i;
2950     }
2951     else if ( str[i] == ')' )
2952     {
2953       nOpen--;
2954       if ( nOpen == 0 )
2955               lst.append( str.mid( start, i-start+1 ) );
2956     }
2957     i++;
2958   }
2959
2960   return lst;
2961 }
2962
2963 /*!
2964   \brief Get view name by index.
2965   \internal
2966
2967   Example:
2968   \code
2969   QString s  = "views active='AnotherView' 'GLView' 'AnotherView'";
2970   QString a0 = getViewName( s, 0 ); // --> a0 contains "GLView"
2971   QString a1 = getViewName( s, 1 ); // --> a1 contains "AnotherView"
2972   \endcode
2973
2974   \param str string to be processed
2975   \param i index
2976   \return view name
2977 */
2978 static QString getViewName( const QString& str, int i )
2979 {
2980   QRegExp exp( "\\s'\\w+'" );
2981   int start = 0; // start index of view name in the string
2982   int num = 0 ; // index of found match
2983   while ( ( start = exp.indexIn( str, start ) ) != -1 && num < i )
2984   {
2985     start += exp.matchedLength();
2986     num ++;
2987   }
2988   if ( start != -1 )      // +2 and -3 avoid starting space and starting and ending ' symbols
2989     return str.mid( start + 2, exp.matchedLength() - 3 );
2990
2991   return QString();
2992 }
2993
2994 /*!
2995   \brief Get child widget with specified name.
2996   \internal
2997   \param parent parent widget
2998   \param aName child widget name
2999   \return child widget or 0 if not found
3000 */
3001 static QWidget* getView( const QWidget* parent, const QString& aName )
3002 {
3003   QWidget* view = 0;
3004   QList<QWidget*> l = qFindChildren<QWidget*>( parent->topLevelWidget(), aName );
3005   if ( !l.isEmpty() )
3006     view = ::qobject_cast<QWidget*>( l.first() );
3007   return view;
3008 }
3009
3010 /*!
3011   \brief Setup splitter according to the specified parameters string.
3012   \param splitter splitter to be set up
3013   \param parameters splitter parameters description
3014   \param sMap map containing resulting child splitters sizes
3015 */
3016 void QtxWorkstack::setSplitter( QSplitter* splitter, const QString& parameters, QMap<QSplitter*, QList<int> >& sMap )
3017 {
3018   printf( QString( parameters + '\n' ).toLatin1() );
3019   if ( !::checkFormat( parameters ) ) {
3020     printf( "\nInvalid format of workstack parameters.  Positions of viewers can not be restored.\n" );
3021     return;
3022   }
3023
3024   QString params( parameters );
3025   ::cutBrackets( params );
3026
3027   // get splitter sizes and store it in the map for future setting
3028   QList<int> sizes;
3029   QStringList sizesLst = ::getValue( params, "sizes" ).split( ':', QString::SkipEmptyParts );
3030   QStringList::Iterator it;
3031   for ( it = sizesLst.begin(); it != sizesLst.end(); ++it )
3032     sizes.append( (*it).toInt() );
3033   sMap[ splitter ] = sizes;
3034
3035   // set orientation of splitter
3036   int orient = ::getValue( params, "orientation" ).toInt();
3037   splitter->setOrientation( (Qt::Orientation)orient );
3038
3039   // get children
3040   QString options = params.left( params.indexOf( '(' ) );
3041   QString childrenStr = params.right( params.length()-options.length() );
3042   QStringList children = ::getChildren( childrenStr );
3043
3044   // debug output..
3045   //  printf (" splitter orient=%d, sizes_count=%d, children=%d\n", orient, sizes.count(), children.count() );
3046   //  for ( QStringList::Iterator tit = children.begin(); tit != children.end(); ++tit )
3047   //    printf ("   |-> child = [%s]\n", (*tit).latin1() );
3048
3049   for ( it = children.begin(); it != children.end(); ++it )
3050   {
3051     if ( (*it).startsWith( "(splitter" ) )
3052     {
3053       QSplitter* newSplitter = new QSplitter( splitter );
3054       setSplitter( newSplitter, *it, sMap );
3055     }
3056     else if ( (*it).startsWith( "(views" ) )
3057     {
3058       QtxWorkstackArea* newArea = createArea( splitter );
3059       QString activeViewName = ::getValue( *it, "active" );
3060       QWidget* activeView = 0;
3061       activeViewName = activeViewName.mid( 1, activeViewName.length()-2 ); // chop off ' symbols
3062       int i = 0;
3063       QString viewName = ::getViewName( *it, i );
3064       while ( !viewName.isEmpty() )
3065       {
3066               if ( QWidget* view = ::getView( splitter, viewName ) )
3067         {
3068                 newArea->insertWidget( view );
3069                 if ( activeViewName == view->objectName() )
3070                   activeView = view;
3071               }
3072               viewName = ::getViewName( *it, ++i );
3073       }
3074       if ( activeView )
3075         newArea->setActiveWidget( activeView );
3076     }
3077   }
3078 }
3079
3080 /*!
3081   \brief Restore workstack configuration from the state description string.
3082   \param parameters workstack state description
3083   \return reference to this workstack
3084 */
3085 QtxWorkstack& QtxWorkstack::operator<<( const QString& parameters )
3086 {
3087   // clear the main splitter - remove all child splitters and empty areas from it
3088   QList<QSplitter*> splitList;
3089   QList<QtxWorkstackArea*> areaList;
3090   splitters( mySplit, splitList, false );
3091   areas( mySplit, areaList, false );
3092   for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end(); ++iter )
3093     delete *iter;
3094
3095   for ( QList<QtxWorkstackArea*>::iterator it = areaList.begin(); it != areaList.end(); ++it )
3096   {
3097     if ( (*it)->isEmpty() )
3098       delete *it;
3099   }
3100
3101   // restore splitter recursively
3102   QMap< QSplitter*, QList<int> > sMap;
3103   setSplitter( mySplit, parameters, sMap );
3104
3105   // now mySplit may contains empty area (where all views were located before restoring)
3106   // in order setSize to work correctly we have to exclude this area
3107   areaList.clear();
3108   areas( mySplit, areaList, false );
3109   for ( QList<QtxWorkstackArea*>::iterator delIt = areaList.begin(); delIt != areaList.end(); ++delIt )
3110   {
3111     if ( (*delIt)->isEmpty() )
3112       delete *delIt;
3113   }
3114
3115   QApplication::instance()->processEvents();
3116
3117   // restore splitters' sizes (map of sizes is filled in setSplitters)
3118   for ( QMap< QSplitter*, QList<int> >::iterator itm = sMap.begin(); itm != sMap.end(); ++itm )
3119     itm.key()->setSizes( itm.value() );
3120
3121   return (*this);
3122 }
3123
3124 /*!
3125   \brief Dump workstack configuration to the state description string.
3126   \param parameters resulting workstack state description
3127   \return reference to this workstack
3128 */
3129 QtxWorkstack& QtxWorkstack::operator>>( QString& outParameters )
3130 {
3131   splitterInfo( mySplit, outParameters );
3132   return (*this);
3133 }
3134
3135 /*!
3136   \fn void QtxWorkstack::windowActivated( QWidget* w )
3137   \brief Emitted when the workstack's child widget \w is activated.
3138   \param w widget being activated
3139 */
3140
3141 /*!
3142   \brief Gets area containing given widget
3143   \param wid widget
3144   \return pointer to QtxWorkstackArea* object
3145 */
3146 QtxWorkstackArea* QtxWorkstack::wgArea( QWidget* wid ) const
3147 {
3148   QtxWorkstackArea* resArea = 0;
3149
3150   QList<QtxWorkstackArea*> areaList;
3151   areas( mySplit, areaList, true );
3152
3153   QList<QtxWorkstackArea*>::ConstIterator it;
3154   for ( it = areaList.begin(); it != areaList.end() && !resArea; ++it )
3155   {
3156     if ( (*it)->contains( wid ) )
3157       resArea = *it;
3158   }
3159
3160   return resArea;
3161 }
3162
3163 /*!
3164   \brief Moves the first widget to the same area which the second widget belongs to
3165   \param wid widget to be moved
3166   \param wid_to widget specified the destination area
3167   \param before specifies whether the first widget has to be moved before or after 
3168          the second widget
3169   \return TRUE if operation is completed successfully, FALSE otherwise 
3170 */
3171 bool QtxWorkstack::move( QWidget* wid, QWidget* wid_to, const bool before )
3172 {
3173   if ( wid && wid_to )
3174   {
3175     QtxWorkstackArea* area_src = wgArea( wid );
3176     QtxWorkstackArea* area_to = wgArea( wid_to );
3177     if ( area_src && area_to )
3178     {
3179       // find index of the second widget
3180       QWidgetList wgList = area_to->widgetList();
3181       QWidgetList::ConstIterator it;
3182       int idx = 0;
3183       for ( it = wgList.begin(); it != wgList.begin(); ++it, idx++ )
3184       {
3185         if ( *it == wid_to )
3186           break;
3187       }
3188
3189       if ( idx < wgList.count() ) // paranoidal check
3190       {
3191         if ( !before )
3192           idx++;
3193         area_src->removeWidget( wid, true );
3194         area_to->insertWidget( wid, idx );
3195         return true;
3196       }
3197     }
3198   }
3199   return false;
3200 }
3201
3202 /*!
3203   \brief Group all windows in one area
3204   \return TRUE if operation is completed successfully, FALSE otherwise 
3205 */
3206 void QtxWorkstack::stack()
3207 {
3208   QWidgetList wgList = windowList();
3209   if ( !wgList.count() )
3210     return; // nothing to do
3211
3212   QtxWorkstackArea* area_to = 0;
3213   QWidgetList::ConstIterator it;
3214   for ( it = wgList.begin(); it != wgList.end(); ++it )
3215   {
3216     QtxWorkstackArea* area_src = 0;
3217     if ( !area_to )
3218     {
3219       area_to = wgArea( *it );
3220       area_src = area_to;
3221     }
3222     else 
3223       area_src = wgArea( *it );
3224
3225     if ( area_src != area_to )
3226     {
3227       area_src->removeWidget( *it, true );
3228       area_to->insertWidget( *it, -1 );
3229     }
3230   }
3231 }