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