Salome HOME
ec3fb77177e76a2376302566523764e503035508
[modules/gui.git] / src / Qtx / QtxWorkstack.cxx
1 // Copyright (C) 2007-2021  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   myTop = new QWidget( this );
532   base->addWidget( myTop );
533
534   QHBoxLayout* tl = new QHBoxLayout( myTop );
535   tl->setMargin( 0 );
536
537   myBar = new QtxWorkstackTabBar( myTop );
538   tl->addWidget( myBar, 1 );
539
540   CloseButton* close = new CloseButton( myTop );
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.toUtf8() );
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 Show/Hide tab bar.
907 */
908 void QtxWorkstackArea::showTabBar( bool visible)
909 {
910   myTop->setVisible(visible);
911   myBar->setVisible(visible);
912 }
913
914
915 /*!
916   \brief Get rectangle to be drawn when highlighting drop area.
917   \return area drop rectangle
918 */
919 QRect QtxWorkstackArea::floatRect() const
920 {
921   QRect r = myStack->geometry();
922   return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
923 }
924
925 /*!
926   \brief Get rectangle to be drawn when highlighting drop area on tab bar.
927   \param idx tab index
928   \return tab bar drop rectrangle
929 */
930 QRect QtxWorkstackArea::floatTab( const int idx ) const
931 {
932   QRect r = myBar->tabRect( idx );
933   return QRect( myBar->mapToGlobal( r.topLeft() ), r.size() );
934 }
935
936 /*!
937   \brief Get tab index by point.
938   \param p point
939   \return tab covering point or -1 if there is no tab covering point
940 */
941 int QtxWorkstackArea::tabAt( const QPoint& pnt ) const
942 {
943   int idx = -1;
944   QPoint p = myBar->mapFromGlobal( pnt );
945   for ( int i = 0; i < myBar->count() && idx == -1; i++ )
946   {
947     QRect r = myBar->tabRect( i );
948     if ( r.isValid() && r.contains( p ) )
949       idx = i;
950   }
951   return idx;
952 }
953
954 /*!
955   \brief Event handler for custom events.
956   \param e custom event
957 */
958 void QtxWorkstackArea::customEvent( QEvent* e )
959 {
960   WidgetEvent* we = (WidgetEvent*)e;
961
962   switch ( (int)we->type() )
963   {
964   case ActivateWidget:
965     myBar->updateActiveState();
966     // IMN 27/03/2015: This workaround caused by the bug INT PAL 0052623: OCC view blinking when
967     // using polyline sketcher which is reproduced on Unix systems with qt-4.8.4.
968     myStack->setUpdatesEnabled( false );
969     updateCurrent();
970     myStack->setUpdatesEnabled( true );
971     emit activated( activeWidget() );
972     break;
973   case FocusWidget:
974     if ( activeWidget() )
975     {
976       if ( !activeWidget()->focusWidget() )
977         activeWidget()->setFocus();
978       else
979       {
980         if ( activeWidget()->focusWidget()->hasFocus() )
981         {
982           QFocusEvent in( QEvent::FocusIn );
983                 QApplication::sendEvent( this, &in );
984               }
985         else
986         {
987           activeWidget()->focusWidget()->setFocus();
988                 myBar->updateActiveState();
989               }
990       }
991     }
992     break;
993   case MakeCurrent:
994     if ( we->child()->widget() )
995       setActiveWidget( we->child()->widget() );
996     break;
997   case RestoreWidget:
998     if ( we->child() )
999     {
1000       QtxWorkstackChild* c = we->child();
1001       RestoreEvent* re = (RestoreEvent*)we;
1002       if ( c->widget() )
1003         c->widget()->setVisible( re->flags() & QtxWorkstack::Visible );
1004       c->setId( re->id() );
1005       insertChild( c );
1006     }
1007     break;
1008   default:
1009     break;
1010   }
1011 }
1012
1013 /*!
1014   \brief Customize focus in event handler.
1015   \param e focus in event
1016 */
1017 void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
1018 {
1019   QFrame::focusInEvent( e );
1020
1021   myBar->updateActiveState();
1022
1023   emit activated( activeWidget() );
1024 }
1025
1026 /*!
1027   \brief Customize mouse press event handler.
1028   \param e mouse press event
1029 */
1030 void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
1031 {
1032   QFrame::mousePressEvent( e );
1033
1034   emit activated( activeWidget() );
1035 }
1036
1037 /*!
1038   \brief Called when user presses "Close" button.
1039 */
1040 void QtxWorkstackArea::onClose()
1041 {
1042   QWidget* wid = activeWidget();
1043   if ( wid )
1044     wid->close();
1045 }
1046
1047 /*!
1048   \brief Called when user selects any tab page.
1049   \param idx tab page index (not used)
1050 */
1051 void QtxWorkstackArea::onCurrentChanged( int /*idx*/ )
1052 {
1053   updateCurrent();
1054
1055   emit activated( activeWidget() );
1056 }
1057
1058 /*!
1059   \brief Called when user starts tab page dragging.
1060 */
1061 void QtxWorkstackArea::onDragActiveTab()
1062 {
1063   QtxWorkstackChild* c = child( activeWidget() );
1064   if ( !c )
1065     return;
1066
1067   new QtxWorkstackDrag( workstack(), c );
1068 }
1069
1070 /*!
1071   \brief Called when area's child widget container is destroyed.
1072   \param obj widget container being destroyed
1073 */
1074 void QtxWorkstackArea::onChildDestroyed( QObject* obj )
1075 {
1076   removeChild( (QtxWorkstackChild*)obj, false );
1077 }
1078
1079 /*!
1080   \brief Called when child widget container is shown.
1081   \param c child widget container being shown
1082 */
1083 void QtxWorkstackArea::onChildShown( QtxWorkstackChild* /*c*/ )
1084 {
1085   updateState();
1086 }
1087
1088 /*!
1089   \brief Called when child widget container is hidden.
1090   \param c child widget container being hidden
1091 */
1092 void QtxWorkstackArea::onChildHidden( QtxWorkstackChild* /*c*/ )
1093 {
1094   updateState();
1095 }
1096
1097 /*!
1098   \brief Called when child widget container is activated.
1099   \param c child widget container being activated
1100 */
1101 void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
1102 {
1103   setWidgetActive( c->widget() );
1104 }
1105
1106 /*!
1107   \brief Called when child widget container's title is changed.
1108   \param c child widget container which title is changed
1109 */
1110 void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
1111 {
1112   updateTab( c->widget() );
1113 }
1114
1115 /*!
1116   \brief Update current child widget container.
1117
1118   Raises widget when active tab page is changed.
1119 */
1120 void QtxWorkstackArea::updateCurrent()
1121 {
1122   QWidget* cur = child( myBar->tabId( myBar->currentIndex() ) );
1123   if ( cur )
1124     myStack->setCurrentWidget( cur );
1125 }
1126
1127 /*!
1128   \brief Update tab bar.
1129   \param wid tab page widget
1130 */
1131 void QtxWorkstackArea::updateTab( QWidget* wid )
1132 {
1133   int idx = myBar->indexOf( widgetId( wid ) );
1134   if ( idx < 0 )
1135     return;
1136
1137   myBar->setTabIcon( idx, wid->windowIcon() );
1138   myBar->setTabText( idx, wid->windowTitle() );
1139 }
1140
1141 /*!
1142   \brief Get child widget by specified identifier.
1143   \param id widget ID
1144   \return widget or 0, if identifier in invalid
1145 */
1146 QWidget* QtxWorkstackArea::widget( const int id ) const
1147 {
1148   QtxWorkstackChild* c = child( id );
1149
1150   return c ? c->widget() : 0;
1151 }
1152
1153 /*!
1154   \brief Get child widget identifier.
1155   \param wid widget
1156   \return widget ID or -1 if widget is not found
1157 */
1158 int QtxWorkstackArea::widgetId( QWidget* wid ) const
1159 {
1160   QtxWorkstackChild* c = child( wid );
1161
1162   return c ? c->id() : -1;
1163 }
1164
1165 /*!
1166   \brief Set active child widget.
1167   \param wid widget to be set active
1168 */
1169 void QtxWorkstackArea::setWidgetActive( QWidget* wid )
1170 {
1171   int id = widgetId( wid );
1172   if ( id < 0 )
1173     return;
1174
1175   myBar->setCurrentIndex( myBar->indexOf( id ) );
1176 }
1177
1178 /*!
1179   \brief Update internal state.
1180 */
1181 void QtxWorkstackArea::updateState()
1182 {
1183   bool updBar = myBar->updatesEnabled();
1184   bool updStk = myStack->updatesEnabled();
1185   myBar->setUpdatesEnabled( false );
1186   myStack->setUpdatesEnabled( false );
1187
1188   bool block = myBar->signalsBlocked();
1189   myBar->blockSignals( true );
1190
1191   QWidget* prev = activeWidget();
1192
1193   int idx = 0;
1194   for ( ChildList::iterator it = myList.begin(); it != myList.end(); ++it )
1195   {
1196     QtxWorkstackChild* cont = *it;
1197     QWidget* wid = cont->widget();;
1198     int id = cont->id();
1199
1200     if ( id < 0 )
1201       continue;
1202
1203     bool vis = cont->visibility();
1204
1205     int cIdx = myBar->indexOf( id );
1206     if ( cIdx != -1 && ( !vis || myBar->indexOf( id ) != idx ) )
1207       myBar->removeTab( cIdx );
1208
1209     if ( myBar->indexOf( id ) == -1 && vis )
1210       myBar->setTabId( myBar->insertTab( idx, wid->windowTitle() ), id );
1211
1212     updateTab( wid );
1213
1214     if ( !vis )
1215       myStack->removeWidget( cont );
1216     else if ( myStack->indexOf( cont ) < 0 )
1217       myStack->addWidget( cont );
1218
1219     if ( vis )
1220       idx++;
1221   }
1222
1223   int curId = widgetId( prev );
1224   if ( myBar->indexOf( curId ) < 0 )
1225   {
1226     QtxWorkstackChild* c = 0;
1227     int pos = myList.indexOf( child( prev ) );
1228     for ( int i = pos - 1; i >= 0 && !c; i-- )
1229     {
1230       if ( myList.at( i )->visibility() )
1231         c = myList.at( i );
1232     }
1233
1234     for ( int j = pos + 1; j < (int)myList.count() && !c; j++ )
1235     {
1236       if ( myList.at( j )->visibility() )
1237         c = myList.at( j );
1238     }
1239
1240     if ( c )
1241       curId = c->id();
1242   }
1243
1244   myBar->setCurrentIndex( myBar->indexOf( curId ) );
1245
1246   myBar->blockSignals( block );
1247
1248   updateCurrent();
1249
1250   myBar->updateActiveState();
1251
1252   myBar->setUpdatesEnabled( updBar );
1253   myStack->setUpdatesEnabled( updStk );
1254   if ( updBar )
1255     myBar->update();
1256   if ( updStk )
1257     myStack->update();
1258
1259   QResizeEvent re( myBar->size(), myBar->size() );
1260   QApplication::sendEvent( myBar, &re );
1261
1262   myBar->updateGeometry();
1263
1264   if ( isEmpty() )
1265   {
1266     hide();
1267     emit deactivated( this );
1268   }
1269   else
1270   {
1271     show();
1272     if ( prev != activeWidget() )
1273       emit activated( activeWidget() );
1274   }
1275 }
1276
1277 /*!
1278   \brief Generate unique widget identifier.
1279   \return first non shared widget ID
1280 */
1281 int QtxWorkstackArea::generateId() const
1282 {
1283   QMap<int, int> map;
1284
1285   for ( ChildList::const_iterator it = myList.begin(); it != myList.end(); ++it )
1286     map.insert( (*it)->id(), 0 );
1287
1288   int id = 0;
1289   while ( map.contains( id ) )
1290     id++;
1291
1292   return id;
1293 }
1294
1295 /*!
1296   \brief Get child widget container.
1297   \param wid child widget
1298   \return child widget container corresponding to the \a wid
1299 */
1300 QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
1301 {
1302   QtxWorkstackChild* res = 0;
1303   for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !res; ++it )
1304   {
1305     if ( (*it)->widget() == wid )
1306       res = *it;
1307   }
1308   return res;
1309 }
1310
1311 QtxWorkstackChild* QtxWorkstackArea::child( const int id ) const
1312 {
1313   QtxWorkstackChild* c = 0;
1314   for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !c; ++it )
1315   {
1316     if ( (*it)->id() == id )
1317       c = *it;
1318   }
1319   return c;
1320 }
1321
1322 /*!
1323   \fn void QtxWorkstackArea::activated( QWidget* w )
1324   \brief Emitted when child widget is activated.
1325   \param w child widget being activated
1326 */
1327
1328 /*!
1329   \fn void QtxWorkstackArea::contextMenuRequested( QWidget* w, QPoint p )
1330   \brief Emitted when context popup menu is requested.
1331   \param w child widget popup menu requested for
1332   \param p point popup menu to be shown at
1333 */
1334
1335 /*!
1336   \fn void QtxWorkstackArea::deactivated( QtxWorkstackArea* wa )
1337   \brief Emitted when workarea is deactivated.
1338   \param wa workarea being deactivated
1339 */
1340
1341 /*!
1342   \class QtxWorkstackChild
1343   \internal
1344   \brief Workarea child widget container.
1345 */
1346
1347 /*!
1348   \brief Constructor.
1349   \param wid child widget
1350   \param parent parent widget
1351   \param f widget flags
1352 */
1353 QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent, Qt::WindowFlags f )
1354 : QWidget( parent ),
1355   myId( 0 ),
1356   myWidget( wid )
1357 {
1358   if ( myWidget )
1359   {
1360     myWidget->setParent( this, f );
1361     myWidget->installEventFilter( this );
1362     if ( myWidget->focusProxy() )
1363       myWidget->focusProxy()->installEventFilter( this );
1364     myWidget->setVisible( myWidget->isVisibleTo( myWidget->parentWidget() ) );
1365
1366     QVBoxLayout* base = new QVBoxLayout( this );
1367     base->setMargin( 0 );
1368     base->addWidget( myWidget );
1369
1370     connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1371   }
1372 }
1373
1374 /*!
1375   \brief Destructor.
1376 */
1377 QtxWorkstackChild::~QtxWorkstackChild()
1378 {
1379   QApplication::instance()->removeEventFilter( this );
1380
1381   if ( !widget() )
1382     return;
1383
1384   disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1385
1386   widget()->hide();
1387   widget()->removeEventFilter( this );
1388   if ( widget()->focusProxy() )
1389     widget()->focusProxy()->removeEventFilter( this );
1390
1391   widget()->setParent( 0 );
1392 }
1393
1394 /*!
1395   \brief Get child widget.
1396   \return child widget
1397 */
1398 QWidget* QtxWorkstackChild::widget() const
1399 {
1400   return myWidget;
1401 }
1402
1403 /*!
1404   \brief Returns the id.
1405 */
1406 int QtxWorkstackChild::id() const
1407 {
1408   return myId;
1409 }
1410
1411 /*!
1412   \brief Sets the id.
1413 */
1414 void QtxWorkstackChild::setId( const int id )
1415 {
1416   myId = id;
1417 }
1418
1419 /*!
1420   \brief Returns true if this child window should be visible.
1421 */
1422 bool QtxWorkstackChild::visibility()
1423 {
1424   return myWidget ? myWidget->isVisibleTo( this ) : false;
1425 }
1426
1427 QtxWorkstackArea* QtxWorkstackChild::area() const
1428 {
1429   QtxWorkstackArea* a = 0;
1430   QWidget* w = parentWidget();
1431   while ( !a && w )
1432   {
1433     a = ::qobject_cast<QtxWorkstackArea*>( w );
1434     w = w->parentWidget();
1435   }
1436
1437   return a;
1438 }
1439
1440 /*!
1441   \brief Custom event filter.
1442
1443   Process events from child widgets.
1444
1445   \param o event receiver widget
1446   \param e event
1447   \return \c true if event should be filtered (stop further processing)
1448 */
1449 bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
1450 {
1451   if ( o->isWidgetType() )
1452   {
1453     if ( e->type() == QEvent::WindowTitleChange || e->type() == QEvent::WindowIconChange )
1454       emit captionChanged( this );
1455
1456     if ( !e->spontaneous() && e->type() == QEvent::ShowToParent )
1457       emit shown( this );
1458
1459     if ( !e->spontaneous() && e->type() == QEvent::HideToParent )
1460       emit hidden( this );
1461
1462     if ( e->type() == QEvent::FocusIn )
1463       emit activated( this );
1464   }
1465   return QWidget::eventFilter( o, e );
1466 }
1467
1468 /*!
1469   \brief Called when child widget is destroyed.
1470   \param obj child widget being destroyed
1471 */
1472 void QtxWorkstackChild::onDestroyed( QObject* /*obj*/ )
1473 {
1474   deleteLater();
1475 }
1476
1477 /*!
1478   \brief Customize child event handler.
1479   \param e child event
1480 */
1481 void QtxWorkstackChild::childEvent( QChildEvent* e )
1482 {
1483   if ( e->removed() && e->child() == widget() )
1484   {
1485     myWidget = 0;
1486     deleteLater();
1487   }
1488   QWidget::childEvent( e );
1489 }
1490
1491 /*!
1492   \fn void QtxWorkstackChild::shown( QtxWorkstackChild* w )
1493   \brief Emitted when child widget is shown.
1494   \param w child widget container
1495 */
1496
1497 /*!
1498   \fn void QtxWorkstackChild::hidden( QtxWorkstackChild* w )
1499   \brief Emitted when child widget is hidden.
1500   \param w child widget container
1501 */
1502
1503 /*!
1504   \fn void QtxWorkstackChild::activated( QtxWorkstackChild* w )
1505   \brief Emitted when child widget is activated.
1506   \param w child widget container
1507 */
1508
1509 /*!
1510   \fn void QtxWorkstackChild::captionChanged( QtxWorkstackChild* w )
1511   \brief Emitted when child widget's title is changed.
1512   \param w child widget container
1513 */
1514
1515 /*!
1516   \class QtxWorkstackTabBar
1517   \internal
1518   \brief Workstack tab bar widget
1519 */
1520
1521 /*!
1522   \brief Constructor.
1523   \param parent parent widget
1524 */
1525 QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
1526 : QTabBar( parent ),
1527   myId( -1 ),myActive(false)
1528 {
1529   setDrawBase( true );
1530   setElideMode( Qt::ElideNone );
1531
1532   connect( this, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
1533 }
1534
1535 /*!
1536   \brief Destructor.
1537 */
1538 QtxWorkstackTabBar::~QtxWorkstackTabBar()
1539 {
1540 }
1541
1542 /*!
1543   \brief Get tab page identifier.
1544   \param index tab page index
1545   \return tab page ID or -1 if \a index is out of range
1546 */
1547 int QtxWorkstackTabBar::tabId( const int index ) const
1548 {
1549   QVariant v = tabData( index );
1550   if ( !v.canConvert( QVariant::Int ) )
1551     return -1;
1552   return v.toInt();
1553 }
1554
1555 /*!
1556   \brief Set tab page identifier.
1557   \param index tab page index
1558   \param id tab page ID
1559 */
1560 void QtxWorkstackTabBar::setTabId( const int index, const int id )
1561 {
1562   setTabData( index, id );
1563 }
1564
1565 /*!
1566   \brief Get tab page index by specified identifier.
1567   \param id tab page ID
1568   \return tab page index or -1 if not found
1569 */
1570 int QtxWorkstackTabBar::indexOf( const int id ) const
1571 {
1572   int index = -1;
1573   for ( int i = 0; i < (int)count() && index < 0; i++ )
1574   {
1575     if ( tabId( i ) == id )
1576       index = i;
1577   }
1578   return index;
1579 }
1580
1581 /*!
1582   \brief Check if the tab bar is active.
1583   \return \c true if tab bar is active
1584 */
1585 bool QtxWorkstackTabBar::isActive() const
1586 {
1587   return myActive;
1588 }
1589
1590 /*!
1591   \brief Set tab bar active/inactive.
1592   \param on new active state
1593 */
1594 void QtxWorkstackTabBar::setActive( const bool on )
1595 {
1596   if ( myActive == on )
1597     return;
1598
1599   myActive = on;
1600   updateActiveState();
1601 }
1602
1603 /*!
1604   \brief Update tab bar according to the 'active' state.
1605 */
1606 void QtxWorkstackTabBar::updateActiveState()
1607 {
1608   QColor bc = palette().color( QPalette::Text );
1609   QColor ac = isActive() ? palette().color( QPalette::Highlight ) : bc;
1610   for ( int i = 0; i < (int)count(); i++ )
1611     setTabTextColor( i, currentIndex() == i ? ac : bc );
1612 }
1613
1614 /*!
1615   \brief Called when current tab page is changed.
1616   \param idx tab page index (not used)
1617 */
1618 void QtxWorkstackTabBar::onCurrentChanged( int /*index*/ )
1619 {
1620   updateActiveState();
1621 }
1622
1623 /*!
1624   \brief Customize mouse move event handler.
1625   \param e mouse event
1626 */
1627 void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
1628 {
1629   if ( myId != -1 && !tabRect( indexOf( myId ) ).contains( e->pos() ) )
1630   {
1631     myId = -1;
1632     emit dragActiveTab();
1633   }
1634
1635   QTabBar::mouseMoveEvent( e );
1636 }
1637
1638 /*!
1639   \brief Customize mouse press event handler.
1640   \param e mouse event
1641 */
1642 void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
1643 {
1644   QTabBar::mousePressEvent( e );
1645
1646   if ( e->button() == Qt::LeftButton )
1647     myId = tabId( currentIndex() );
1648 }
1649
1650 /*!
1651   \brief Customize mouse release event handler.
1652   \param e mouse event
1653 */
1654 void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
1655 {
1656   QTabBar::mouseReleaseEvent( e );
1657
1658   myId = -1;
1659
1660   if ( e->button() == Qt::RightButton )
1661     emit contextMenuRequested( e->globalPos() );
1662 }
1663
1664 /*!
1665   \brief Customize context menu event handler.
1666   \param e context menu event
1667 */
1668 void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
1669 {
1670   if ( e->reason() != QContextMenuEvent::Mouse )
1671     emit contextMenuRequested( e->globalPos() );
1672 }
1673
1674 /*!
1675   \brief Process widget change state events (style, palette, enable state changing, etc).
1676   \param e change event (not used)
1677 */
1678 void QtxWorkstackTabBar::changeEvent( QEvent* /*e*/ )
1679 {
1680   updateActiveState();
1681 }
1682
1683 /*
1684 void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
1685 {
1686   if ( currentTab() != t->identifier() )
1687   {
1688     QFont fnt = p->font();
1689     fnt.setUnderline( false );
1690     p->setFont( fnt );
1691   }
1692   QTabBar::paintLabel( p, br, t, has_focus );
1693 }
1694 */
1695
1696 /*!
1697   \fn void QtxWorkstackTabBar::dragActiveTab()
1698   \brief Emitted when dragging operation is started.
1699 */
1700
1701 /*!
1702   \fn void QtxWorkstackTabBar::contextMenuRequested( QPoint p )
1703   \brief Emitted when context popup menu is requested.
1704   \param p point popup menu to be shown at
1705 */
1706
1707 /*!
1708   \class QtxWorkstack
1709   \brief Workstack widget.
1710
1711   Organizes the child widgets in the tabbed space.
1712   Allows splitting the working area to arrange the child widgets in
1713   arbitrary way. Any widgets can be moved to another working area with
1714   drag-n-drop operation.
1715
1716   This widget can be used as workspace of the application main window,
1717   for example, as kind of implementation of multi-document interface.
1718 */
1719
1720 /*!
1721   \brief Constructor.
1722   \param parent parent widget
1723 */
1724 QtxWorkstack::QtxWorkstack( QWidget* parent )
1725 : QWidget( parent ),
1726   myWin( 0 ),
1727   myArea( 0 ),
1728   myWorkWin( 0 ),
1729   myWorkArea( 0 )
1730 {
1731   myActionsMap.insert( SplitVertical,   new QtxAction( QString(), tr( "Split vertically" ), 0, this ) );
1732   myActionsMap.insert( SplitHorizontal, new QtxAction( QString(), tr( "Split horizontally" ), 0, this ) );
1733   myActionsMap.insert( Close,           new QtxAction( QString(), tr( "Close" ), 0, this ) );
1734   myActionsMap.insert( Rename,          new QtxAction( QString(), tr( "Rename" ), 0, this ) );
1735
1736   connect( myActionsMap[SplitVertical], SIGNAL( triggered( bool ) ), this, SLOT( splitVertical() ) );
1737   connect( myActionsMap[SplitHorizontal], SIGNAL( triggered( bool ) ), this, SLOT( splitHorizontal() ) );
1738   connect( myActionsMap[Close], SIGNAL( triggered( bool ) ), this, SLOT( onCloseWindow() ) );
1739   connect( myActionsMap[Rename], SIGNAL( triggered( bool ) ), this, SLOT( onRename() ) );
1740
1741   // Action shortcut will work when action added in any widget.
1742   for ( QMap<int, QAction*>::iterator it = myActionsMap.begin(); it != myActionsMap.end(); ++it )
1743   {
1744     addAction( it.value() );
1745     it.value()->setShortcutContext( Qt::ApplicationShortcut );
1746   }
1747
1748   QVBoxLayout* base = new QVBoxLayout( this );
1749   base->setMargin( 0 );
1750
1751   mySplit = new QtxWorkstackSplitter( this );
1752   base->addWidget( mySplit );
1753 }
1754
1755 /*!
1756   \brief Destructor.
1757 */
1758 QtxWorkstack::~QtxWorkstack()
1759 {
1760 }
1761
1762 /*!
1763   \brief Get list of all widgets in all areas or in specified area which given
1764          widget belongs to
1765   \param wid widget specifying area if it is equal to null when widgets of all
1766          areas are retuned
1767   \return list of widgets
1768 */
1769 QWidgetList QtxWorkstack::windowList( QWidget* wid ) const
1770 {
1771   QList<QtxWorkstackArea*> lst;
1772   if ( !wid )
1773   {
1774     areas( mySplit, lst, true );
1775   }
1776   else
1777   {
1778     QtxWorkstackArea* area = wgArea( wid );
1779     if ( area )
1780       lst.append( area );
1781   }
1782
1783   QWidgetList widList;
1784   for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end(); ++it )
1785   {
1786     QWidgetList wids = (*it)->widgetList();
1787     for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
1788       widList.append( *itr );
1789   }
1790
1791   return widList;
1792 }
1793
1794 /*!
1795   \brief Get all child widgets in the active workarea.
1796   \return list of widgets in active workarea
1797 */
1798 QWidgetList QtxWorkstack::splitWindowList() const
1799 {
1800   return myArea ? myArea->widgetList() : QWidgetList();
1801 }
1802
1803 /*!
1804   \brief Get active widget.
1805   \return active widget
1806 */
1807 QWidget* QtxWorkstack::activeWindow() const
1808 {
1809   return myWin;
1810 }
1811
1812 /*!
1813   \brief Set active widget
1814   \param wid widget to activate
1815 */
1816 void QtxWorkstack::setActiveWindow( QWidget* wid )
1817 {
1818   if ( activeArea() )
1819     activeArea()->setActiveWidget( wid );
1820 }
1821
1822 /*!
1823   \brief Split workstack.
1824
1825   Splitting is possible only if there are two or more widgets in the workarea.
1826   This function splits current workarea to two new ones.
1827
1828   \param o splitting orientation (Qt::Orientation)
1829 */
1830 void QtxWorkstack::split( const int o )
1831 {
1832   QtxWorkstackArea* area = myWorkArea;
1833   if ( !area )
1834     area = activeArea();
1835   if ( !area )
1836     return;
1837
1838   if ( area->widgetList().count() < 2 )
1839     return;
1840
1841   QWidget* curWid = area->activeWidget();
1842   if ( !curWid )
1843     return;
1844
1845   QSplitter* s = splitter( area );
1846   QList<QtxWorkstackArea*> areaList;
1847   areas( s, areaList );
1848
1849   QList<QSplitter*> splitList;
1850   splitters( s, splitList );
1851
1852   QSplitter* trg = 0;
1853   if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
1854     trg = s;
1855
1856   if ( !trg )
1857     trg = wrapSplitter( area );
1858
1859   if ( !trg )
1860     return;
1861
1862   trg->setOrientation( (Qt::Orientation)o );
1863
1864   QtxWorkstackArea* newArea = createArea( 0 );
1865   trg->insertWidget( trg->indexOf( area ) + 1, newArea );
1866
1867   area->removeWidget( curWid );
1868   newArea->insertWidget( curWid );
1869
1870   distributeSpace( trg );
1871
1872   curWid->show();
1873   curWid->setFocus();
1874 }
1875
1876 /*!
1877   \brief Split workarea of the given widget on two parts.
1878
1879   Splitting is possible only if there are two or more widgets in the workarea.
1880   This function splits current workarea to two new ones.
1881
1882   \param wid widget belonging to the workstack
1883   \param o splitting orientation type (Qt::Orientation)
1884   \param type splitting type (QtxWorkstack::SplitType)
1885 */
1886 void QtxWorkstack::Split( QWidget* wid, const Qt::Orientation o, const SplitType type )
1887 {
1888   if (!wid) return;
1889
1890   // find area of the given widget
1891   QtxWorkstackArea* area = NULL;
1892   QList<QtxWorkstackArea*> allAreas;
1893   areas(mySplit, allAreas, true);
1894
1895   for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
1896   {
1897     if ( (*it)->contains( wid ) )
1898       area = *it;
1899   }
1900
1901   if ( !area )
1902     return;
1903
1904   QWidget* curWid = area->activeWidget();
1905   if ( !curWid )
1906     return;
1907
1908   QWidgetList wids = area->widgetList();
1909   if ( wids.count() < 2 )
1910     return;
1911
1912   QSplitter* s = splitter( area );
1913   QList<QtxWorkstackArea*> areaList;
1914   areas( s, areaList );
1915
1916   QList<QSplitter*> splitList;
1917   splitters(s, splitList);
1918
1919   QSplitter* trg = 0;
1920   if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
1921     trg = s;
1922
1923   if (!trg) trg = wrapSplitter(area);
1924   if (!trg) return;
1925
1926   trg->setOrientation(o);
1927
1928   QtxWorkstackArea* newArea = createArea(0);
1929   insertWidget(newArea, trg, area);
1930
1931   switch ( type )
1932   {
1933   case SplitStay:
1934     for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
1935     {
1936       QWidget* wid_i = *itr;
1937       if ( wid_i != wid )
1938       {
1939         area->removeWidget( wid_i );
1940         newArea->insertWidget( wid_i );
1941         wid_i->showMaximized();
1942       }
1943     }
1944     break;
1945   case SplitAt:
1946     {
1947       QWidgetList::iterator itr = wids.begin();
1948       for ( ; itr != wids.end() && *itr != wid; ++itr )
1949       {
1950       }
1951       for ( ; itr != wids.end(); ++itr )
1952       {
1953         area->removeWidget( *itr );
1954         newArea->insertWidget( *itr );
1955         (*itr)->showMaximized();
1956       }
1957     }
1958     break;
1959   case SplitMove:
1960     area->removeWidget( wid );
1961     newArea->insertWidget( wid );
1962     wid->showMaximized();
1963     break;
1964   }
1965
1966   distributeSpace( trg );
1967
1968   curWid->show();
1969   curWid->setFocus();
1970 }
1971
1972 /*!
1973  \brief Move widget(s) from the source workarea into the target workarea
1974         or reorder widgets inside one workarea.
1975
1976  Move \a wid2 in target workarea. Put it right after \a wid1.
1977  If \a all parameter is \c true, all widgets from source workarea
1978  will be moved including \a wid2 and source workarea will be deleted then.
1979
1980  If \a wid1 and \a wid2 belongs to one workarea, widgets will be just reordered
1981  in that workarea.
1982
1983  \param wid1 widget from target workarea
1984  \param wid2 widget from source workarea
1985  \param all  if \c true, all widgets from source workarea will
1986              be moved into the target one, else only the \a wid2 will be moved
1987 */
1988 void QtxWorkstack::Attract( QWidget* wid1, QWidget* wid2, const bool all )
1989 {
1990   if ( !wid1 || !wid2 )
1991     return;
1992
1993   // find area of the widgets
1994   QtxWorkstackArea *area1 = 0, *area2 = 0;
1995   QList<QtxWorkstackArea*> allAreas;
1996   areas( mySplit, allAreas, true );
1997   for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !( area1 && area2 ); ++it )
1998   {
1999     if ( (*it)->contains( wid1 ) )
2000       area1 = *it;
2001
2002     if ( (*it)->contains( wid2 ) )
2003       area2 = *it;
2004   }
2005
2006   if ( !area1 || !area2 )
2007     return;
2008
2009   QSplitter* s1 = splitter( area1 );
2010
2011   QWidget* curWid = area1->activeWidget();
2012   if ( !curWid )
2013     return;
2014
2015   if ( area1 == area2 )
2016   {
2017     if ( all )
2018     {
2019       // Set wid1 at first position, wid2 at second
2020       area1->insertWidget( wid1 );
2021       area1->insertWidget( wid2, 1 );
2022       wid1->showMaximized();
2023       wid2->showMaximized();
2024     }
2025     else
2026     {
2027       // Set wid2 right after wid1
2028       area1->removeWidget( wid2 );
2029       int wid1_ind = 0;
2030       QWidgetList wids1 = area1->widgetList();
2031       for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
2032       area1->insertWidget( wid2, wid1_ind + 1 );
2033       wid2->showMaximized();
2034     }
2035   }
2036   else
2037   {
2038     int wid1_ind = 0;
2039     QWidgetList wids1 = area1->widgetList();
2040     for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
2041     if ( all )
2042     {
2043       // Set wid2 right after wid1, other widgets from area2 right after wid2
2044       QWidgetList wids2 = area2->widgetList();
2045       QWidgetList::iterator itr2 = wids2.begin();
2046       for ( int ind = wid1_ind + 1; itr2 != wids2.end(); ++itr2, ++ind )
2047       {
2048         area2->removeWidget( *itr2 );
2049         if ( *itr2 == wid2 )
2050           area1->insertWidget( *itr2, wid1_ind + 1 );
2051         else
2052           area1->insertWidget( *itr2, ind );
2053         (*itr2)->showMaximized();
2054       }
2055     }
2056     else
2057     {
2058       // Set wid2 right after wid1
2059       area2->removeWidget( wid2 );
2060       area1->insertWidget( wid2, wid1_ind + 1 );
2061       wid2->showMaximized();
2062     }
2063   }
2064
2065   distributeSpace( s1 );
2066
2067   area1->setActiveWidget( curWid );
2068
2069   wid2->show();
2070   wid1->setFocus();
2071   curWid->show();
2072   curWid->setFocus();
2073 }
2074
2075 /*!
2076   \brief Calculate sizes of the splitter widget for the workarea.
2077   \internal
2078 */
2079 static void setSizes (QIntList& szList, const int item_ind,
2080                       const int new_near, const int new_this, const int new_farr)
2081 {
2082   // set size to all items before an item # <item_ind>
2083   int cur_pos = 0;
2084   QIntList::iterator its = szList.begin();
2085   for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
2086     *its = new_near;
2087   }
2088   if (its == szList.end()) return;
2089   // set size to item # <item_ind>
2090   *its = new_this;
2091   ++its;
2092   // set size to all items after an item # <item_ind>
2093   for (; its != szList.end(); ++its) {
2094     *its = new_farr;
2095   }
2096 }
2097
2098 /*!
2099   \brief Set position of the widget relatively to its parent splitter.
2100
2101   Orientation of positioning will correspond to the splitter orientation.
2102
2103   \param wid widget
2104   \param pos position relatively to the splitter; value in the range [0..1]
2105 */
2106 void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
2107 {
2108   if ( position < 0.0 || 1.0 < position)
2109     return;
2110
2111   if ( !wid )
2112     return;
2113
2114   // find area of the given widget
2115   QtxWorkstackArea* area = NULL;
2116   QList<QtxWorkstackArea*> allAreas;
2117   areas( mySplit, allAreas, true );
2118   for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
2119   {
2120     if ( (*it)->contains( wid ) )
2121       area = *it;
2122   }
2123
2124   if ( !area )
2125     return;
2126
2127   QSplitter* split = splitter( area );
2128   if ( !split )
2129     return;
2130
2131   // find index of the area in its splitter
2132   int item_ind = -1;
2133   bool isFound = false;
2134   const QObjectList& was = split->children();
2135   for ( QObjectList::const_iterator ito = was.begin(); ito != was.end() && !isFound; ++ito, ++item_ind )
2136   {
2137     if ( *ito == area )
2138       isFound = true;
2139   }
2140
2141   if ( !isFound || item_ind == 0 )
2142     return;
2143
2144   QIntList szList = split->sizes();
2145   int splitter_size = ( split->orientation() == Qt::Horizontal ? split->width() : split->height());
2146   int nb = szList.count();
2147
2148   int new_prev = int( splitter_size * position / item_ind );
2149   if (nb == item_ind) return;
2150   int new_next = int( splitter_size * ( 1.0 - position ) / ( nb - item_ind ) );
2151   setSizes( szList, item_ind, new_prev, new_next, new_next );
2152   split->setSizes( szList );
2153 }
2154
2155 /*!
2156   \brief Set position of the widget relatively to the entire workstack.
2157
2158   If \a o is \c Qt::Horizontal, the horizontal position of \a wid will be changed.
2159   If \a o is \c Qt::Vertical, the vertical position of \a wid will be changed.
2160
2161   \param wid widget
2162   \param o   orientation of positioning (\c Qt::Horizontal or \c Qt::Vertical)
2163   \param pos position relatively to the workstack; value in range [0..1]
2164 */
2165 void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
2166                                         const double position )
2167 {
2168   if ( position < 0.0 || 1.0 < position )
2169     return;
2170
2171   if ( !wid )
2172     return;
2173
2174   int splitter_size = o == Qt::Horizontal ? mySplit->width() : mySplit->height();
2175   int need_pos = int( position * splitter_size );
2176   int splitter_pos = 0;
2177
2178   if ( setPosition( wid, mySplit, o, need_pos, splitter_pos ) != 0 )
2179   {
2180     // impossible to set required position
2181   }
2182 }
2183
2184 /*!
2185   \brief Set accelerator key-combination for the action with specified \a id.
2186   \param id action ID
2187   \param accel action accelerator
2188 */
2189 void QtxWorkstack::setAccel( const int id, const int accel )
2190 {
2191   if ( !myActionsMap.contains( id ) )
2192     return;
2193
2194   myActionsMap[id]->setShortcut( accel );
2195 }
2196
2197 /*!
2198   \brief Get the action's accelerator key-combination.
2199   \param id action ID
2200   \return action accelerator
2201 */
2202 int QtxWorkstack::accel( const int id ) const
2203 {
2204   int res = 0;
2205   if ( myActionsMap.contains( id ) )
2206     res = myActionsMap[id]->shortcut()[0];
2207   return res;
2208 }
2209
2210 /*!
2211   \brief Get icon for the specified action.
2212
2213   If \a id is invalid, null icon is returned.
2214
2215   \param id menu action ID
2216   \return menu item icon
2217 */
2218 QIcon QtxWorkstack::icon( const int id ) const
2219 {
2220   QIcon ico;
2221   if ( myActionsMap.contains( id ) )
2222     ico = myActionsMap[id]->icon();
2223   return ico;
2224 }
2225
2226 /*!
2227   \brief Set menu item icon for the specified action.
2228   \param id menu action ID
2229   \param ico new menu item icon
2230 */
2231 void QtxWorkstack::setIcon( const int id, const QIcon& icon )
2232 {
2233   if ( !myActionsMap.contains( id ) )
2234     return;
2235
2236   myActionsMap[id]->setIcon( icon );
2237 }
2238
2239 /*!
2240   \brief Set actions to be visible in the context popup menu.
2241
2242   Actions, which IDs are set in \a flags parameter, will be shown in the
2243   context popup menu. Other actions will not be shown.
2244
2245   \param flags ORed together actions flags
2246 */
2247 void QtxWorkstack::setMenuActions( const int flags )
2248 {
2249   myActionsMap[SplitVertical]->setVisible( flags & SplitVertical );
2250   myActionsMap[SplitHorizontal]->setVisible( flags & SplitHorizontal );
2251   myActionsMap[Close]->setVisible( flags & Close );
2252   myActionsMap[Rename]->setVisible( flags & Rename );
2253 }
2254
2255 /*!
2256   \brief Set actions to be visible in the context popup menu.
2257
2258   Actions, which IDs are set in \a flags parameter, will be shown in the
2259   context popup menu. Other actions will not be shown.
2260
2261   \param flags ORed together actions flags
2262 */
2263 int QtxWorkstack::menuActions() const
2264 {
2265   int ret = 0;
2266   ret = ret | ( myActionsMap[SplitVertical]->isVisible() ? SplitVertical : 0 );
2267   ret = ret | ( myActionsMap[SplitHorizontal]->isVisible() ? SplitHorizontal : 0 );
2268   ret = ret | ( myActionsMap[Close]->isVisible() ? Close : 0 );
2269   ret = ret | ( myActionsMap[Rename]->isVisible() ? Rename : 0 );
2270   return ret;
2271 }
2272
2273 /*!
2274   \brief Calculate sizes of the splitter widget for the workarea.
2275   \internal
2276 */
2277 static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
2278                            const int item_ind, const int item_rel_pos,
2279                            const int need_pos, const int splitter_pos)
2280 {
2281   if (item_ind == 0) { // cannot move in this splitter
2282     return (need_pos - splitter_pos);
2283   }
2284
2285   int delta = 0;
2286   int new_prev = 0;
2287   int new_this = szList[item_ind];
2288   int new_next = 0;
2289
2290   if (need_pos < splitter_pos) {
2291     // Set size of all previous workareas to zero <--
2292     if (item_ind == nb - 1) {
2293       // item iz last in the splitter, it will occupy all the splitter
2294       new_this = splitter_size;
2295     } else {
2296       // recompute size of next items in splitter
2297       new_next = (splitter_size - new_this) / (nb - item_ind - 1);
2298     }
2299     delta = need_pos - splitter_pos;
2300
2301   } else if (need_pos > (splitter_pos + splitter_size)) {
2302     // Set size of all next workareas to zero -->
2303     // recompute size of previous items in splitter
2304     new_this = 0;
2305     new_prev = (splitter_size - new_this) / item_ind;
2306     delta = need_pos - (splitter_pos + splitter_size - new_this);
2307
2308   } else { // required position lays inside this splitter
2309     // Move workarea inside splitter into required position <->
2310     int new_item_rel_pos = need_pos - splitter_pos;
2311     new_prev = new_item_rel_pos / item_ind;
2312     if (need_pos < (splitter_pos + item_rel_pos)) {
2313       // Make previous workareas smaller, next - bigger
2314       // No problem to keep old size of the widget
2315     } else {
2316       // Make previous workareas bigger, next - smaller
2317       if (new_this > splitter_size - new_item_rel_pos) {
2318         new_this = splitter_size - new_item_rel_pos;
2319       }
2320       // jfa to do: in this case fixed size of next widgets could prevent right resizing
2321     }
2322     if (item_ind == nb - 1) {
2323       new_this = splitter_size - new_item_rel_pos;
2324     } else {
2325       new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
2326     }
2327     delta = 0;
2328   }
2329
2330   setSizes (szList, item_ind, new_prev, new_this, new_next);
2331   return delta;
2332 }
2333
2334 /*!
2335   \brief Set position of the widget.
2336
2337   Called from SetRelativePosition() public method.
2338
2339   \param wid   widget to be moved
2340   \param split currently processed splitter (goes from more common
2341                to more particular splitter in recursion calls)
2342   \param o     orientation of positioning
2343   \param need_pos required position of the given widget in pixels
2344                (from top/left side of workstack area)
2345   \param splitter_pos position of the splitter \a split
2346                (from top/left side of workstack area)
2347   \return difference between a required and a distinguished position
2348 */
2349 int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
2350                                const int need_pos, const int splitter_pos )
2351 {
2352   if ( !wid || !split )
2353     return need_pos - splitter_pos;
2354
2355   // Find corresponding sub-splitter.
2356   // Find also index of appropriate item in current splitter.
2357   int cur_ind = 0, item_ind = 0;
2358   bool isBottom = false, isFound = false;
2359   QSplitter* sub_split = NULL;
2360   const QObjectList& objs = split->children();
2361   for ( QObjectList::const_iterator it = objs.begin(); it != objs.end() && !isFound; ++it )
2362   {
2363     QtxWorkstackArea* area = ::qobject_cast<QtxWorkstackArea*>( *it );
2364     if ( area )
2365     {
2366       if ( area->contains( wid ) )
2367       {
2368         item_ind = cur_ind;
2369         isBottom = true;
2370         isFound = true;
2371       }
2372       cur_ind++;
2373     }
2374     else if ( (*it)->inherits( "QSplitter" ) )
2375     {
2376       QList<QtxWorkstackArea*> areaList;
2377       areas( (QSplitter*)(*it), areaList, true );
2378       for ( QList<QtxWorkstackArea*>::iterator ita = areaList.begin(); ita != areaList.end() && !isFound; ++ita )
2379       {
2380         if ( (*ita)->contains( wid ) )
2381         {
2382           item_ind = cur_ind;
2383           isFound = true;
2384           sub_split = (QSplitter*)*it;
2385         }
2386       }
2387       cur_ind++;
2388     }
2389   }
2390
2391   if ( !isFound )
2392     return ( need_pos - splitter_pos );
2393
2394   if ( split->orientation() == o )
2395   {
2396     // Find coordinates of near and far sides of the appropriate item relatively current splitter
2397     int splitter_size = ( o == Qt::Horizontal ? split->width() : split->height() );
2398     QIntList szList = split->sizes();
2399     int nb = szList.count();
2400     int item_rel_pos = 0; // position of near side of item relatively this splitter
2401     for (int i = 0; i < item_ind; i++) {
2402       item_rel_pos += szList[i];
2403     }
2404     int item_size = szList[item_ind]; // size of item
2405     int item_pos = splitter_pos + item_rel_pos;
2406
2407     // Resize splitter items to complete the conditions
2408     if (isBottom) {
2409       // I. Bottom of splitters stack reached
2410
2411       int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
2412       split->setSizes(szList);
2413       // Recompute delta, as some windows can reject given size
2414       int new_item_rel_pos = 0;
2415       QIntList szList1 = split->sizes();
2416       for (int i = 0; i < item_ind; i++) {
2417         new_item_rel_pos += szList1[i];
2418       }
2419       delta = need_pos - (splitter_pos + new_item_rel_pos);
2420       return delta;
2421
2422     } else {
2423       // II. Bottom of splitters stack is not yet reached
2424
2425       if (item_ind == 0) { // cannot move in this splitter
2426         // Process in sub-splitter
2427         return setPosition(wid, sub_split, o, need_pos, splitter_pos);
2428       }
2429
2430       int new_prev = 0;
2431       int new_this = szList[item_ind];
2432       int new_next = 0;
2433
2434       if (need_pos < splitter_pos) {
2435         // Set size of all previous workareas to zero <--
2436         if (item_ind == nb - 1) {
2437           new_this = splitter_size;
2438         } else {
2439           new_next = (splitter_size - new_this) / (nb - item_ind - 1);
2440         }
2441         setSizes (szList, item_ind, new_prev, new_this, new_next);
2442         split->setSizes(szList);
2443         // Recompute splitter_pos, as some windows can reject given size
2444         int new_item_rel_pos = 0;
2445         QIntList szList1 = split->sizes();
2446         for (int i = 0; i < item_ind; i++) {
2447           new_item_rel_pos += szList1[i];
2448         }
2449         // Process in sub-splitter
2450         return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2451       } else if (need_pos > (splitter_pos + splitter_size)) {
2452         // Set size of all next workareas to zero -->
2453         new_prev = (splitter_size - new_this) / item_ind;
2454         setSizes (szList, item_ind, new_prev, new_this, new_next);
2455         split->setSizes(szList);
2456         // Recompute splitter_pos, as some windows can reject given size
2457         int new_item_rel_pos = 0;
2458         QIntList szList1 = split->sizes();
2459         for (int i = 0; i < item_ind; i++) {
2460           new_item_rel_pos += szList1[i];
2461         }
2462         // Process in sub-splitter
2463         return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2464       } else {
2465         // Set appropriate size of all previous/next items <->
2466         int new_item_rel_pos = item_rel_pos;
2467         if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
2468           // Move item inside splitter into required position <->
2469           int new_this = szList[item_ind];
2470           int new_next = 0;
2471           new_item_rel_pos = need_pos - splitter_pos;
2472           if ((item_pos + item_size) < need_pos) {
2473             //new_item_rel_pos = need_pos - (item_pos + item_size);
2474             new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
2475           }
2476           int new_prev = new_item_rel_pos / item_ind;
2477           if (need_pos < (splitter_pos + item_rel_pos)) {
2478             // Make previous workareas smaller, next - bigger
2479             // No problem to keep old size of the widget
2480           } else {
2481             // Make previous workareas bigger, next - smaller
2482             if (new_this > splitter_size - new_item_rel_pos) {
2483               new_this = splitter_size - new_item_rel_pos;
2484             }
2485           }
2486           if (item_ind == nb - 1) {
2487             new_this = splitter_size - new_item_rel_pos;
2488           } else {
2489             new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
2490           }
2491           setSizes (szList, item_ind, new_prev, new_this, new_next);
2492           split->setSizes(szList);
2493           // Recompute new_item_rel_pos, as some windows can reject given size
2494           new_item_rel_pos = 0;
2495           QIntList szList1 = split->sizes();
2496           for (int i = 0; i < item_ind; i++) {
2497             new_item_rel_pos += szList1[i];
2498           }
2499         } else {
2500           // Do nothing
2501         }
2502         // Process in sub-splitter
2503         int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2504         if (add_pos == 0)
2505           return 0;
2506
2507         // this can be if corresponding workarea is first in sub-splitter
2508         // or sub-splitter has another orientation
2509
2510         // Resize ones again to reach precize position <->
2511         int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
2512
2513         // Move workarea inside splitter into required position <->
2514         int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
2515                                      new_item_rel_pos, need_pos_1, splitter_pos);
2516         split->setSizes(szList);
2517         // Recompute new_item_rel_pos, as some windows can reject given size
2518         new_item_rel_pos = 0;
2519         QIntList szList1 = split->sizes();
2520         for (int i = 0; i < item_ind; i++) {
2521           new_item_rel_pos += szList1[i];
2522         }
2523         delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
2524         return delta_1;
2525       }
2526     }
2527   } else {
2528     return setPosition(wid, sub_split, o, need_pos, splitter_pos);
2529   }
2530
2531   return 0;
2532 }
2533
2534 /*!
2535   \brief Redistribute space among widgets equally.
2536   \param split splitter
2537 */
2538 void QtxWorkstack::distributeSpace( QSplitter* split ) const
2539 {
2540   if ( !split )
2541     return;
2542
2543   QIntList szList = split->sizes();
2544   int size = ( split->orientation() == Qt::Horizontal ?
2545                split->width() : split->height() ) / szList.count();
2546   for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
2547     *it = size;
2548   split->setSizes( szList );
2549 }
2550
2551 /*!
2552   \brief Split widgets vertically.
2553 */
2554 void QtxWorkstack::splitVertical()
2555 {
2556   split( Qt::Horizontal );
2557 }
2558
2559 /*!
2560   \brief Split widgets horizontally.
2561 */
2562 void QtxWorkstack::splitHorizontal()
2563 {
2564   split( Qt::Vertical );
2565 }
2566
2567 /*!
2568   \brief Called when user activates "Rename" menu item.
2569
2570   Changes widget title.
2571 */
2572 void QtxWorkstack::onRename()
2573 {
2574   if ( !myWorkWin )
2575     return;
2576
2577   bool ok = false;
2578   QString newName = QInputDialog::getText( topLevelWidget(),  tr( "Rename" ), tr( "Enter new name:" ),
2579                                            QLineEdit::Normal, myWorkWin->windowTitle(), &ok );
2580   if ( ok && !newName.isEmpty() )
2581     myWorkWin->setWindowTitle( newName );
2582 }
2583
2584 /*!
2585   \brief Wrap area into the new splitter.
2586   \param workarea
2587   \return new splitter
2588 */
2589 QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
2590 {
2591   if ( !area )
2592     return 0;
2593
2594   QSplitter* pSplit = splitter( area );
2595   if ( !pSplit )
2596     return 0;
2597
2598   bool upd = pSplit->updatesEnabled();
2599   pSplit->setUpdatesEnabled( false );
2600
2601   QIntList szList = pSplit->sizes();
2602
2603   QSplitter* wrap = new QtxWorkstackSplitter( 0 );
2604   pSplit->insertWidget( pSplit->indexOf( area ) + 1, wrap );
2605   wrap->setVisible( true );
2606   wrap->addWidget( area );
2607
2608   pSplit->setSizes( szList );
2609
2610   pSplit->setUpdatesEnabled( upd );
2611
2612   return wrap;
2613 }
2614
2615 /*!
2616   \brief Reparent and add widget.
2617   \param wid widget
2618   \param pWid parent widget
2619   \param after widget after which \a wid should be added
2620 */
2621 void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
2622 {
2623   if ( !wid || !pWid )
2624     return;
2625
2626   QWidgetList moveList;
2627   const QObjectList& lst = pWid->children();
2628   bool found = false;
2629   for ( QObjectList::const_iterator it = lst.begin(); it != lst.end(); ++it )
2630   {
2631     if ( found && ( (*it)->inherits( "QSplitter" ) ||
2632                     (*it)->inherits( "QtxWorkstackArea" ) ) )
2633       moveList.append( (QWidget*)(*it) );
2634     if ( *it == after )
2635       found = true;
2636   }
2637
2638   QMap<QWidget*, bool> map;
2639   for ( QWidgetList::iterator it = moveList.begin(); it != moveList.end(); ++it )
2640   {
2641     map.insert( *it, (*it)->isVisibleTo( (*it)->parentWidget() ) );
2642     (*it)->setParent( 0 );
2643     (*it)->hide();
2644   }
2645
2646   wid->setParent( pWid );
2647
2648   for ( QWidgetList::iterator itr = moveList.begin(); itr != moveList.end(); ++itr )
2649   {
2650     (*itr)->setParent( pWid );
2651     (*itr)->setVisible( map.contains( *itr ) ? map[*itr] : false );
2652   }
2653 }
2654
2655 /*!
2656   \brief Close active window.
2657 */
2658 void QtxWorkstack::onCloseWindow()
2659 {
2660   if ( myWorkWin )
2661     myWorkWin->close();
2662   else if( activeWindow() )
2663     activeWindow()->close();
2664 }
2665
2666 /*!
2667   \brief Called when workarea is destroyed.
2668
2669   Set input focus to the neighbour area.
2670
2671   \param obj workarea being destroyed
2672 */
2673 void QtxWorkstack::onDestroyed( QObject* obj )
2674 {
2675   QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
2676
2677   if ( area == myArea )
2678     myArea = 0;
2679
2680   if ( !myArea )
2681   {
2682     QtxWorkstackArea* cur = neighbourArea( area );
2683     if ( cur )
2684       cur->setFocus();
2685   }
2686
2687   QApplication::postEvent( this, new QEvent( QEvent::User ) );
2688 }
2689
2690 /*!
2691   \brief Called on window activating.
2692   \param area workarea being activated (not used)
2693 */
2694 void QtxWorkstack::onWindowActivated( QWidget* /*area*/ )
2695 {
2696   const QObject* obj = sender();
2697   if ( !obj->inherits( "QtxWorkstackArea" ) )
2698     return;
2699
2700   setActiveArea( (QtxWorkstackArea*)obj );
2701 }
2702
2703 /*!
2704   \brief Called on window deactivating.
2705   \param area workarea being deactivated
2706 */
2707 void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
2708 {
2709   if ( myArea != area )
2710     return;
2711
2712   QList<QtxWorkstackArea*> lst;
2713   areas( mySplit, lst, true );
2714
2715   int idx = lst.indexOf( area );
2716   if ( idx == -1 )
2717     return;
2718
2719   myWin = 0;
2720   myArea = 0;
2721
2722   QtxWorkstackArea* newArea = neighbourArea( area );
2723   if ( newArea && newArea->activeWidget() )
2724     newArea->activeWidget()->setFocus();
2725
2726   QApplication::postEvent( this, new QEvent( QEvent::User ) );
2727 }
2728
2729 /*!
2730   \brief Create and show popup menu for workarea.
2731   \param w workarea
2732   \param p popup position
2733 */
2734 void QtxWorkstack::onContextMenuRequested( QWidget* w, QPoint p )
2735 {
2736   QtxWorkstackArea* anArea = ::qobject_cast<QtxWorkstackArea*>( (QObject*)sender() );
2737   if ( !anArea )
2738     anArea = activeArea();
2739
2740   if ( !anArea )
2741     return;
2742
2743   QWidgetList lst = anArea->widgetList();
2744   if ( lst.isEmpty() )
2745     return;
2746
2747   myWorkWin = w;
2748   myWorkArea = anArea;
2749
2750   QMenu* pm = new QMenu();
2751
2752   if ( lst.count() > 1 )
2753   {
2754     if ( !myActionsMap[SplitVertical]->isEnabled() )
2755       myActionsMap[SplitVertical]->setEnabled(true);
2756     pm->addAction( myActionsMap[SplitVertical] );
2757     if ( !myActionsMap[SplitHorizontal]->isEnabled() )
2758       myActionsMap[SplitHorizontal]->setEnabled(true);
2759     pm->addAction( myActionsMap[SplitHorizontal] );
2760     pm->addSeparator();
2761   }
2762
2763   if ( w )
2764   {
2765     if ( myActionsMap[Close]->isEnabled() )
2766       pm->addAction( myActionsMap[Close] );
2767     if ( myActionsMap[Rename]->isEnabled() )
2768       pm->addAction( myActionsMap[Rename] );
2769   }
2770
2771   Qtx::simplifySeparators( pm );
2772
2773   if ( !pm->actions().isEmpty() )
2774     pm->exec( p );
2775
2776   delete pm;
2777
2778   myWorkWin = 0;
2779   myWorkArea = 0;
2780 }
2781
2782 /*!
2783   \brief Add child widget.
2784   \param w widget
2785   \param f widget flags
2786   \return child widget container
2787 */
2788 QWidget* QtxWorkstack::addWindow( QWidget* w, Qt::WindowFlags f )
2789 {
2790   if ( !w )
2791     return 0;
2792
2793   return targetArea()->insertWidget( w, -1, f );
2794 }
2795
2796 /*!
2797   \brief Handle custom events.
2798   \param e custom event (not used)
2799 */
2800 void QtxWorkstack::customEvent( QEvent* /*e*/ )
2801 {
2802   updateState();
2803 }
2804
2805 /*!
2806   \brief Get splitter corresponding to the workarea.
2807   \param workarea
2808   \return splitter corresponding to the workarea
2809 */
2810 QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
2811 {
2812   if ( !area )
2813     return 0;
2814
2815   QSplitter* split = 0;
2816
2817   QWidget* wid = area->parentWidget();
2818   if ( wid && wid->inherits( "QSplitter" ) )
2819     split = (QSplitter*)wid;
2820
2821   return split;
2822 }
2823
2824 /*!
2825   \brief Get list of child splitters.
2826   \param split parent splitter
2827   \param splitList list to be filled with child splitters
2828   \param rec if \c true, perform recursive search of children
2829 */
2830 void QtxWorkstack::splitters( QSplitter* split, QList<QSplitter*>& splitList, const bool rec ) const
2831 {
2832   if ( !split )
2833     return;
2834
2835   const QObjectList& objs = split->children();
2836   for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
2837   {
2838     if ( rec )
2839       splitters( (QSplitter*)*it, splitList, rec );
2840     if ( (*it)->inherits( "QSplitter" ) )
2841       splitList.append( (QSplitter*)*it );
2842   }
2843 }
2844
2845 /*!
2846   \brief Get list of child workareas.
2847   \param split parent splitter
2848   \param areaList list to be filled with child workareas
2849   \param rec if \c true, perform recursive search of children
2850 */
2851 void QtxWorkstack::areas( QSplitter* split, QList<QtxWorkstackArea*>& areaList, const bool rec ) const
2852 {
2853   if ( !split )
2854     return;
2855
2856   const QObjectList& objs = split->children();
2857   for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
2858   {
2859     if ( (*it)->inherits( "QtxWorkstackArea" ) )
2860       areaList.append( (QtxWorkstackArea*)*it );
2861     else if ( rec && (*it)->inherits( "QSplitter" ) )
2862       areas( (QSplitter*)*it, areaList, rec );
2863   }
2864 }
2865
2866 /*!
2867   \brief Get active workarea.
2868   \return active workarea
2869 */
2870 QtxWorkstackArea* QtxWorkstack::activeArea() const
2871 {
2872   return myArea;
2873 }
2874
2875 /*!
2876   \brief Get target area (for which the current operation should be done).
2877
2878   Returns active workarea or current area (if there is no active workarea).
2879   If there are no workareas, create new workarea and return it.
2880
2881   \return workarea
2882 */
2883 QtxWorkstackArea* QtxWorkstack::targetArea()
2884 {
2885   QtxWorkstackArea* area = activeArea();
2886   if ( !area )
2887     area = currentArea();
2888   if ( !area )
2889   {
2890     QList<QtxWorkstackArea*> lst;
2891     areas( mySplit, lst );
2892     if ( !lst.isEmpty() )
2893       area = lst.first();
2894   }
2895
2896   if ( !area )
2897     area = createArea( mySplit );
2898
2899   return area;
2900 }
2901
2902 /*!
2903   \brief Get current workarea.
2904
2905   Current workarea is that one which has input focus.
2906
2907   \return current area
2908 */
2909 QtxWorkstackArea* QtxWorkstack::currentArea() const
2910 {
2911   QtxWorkstackArea* area = 0;
2912   QWidget* wid = focusWidget();
2913   while ( wid && !area )
2914   {
2915     if ( wid->inherits( "QtxWorkstackArea" ) )
2916       area = (QtxWorkstackArea*)wid;
2917     wid = wid->parentWidget();
2918   }
2919
2920   return area;
2921 }
2922
2923 /*!
2924   \brief Create new workarea.
2925   \param parent parent widget
2926   \return created workarea
2927 */
2928 QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
2929 {
2930   QtxWorkstackArea* area = new QtxWorkstackArea( parent );
2931
2932   connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
2933   connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
2934   connect( area, SIGNAL( contextMenuRequested( QWidget*, QPoint ) ),
2935            this, SLOT( onContextMenuRequested( QWidget*, QPoint ) ) );
2936   connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
2937
2938   return area;
2939 }
2940
2941 /*!
2942   \brief Set active workarea.
2943   \param workarea
2944 */
2945 void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
2946 {
2947   QWidget* oldCur = myWin;
2948
2949   QtxWorkstackArea* oldArea = myArea;
2950
2951   myArea = area;
2952
2953   if ( myArea != oldArea )
2954   {
2955     if ( oldArea )
2956       oldArea->updateActiveState();
2957     if ( myArea )
2958       myArea->updateActiveState();
2959   }
2960
2961   if ( myArea )
2962     myWin = myArea->activeWidget();
2963
2964   if ( myWin && oldCur != myWin )
2965     emit windowActivated( myWin );
2966 }
2967
2968 /*!
2969   \brief Get workarea which is nearest to \a area.
2970   \param area area for which neighbour is searched
2971   \return neighbour area (or 0 if not found)
2972 */
2973 QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
2974 {
2975   QList<QtxWorkstackArea*> lst;
2976   areas( mySplit, lst, true );
2977   int pos = lst.indexOf( area );
2978   if ( pos < 0 )
2979     return 0;
2980
2981   QtxWorkstackArea* na = 0;
2982   for ( int i = pos - 1; i >= 0 && !na; i-- )
2983   {
2984     if ( !lst.at( i )->isEmpty() )
2985       na = lst.at( i );
2986   }
2987
2988   for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
2989   {
2990     if ( !lst.at( j )->isEmpty() )
2991         na = lst.at( j );
2992   }
2993   return na;
2994 }
2995
2996 /*!
2997   \brief Get workarea covering point.
2998   \return workarea
2999   \param p point
3000 */
3001 QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
3002 {
3003   QtxWorkstackArea* area = 0;
3004   QList<QtxWorkstackArea*> lst;
3005   areas( mySplit, lst, true );
3006   for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end() && !area; ++it )
3007   {
3008     QtxWorkstackArea* cur = *it;
3009     QRect r = cur->geometry();
3010     if ( cur->parentWidget() )
3011       r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
3012     if ( r.contains( p ) )
3013       area = cur;
3014   }
3015   return area;
3016 }
3017
3018 /*!
3019   \brief Update internal state.
3020 */
3021 void QtxWorkstack::updateState()
3022 {
3023   updateState( mySplit );
3024 }
3025
3026 /*!
3027   \brief Update splitter state.
3028   \param split splitter to be updated
3029 */
3030 void QtxWorkstack::updateState( QSplitter* split )
3031 {
3032   QList<QSplitter*> recList;
3033   splitters( split, recList, false );
3034   for ( QList<QSplitter*>::iterator itr = recList.begin(); itr != recList.end(); ++itr )
3035     updateState( *itr );
3036
3037   QList<QSplitter*> splitList;
3038   splitters( split, splitList, false );
3039
3040   QList<QtxWorkstackArea*> areaList;
3041   areas( split, areaList, false );
3042
3043   bool vis = false;
3044   for ( QList<QtxWorkstackArea*>::iterator it = areaList.begin(); it != areaList.end(); ++it )
3045   {
3046     if ( (*it)->isEmpty() )
3047       (*it)->hide();
3048     else
3049     {
3050       (*it)->show();
3051       vis = true;
3052     }
3053   }
3054
3055   if ( split == mySplit )
3056     return;
3057
3058   for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end() && !vis; ++iter )
3059     vis = (*iter)->isVisibleTo( (*iter)->parentWidget() );
3060
3061   if ( areaList.isEmpty() && splitList.isEmpty() )
3062     delete split;
3063   else
3064     split->setVisible( vis );
3065 }
3066
3067 /*!
3068   \brief Dump workstack configuration to the state description array.
3069   \param version number
3070   \return state byte array.
3071 */
3072 QByteArray QtxWorkstack::saveState( int version ) const
3073 {
3074   QByteArray data;
3075
3076   QDataStream stream( &data, QIODevice::WriteOnly );
3077   stream << QtxWorkstack::VersionMarker;
3078   stream << version;
3079   saveState( stream );
3080
3081   return data;
3082 }
3083
3084 /*!
3085   \brief Restore workstack configuration from the state description array.
3086   \param version number
3087   \return restore performing state
3088 */
3089 bool QtxWorkstack::restoreState( const QByteArray& state, int version )
3090 {
3091   if ( state.isEmpty() )
3092     return false;
3093
3094   QByteArray sd = state;
3095   QDataStream stream( &sd, QIODevice::ReadOnly );
3096   int marker, ver;
3097   stream >> marker;
3098   stream >> ver;
3099   if ( stream.status() != QDataStream::Ok || marker != QtxWorkstack::VersionMarker || ver != version )
3100     return false;
3101
3102   return restoreState( stream );
3103 }
3104
3105 void QtxWorkstack::saveState( QDataStream& stream ) const
3106 {
3107   mySplit->saveState( stream );
3108 }
3109
3110 bool QtxWorkstack::restoreState( QDataStream& stream )
3111 {
3112   QMap<QString, QtxWorkstackChild*> map;
3113   QList<QtxWorkstackArea*> areaList;
3114   areas( mySplit, areaList, true );
3115   for ( QList<QtxWorkstackArea*>::const_iterator it = areaList.begin(); it != areaList.end(); ++it )
3116   {
3117     QtxWorkstackArea* area = *it;
3118     QList<QtxWorkstackChild*> childList = area->childList();
3119     for ( QList<QtxWorkstackChild*>::iterator itr = childList.begin(); itr != childList.end(); ++itr )
3120     {
3121       QtxWorkstackChild* c = *itr;
3122       if ( !c->widget() )
3123         continue;
3124
3125       map.insert( c->widget()->objectName(), c );
3126
3127       qDebug( "QtxWorkstack::restoreState: found widget \"%s\"", (const char*)c->widget()->objectName().toUtf8() );
3128     }
3129   }
3130
3131   int marker;
3132   stream >> marker;
3133   if ( stream.status() != QDataStream::Ok || marker != QtxWorkstack::SplitMarker )
3134     return false;
3135
3136   QtxWorkstackSplitter* split = new QtxWorkstackSplitter( this );
3137   if ( layout() )
3138     layout()->addWidget( split );
3139
3140   bool ok = split->restoreState( stream, map );
3141   if ( !ok )
3142     delete split;
3143   else
3144   {
3145     mySplit->deleteLater();
3146     mySplit = split;
3147
3148     QList<QtxWorkstackArea*> aList;
3149     areas( mySplit, aList, true );
3150
3151     QtxWorkstackArea* a = !aList.isEmpty() ? aList.first() : 0;
3152     for ( QMap<QString, QtxWorkstackChild*>::const_iterator it = map.begin(); it != map.end(); ++it )
3153     {
3154       QtxWorkstackChild* c = it.value();
3155       if ( c->widget() )
3156         c->widget()->setVisible( false );
3157       if ( a )
3158         a->insertChild( c );
3159       else
3160         c->setVisible( false );
3161     }
3162   }
3163
3164   return ok;
3165 }
3166
3167 /*!
3168   \brief Set resize mode of all splitters opaque or transparent.
3169   \param opaque opaque mode
3170 */
3171 void QtxWorkstack::setOpaqueResize( bool opaque )
3172 {
3173   QList<QSplitter*> splitList;
3174   splitters( mySplit, splitList, true );
3175   splitList << mySplit;
3176   foreach( QSplitter* split, splitList )
3177     split->setOpaqueResize( opaque );
3178 }
3179
3180 /*!
3181   \brief Get resize mode of all splitters: opaque (\c true) or transparent (\c false).
3182   \return current opaque mode
3183 */
3184 bool QtxWorkstack::opaqueResize() const
3185 {
3186   return mySplit->opaqueResize();
3187 }
3188
3189 /*!
3190   \brief Show/hide splitter state and area.
3191   \param wid widget (and parent area) will be shown/hidden
3192   \param parent_list parent splitters list
3193   \param split splitter will be shown/hidden
3194   \param visible splitter
3195 */
3196 void QtxWorkstack::splitterVisible(QWidget* wid, QList<QSplitter*>& parent_list, QSplitter* split, bool visible)
3197 {
3198   QList<QSplitter*> recList;
3199   splitters( split, recList, false );
3200   for ( QList<QSplitter*>::iterator itr = recList.begin(); itr != recList.end(); ++itr ) {
3201     parent_list.prepend( *itr );
3202     splitterVisible( wid, parent_list, *itr, visible );
3203   }
3204
3205   QList<QtxWorkstackArea*> areaList;
3206   areas( split, areaList, false );
3207   for ( QList<QtxWorkstackArea*>::const_iterator it = areaList.begin(); it != areaList.end(); ++it ) {
3208     QtxWorkstackArea* area = *it;
3209     bool isCurrentWidget = false;
3210
3211     area->showTabBar(visible);
3212
3213     // 1. Looking for the selected widget at the lowest level among all splitted areas.
3214     QList<QtxWorkstackChild*> childList = area->childList();
3215     for ( QList<QtxWorkstackChild*>::iterator itr = childList.begin(); itr != childList.end(); ++itr ) {
3216       QWidget* aCurWid = (*itr)->widget();
3217       if ( aCurWid == wid ) {
3218         isCurrentWidget = true;
3219         aCurWid->setVisible( true );
3220       }
3221       else
3222         aCurWid->setVisible( visible );
3223     }
3224
3225     // 2. Show/Hide other areas and widgets that don't contain the desired widget
3226     if ( !isCurrentWidget || visible )
3227       area->setVisible( visible );
3228
3229     if ( !isCurrentWidget && !visible )
3230       continue;
3231
3232     // 3. Show/hide all parent widgets
3233     QSplitter* pSplit = splitter( area );
3234     int count = pSplit->count();
3235     for ( int i = 0; i < count; i++ ) {
3236       if ( pSplit->indexOf( area ) == i && !visible )
3237         continue;
3238       pSplit->widget(i)->setVisible( visible );
3239     }
3240
3241     // 4. Show/hide all parent splitters don't contain the selected widget
3242     if ( visible )
3243       pSplit->setVisible( true );
3244
3245     if ( isCurrentWidget && !visible ) {
3246       for ( QList<QSplitter*>::iterator itr = parent_list.begin(); itr != parent_list.end() && pSplit != mySplit; ++itr ) {
3247         if ( pSplit == *itr )
3248           continue;
3249         QList<QSplitter*> splitList;
3250         splitters( *itr, splitList, false );
3251         for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end(); ++iter ) {
3252           if ( pSplit == (*iter) ) {
3253             pSplit = *itr;
3254             continue;
3255           }
3256           (*iter)->setVisible( false );
3257         }
3258       }
3259     }
3260   }
3261 }
3262
3263 /*!
3264   \brief Show/hide splitters state and area.
3265   \param wid widget (and parent area) will be shown/hidden
3266   \param visible splitters
3267 */
3268 void QtxWorkstack::splittersVisible( QWidget* wid, bool visible )
3269 {
3270   QList<QSplitter*> parent_list;
3271   parent_list.append( mySplit );
3272   splitterVisible( wid, parent_list, mySplit, visible );
3273 }
3274
3275 /*!
3276   \fn void QtxWorkstack::windowActivated( QWidget* w )
3277   \brief Emitted when the workstack's child widget \w is activated.
3278   \param w widget being activated
3279 */
3280
3281 /*!
3282   \brief Gets area containing given widget
3283   \param wid widget
3284   \return pointer to QtxWorkstackArea* object
3285 */
3286 QtxWorkstackArea* QtxWorkstack::wgArea( QWidget* wid ) const
3287 {
3288   QtxWorkstackArea* resArea = 0;
3289
3290   QList<QtxWorkstackArea*> areaList;
3291   areas( mySplit, areaList, true );
3292
3293   QList<QtxWorkstackArea*>::ConstIterator it;
3294   for ( it = areaList.begin(); it != areaList.end() && !resArea; ++it )
3295   {
3296     if ( (*it)->contains( wid ) )
3297       resArea = *it;
3298   }
3299
3300   return resArea;
3301 }
3302
3303 /*!
3304   \brief Moves the first widget to the same area which the second widget belongs to
3305   \param wid widget to be moved
3306   \param wid_to widget specified the destination area
3307   \param before specifies whether the first widget has to be moved before or after
3308          the second widget
3309   \return \c true if operation is completed successfully, \c false otherwise
3310 */
3311 bool QtxWorkstack::move( QWidget* wid, QWidget* wid_to, const bool before )
3312 {
3313   if ( wid && wid_to )
3314   {
3315     QtxWorkstackArea* area_src = wgArea( wid );
3316     QtxWorkstackArea* area_to = wgArea( wid_to );
3317     if ( area_src && area_to )
3318     {
3319       // find index of the second widget
3320       QWidgetList wgList = area_to->widgetList();
3321       QWidgetList::ConstIterator it;
3322       int idx = 0;
3323       for ( it = wgList.begin(); it != wgList.begin(); ++it, idx++ )
3324       {
3325         if ( *it == wid_to )
3326           break;
3327       }
3328
3329       if ( idx < wgList.count() ) // paranoidal check
3330       {
3331         if ( !before )
3332           idx++;
3333         area_src->removeWidget( wid, true );
3334         area_to->insertWidget( wid, idx );
3335         wid->showMaximized();
3336         return true;
3337       }
3338     }
3339   }
3340   return false;
3341 }
3342
3343 /*!
3344   \brief Group all windows in one area
3345   \return \c true if operation is completed successfully, \c false otherwise
3346 */
3347 void QtxWorkstack::stack()
3348 {
3349   QWidgetList wgList = windowList();
3350   if ( !wgList.count() )
3351     return; // nothing to do
3352
3353   QtxWorkstackArea* area_to = 0;
3354   QWidgetList::ConstIterator it;
3355   for ( it = wgList.begin(); it != wgList.end(); ++it )
3356   {
3357     QtxWorkstackArea* area_src = 0;
3358     if ( !area_to )
3359     {
3360       area_to = wgArea( *it );
3361       area_src = area_to;
3362     }
3363     else
3364       area_src = wgArea( *it );
3365
3366     if ( area_src != area_to )
3367     {
3368       area_src->removeWidget( *it, true );
3369       area_to->insertWidget( *it, -1 );
3370       (*it)->showMaximized();
3371     }
3372   }
3373 }
3374
3375 QAction* QtxWorkstack::action( const int id ) const
3376 {
3377   return myActionsMap.contains( id ) ? myActionsMap[id] : 0;
3378 }