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