Salome HOME
Update copyrights 2014.
[modules/gui.git] / src / Qtx / QtxWorkstack.cxx
1 // Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File:      QtxWorkstack.cxx
24 // Author:    Sergey TELKOV
25 //
26 #include "QtxWorkstack.h"
27
28 #include "QtxAction.h"
29
30 #include <QMenu>
31 #include <QStyle>
32 #include <QRegExp>
33 #include <QLayout>
34 #include <QPainter>
35 #include <QDataStream>
36 #include <QFocusEvent>
37 #include <QMouseEvent>
38 #include <QRubberBand>
39 #include <QApplication>
40 #include <QStyleOption>
41 #include <QInputDialog>
42 #include <QStackedWidget>
43 #include <QAbstractButton>
44
45 /*!
46   \class QtxWorkstackArea::WidgetEvent
47   \internal
48   \brief Internal class used to forward child widgets events to the workarea
49 */
50
51 class QtxWorkstackArea::WidgetEvent : public QEvent
52 {
53 public:
54   WidgetEvent( Type t, QtxWorkstackChild* w = 0 ) : QEvent( t ), myChild( w ) {};
55
56   QtxWorkstackChild* child() const { return myChild; }
57
58 private:
59   QtxWorkstackChild* myChild;   // event receiver widget
60 };
61
62 /*!
63   \class QtxWorkstackArea::RestoreEvent
64   \internal
65   \brief Internal class used to forward restore info events to the workarea
66 */
67
68 class QtxWorkstackArea::RestoreEvent : public QtxWorkstackArea::WidgetEvent
69 {
70 public:
71   RestoreEvent( Type t, int id, int f, QtxWorkstackChild* w )
72   : WidgetEvent( t, w ), myId( id ), myFlags( f ) {};
73
74   int                id() const { return myId; }
75   int                flags() const { return myFlags; }
76
77 private:
78   int                myId;
79   int                myFlags;
80 };
81
82 /*!
83   \class QtxWorkstackDrag
84   \internal
85   \brief Workstack drag object
86 */
87
88 /*!
89   \brief Constructor.
90   \param ws parent workstack
91   \param child child widget container
92 */
93 QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
94 : QObject( 0 ),
95   myWS( ws ),
96   myChild( child ),
97   myTab( -1 ),
98   myArea( 0 ),
99   myTabRect( 0 ),
100   myAreaRect( 0 )
101 {
102   QApplication::instance()->installEventFilter( this );
103 }
104
105 /*!
106   \brief Destructor.
107 */
108 QtxWorkstackDrag::~QtxWorkstackDrag()
109 {
110   QApplication::instance()->removeEventFilter( this );
111
112   endDrawRect();
113 }
114
115 /*!
116   \brief Custom event filter.
117   \param o event receiver widget
118   \param e event
119   \return \c true if event should be filtered (stop further processing)
120 */
121 bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
122 {
123   switch ( e->type() )
124   {
125   case QEvent::MouseMove:
126     updateTarget( ((QMouseEvent*)e)->globalPos() );
127     break;
128   case QEvent::MouseButtonRelease:
129     drawRect();
130     endDrawRect();
131     dropWidget();
132     deleteLater();
133     break;
134   default:
135     return false;
136   }
137   return true;
138 }
139
140 /*!
141   \brief Detect and set dropping target widget.
142   \param p current dragging position
143 */
144 void QtxWorkstackDrag::updateTarget( const QPoint& p )
145 {
146   int tab = -1;
147   QtxWorkstackArea* area = detectTarget( p, tab );
148   setTarget( area, tab );
149 }
150
151 /*!
152   \brief Detect dropping target.
153   \param p current dragging position
154   \param tab resulting target tab page index
155   \return target workarea or 0 if there is no correct drop target
156 */
157 QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
158 {
159   if ( p.isNull() )
160     return 0;
161
162   QtxWorkstackArea* area = myWS->areaAt( p );
163   if ( area )
164     tab = area->tabAt( p );
165   return area;
166 }
167
168 /*!
169   \brief Set dropping target.
170   \param area new target workarea
171   \param tab target workarea's tab page index
172 */
173 void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
174 {
175   if ( !area || ( myArea == area && tab == myTab ) )
176     return;
177
178   startDrawRect();
179
180   if ( myArea )
181     drawRect();
182
183   myTab = tab;
184   myArea = area;
185
186   if ( myArea )
187     drawRect();
188 }
189
190 /*!
191   \brief Called when drop operation is finished.
192
193   Inserts dropped widget to the target workarea.
194 */
195 void QtxWorkstackDrag::dropWidget()
196 {
197   if ( myArea )
198     myArea->insertChild( myChild, myTab );
199 }
200
201 /*!
202   \brief Draw floating rectangle.
203 */
204 void QtxWorkstackDrag::drawRect()
205 {
206   if ( !myArea )
207     return;
208
209   QRect r = myArea->floatRect();
210   int m = 2;
211
212   r.setTop( r.top() + m + 2 );
213   r.setLeft( r.left() + m + 2 );
214   r.setRight( r.right() - m - 2 );
215   r.setBottom( r.bottom() - m - 2 );
216
217   if ( myAreaRect )
218   {
219     myAreaRect->setGeometry( r );
220     myAreaRect->setVisible( r.isValid() );
221   }
222
223   QRect tr = myArea->floatTab( myTab );
224
225   tr.setTop( tr.top() + m );
226   tr.setLeft( tr.left() + m );
227   tr.setRight( tr.right() - m );
228   tr.setBottom( tr.bottom() - m );
229
230   if ( myTabRect )
231   {
232     myTabRect->setGeometry( tr );
233     myTabRect->setVisible( tr.isValid() );
234   }
235 }
236
237 /*!
238   \brief Delete rubber band on the end on the dragging operation.
239 */
240 void QtxWorkstackDrag::endDrawRect()
241 {
242   delete myAreaRect;
243   myAreaRect = 0;
244
245   delete myTabRect;
246   myTabRect = 0;
247 }
248
249 /*!
250   \brief Create rubber band to be drawn on the dragging operation.
251 */
252 void QtxWorkstackDrag::startDrawRect()
253 {
254   if ( !myTabRect )
255     myTabRect = new QRubberBand( QRubberBand::Rectangle );
256
257   myTabRect->hide();
258
259   if ( !myAreaRect )
260     myAreaRect = new QRubberBand( QRubberBand::Rectangle );
261
262   myAreaRect->hide();
263 }
264
265
266 /*
267   \class CloseButton
268   \brief Workstack area close button.
269   \internal
270 */
271 class CloseButton : public QAbstractButton
272 {
273 public:
274   CloseButton( QWidget* );
275
276   QSize        sizeHint() const;
277   QSize        minimumSizeHint() const;
278
279   void enterEvent( QEvent* );
280   void leaveEvent( QEvent* );
281   void paintEvent( QPaintEvent* );
282 };
283
284 /*!
285   \brief Constructor
286   \internal
287   \param parent parent widget
288 */
289 CloseButton::CloseButton( QWidget* parent )
290 : QAbstractButton( parent )
291 {
292  setFocusPolicy( Qt::NoFocus );
293 }
294
295 /*!
296   \brief Get appropriate size for the button.
297   \internal
298   \return size value
299 */
300 QSize CloseButton::sizeHint() const
301 {
302   ensurePolished();
303   int dim = 0;
304   if( !icon().isNull() )
305   {
306     const QPixmap pm = icon().pixmap( style()->pixelMetric( QStyle::PM_SmallIconSize ),
307                                       QIcon::Normal );
308     dim = qMax( pm.width(), pm.height() );
309   }
310   return QSize( dim + 4, dim + 4 );
311 }
312
313 /*!
314   \brief Get minimum appropriate size for the button.
315   \internal
316   \return minimum size value
317 */
318 QSize CloseButton::minimumSizeHint() const
319 {
320   return sizeHint();
321 }
322
323 /*!
324   \brief Process mouse enter event.
325   \internal
326   \param event mouse enter event
327 */
328 void CloseButton::enterEvent( QEvent *event )
329 {
330   if ( isEnabled() )
331     update();
332   QAbstractButton::enterEvent( event );
333 }
334
335 /*!
336   \brief Process mouse leave event.
337   \internal
338   \param event mouse leave event
339 */
340 void CloseButton::leaveEvent( QEvent *event )
341 {
342   if( isEnabled() )
343     update();
344   QAbstractButton::leaveEvent( event );
345 }
346
347 /*!
348   \brief Process paint event.
349   \internal
350   \param event paint event
351 */
352 void CloseButton::paintEvent( QPaintEvent* )
353 {
354   QPainter p( this );
355
356   QRect r = rect();
357   QStyleOption opt;
358   opt.init( this );
359   opt.state |= QStyle::State_AutoRaise;
360   if ( isEnabled() && underMouse() && !isChecked() && !isDown() )
361     opt.state |= QStyle::State_Raised;
362   if ( isChecked() )
363     opt.state |= QStyle::State_On;
364   if ( isDown() )
365     opt.state |= QStyle::State_Sunken;
366   style()->drawPrimitive( QStyle::PE_PanelButtonTool, &opt, &p, this );
367
368   int shiftHorizontal = opt.state & QStyle::State_Sunken ? style()->pixelMetric( QStyle::PM_ButtonShiftHorizontal, &opt, this ) : 0;
369   int shiftVertical = opt.state & QStyle::State_Sunken ? style()->pixelMetric( QStyle::PM_ButtonShiftVertical, &opt, this ) : 0;
370
371   r.adjust( 2, 2, -2, -2 );
372   r.translate( shiftHorizontal, shiftVertical );
373
374   QPixmap pm = icon().pixmap( style()->pixelMetric( QStyle::PM_SmallIconSize ), isEnabled() ?
375                               underMouse() ? QIcon::Active : QIcon::Normal
376                               : QIcon::Disabled,
377                               isDown() ? QIcon::On : QIcon::Off );
378   style()->drawItemPixmap( &p, r, Qt::AlignCenter, pm );
379 }
380
381
382 /*!
383   \class QtxWorkstackSplitter
384   \internal
385   \brief Workstack splitter.
386 */
387
388 /*!
389   \brief Constructor.
390   \param parent parent widget
391 */
392 QtxWorkstackSplitter::QtxWorkstackSplitter( QWidget* parent )
393 : QSplitter( parent )
394 {
395   setChildrenCollapsible( false );
396 }
397
398 /*!
399   \brief Destructor.
400 */
401 QtxWorkstackSplitter::~QtxWorkstackSplitter()
402 {
403 }
404
405 /*!
406   \brief Get parent workstack
407   \return workstack owning this workarea
408 */
409 QtxWorkstack* QtxWorkstackSplitter::workstack() const
410 {
411   QtxWorkstack* ws = 0;
412   QWidget* wid = parentWidget();
413   while ( wid && !ws )
414   {
415     ws = ::qobject_cast<QtxWorkstack*>( wid );
416     wid = wid->parentWidget();
417   }
418   return ws;
419 }
420
421 /*!
422   \brief Save the widget area configuration into data stream.
423 */
424 void QtxWorkstackSplitter::saveState( QDataStream& stream ) const
425 {
426   stream << QtxWorkstack::SplitMarker;
427
428   uchar flags = 0;
429   if ( orientation() == Qt::Horizontal )
430     flags |= QtxWorkstack::Horizontal;
431
432   stream << flags;
433   stream << count();
434
435   QList<int> sz = sizes();
436   for ( QList<int>::const_iterator it = sz.begin(); it != sz.end(); ++it )
437     stream << *it;
438
439   for ( int i = 0; i < count(); i++ )
440   {
441     QWidget* wid = widget( i );
442     QtxWorkstackSplitter* split = ::qobject_cast<QtxWorkstackSplitter*>( wid );
443     if ( split )
444       split->saveState( stream );
445     else
446     {
447       QtxWorkstackArea* area = ::qobject_cast<QtxWorkstackArea*>( wid );
448       if ( area )
449               area->saveState( stream );
450     }
451   }
452 }
453
454 /*!
455   \brief Restore the widget area configuration from data stream info.
456   \return \c true in successful case.
457 */
458 bool QtxWorkstackSplitter::restoreState( QDataStream& stream, QMap<QString, QtxWorkstackChild*>& map )
459 {
460   int num = 0;
461   uchar flags = 0;
462
463   stream >> flags;
464   stream >> num;
465
466   setOrientation( flags & QtxWorkstack::Horizontal ? Qt::Horizontal : Qt::Vertical );
467
468   QList<int> sz;
469   for ( int s = 0; s < num; s++ )
470   {
471     int sn = 0;
472     stream >> sn;
473     sz.append( sn );
474   }
475
476   bool ok = true;
477   for ( int i = 0; i < num && ok; i++ )
478   {
479     int marker;
480     stream >> marker;
481
482     if ( stream.status() != QDataStream::Ok )
483       return false;
484
485     if ( marker == QtxWorkstack::SplitMarker )
486     {
487       QtxWorkstackSplitter* split = new QtxWorkstackSplitter( this );
488       addWidget( split );
489       split->setVisible( true );
490
491       ok = split->restoreState( stream, map );
492     }
493     else if ( marker == QtxWorkstack::AreaMarker )
494     {
495       QtxWorkstack* ws = workstack();
496       QtxWorkstackArea* area = ws->createArea( this );
497       addWidget( area );
498       area->setVisible( true );
499
500       ok = area->restoreState( stream, map );
501     }
502     else
503       return false;
504   }
505
506   if ( ok )
507     setSizes( sz );
508
509   return ok;
510 }
511
512 /*!
513   \class QtxWorkstackArea
514   \internal
515   \brief Workstack widget workarea.
516 */
517
518 /*!
519   \brief Constructor.
520   \param parent parent widget
521 */
522 QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
523 : QFrame( parent )
524 {
525   setFrameStyle( QFrame::Panel | QFrame::Sunken );
526
527   QVBoxLayout* base = new QVBoxLayout( this );
528   base->setMargin( frameWidth() );
529   base->setSpacing( 0 );
530
531   QWidget* top = new QWidget( this );
532   base->addWidget( top );
533
534   QHBoxLayout* tl = new QHBoxLayout( top );
535   tl->setMargin( 0 );
536
537   myBar = new QtxWorkstackTabBar( top );
538   tl->addWidget( myBar, 1 );
539
540   CloseButton* close = new CloseButton( top );
541   close->setIcon( style()->standardIcon( QStyle::SP_TitleBarCloseButton ) );
542   myClose = close;
543   tl->addWidget( myClose );
544
545   myStack = new QStackedWidget( this );
546
547   base->addWidget( myStack, 1 );
548
549   connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
550   connect( myBar, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
551   connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
552   connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
553
554   updateState();
555
556   updateActiveState();
557
558   QApplication::instance()->installEventFilter( this );
559 }
560
561 /*!
562   \brief Destructor.
563 */
564 QtxWorkstackArea::~QtxWorkstackArea()
565 {
566   QApplication::instance()->removeEventFilter( this );
567 }
568
569 /*!
570   \brief Check if workarea contains any widgets.
571   \return \c true if area is null (havn't any child widgets)
572 */
573 bool QtxWorkstackArea::isNull() const
574 {
575   return myList.isEmpty();
576 }
577
578 /*!
579   \brief Check if workarea contains visible widgets.
580   \return \c true if area is empty (all child widgets are removed or now shown)
581 */
582 bool QtxWorkstackArea::isEmpty() const
583 {
584   bool res = false;
585   for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !res; ++it )
586     res = (*it)->visibility();
587   return !res;
588 }
589
590 /*!
591   \brief Add widget to the workarea.
592   \param wid widget to be added
593   \param idx position in the area widget to be added to
594   \param f widget flags
595   \return child widget container object (or 0 if index is invalid)
596 */
597 QtxWorkstackChild* QtxWorkstackArea::insertWidget( QWidget* wid, const int idx, Qt::WindowFlags f )
598 {
599   if ( !wid )
600     return 0;
601
602   QtxWorkstackChild* c = child( wid );
603   if ( !c )
604     c = new QtxWorkstackChild( wid, myStack, f );
605
606   insertChild( c, idx );
607
608   return c;
609 }
610
611 void QtxWorkstackArea::insertChild( QtxWorkstackChild* child, const int idx )
612 {
613   if ( !child )
614     return;
615
616   QtxWorkstackArea* a = child->area();
617   if ( a && a != this )
618     a->removeChild( child, false );
619
620   int pos = myList.indexOf( child );
621   if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
622     return;
623
624   bool found = myList.contains( child );
625
626   myList.removeAll( child );
627   pos = idx < 0 ? myList.count() : idx;
628   myList.insert( qMin( pos, (int)myList.count() ), child );
629
630   if ( !found )
631   {
632     bool hasId = false;
633     for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !hasId; ++it )
634       hasId = (*it)->id() == child->id();
635
636     if ( hasId || child->id() < 0 )
637       child->setId( generateId() );
638
639     connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
640     connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
641     connect( child, SIGNAL( hidden( QtxWorkstackChild* ) ), this, SLOT( onChildHidden( QtxWorkstackChild* ) ) );
642     connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
643     connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
644   }
645
646   updateState();
647
648   setWidgetActive( child->widget() );
649   child->widget()->setFocus();
650 }
651
652 /*!
653   \brief Create and show popup menu for the area.
654   \param p mouse pointer position at which popup menu should be shown
655 */
656 void QtxWorkstackArea::onContextMenuRequested( QPoint p )
657 {
658   const QtxWorkstackTabBar* bar = ::qobject_cast<const QtxWorkstackTabBar*>( sender() );
659   if ( !bar )
660     return;
661
662   QWidget* wid = 0;
663   int idx = tabAt( p );
664   if ( idx != -1 )
665     wid = widget( myBar->tabId( idx ) );
666
667   emit contextMenuRequested( wid, p );
668 }
669
670 /*!
671   \brief Remove widget from workarea.
672   \param wid widget to be removed
673   \param del if \c true the widget should be also deleted
674 */
675 void QtxWorkstackArea::removeWidget( QWidget* wid, const bool del )
676 {
677   removeChild( child( wid ), del );
678 }
679
680 /*!
681   \brief Remove child from workarea.
682   \param child child to be removed
683   \param del if \c true the widget should be also deleted
684 */
685 void QtxWorkstackArea::removeChild( QtxWorkstackChild* child, const bool del )
686 {
687   if ( !myList.contains( child ) )
688     return;
689
690   myStack->removeWidget( child );
691
692   if ( myBar->indexOf( child->id() ) != -1 )
693     myBar->removeTab( myBar->indexOf( child->id() ) );
694
695   myList.removeAll( child );
696
697   if ( del )
698     delete child;
699   else if ( child->widget() )
700   {
701     disconnect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
702     disconnect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
703     disconnect( child, SIGNAL( hidden( QtxWorkstackChild* ) ), this, SLOT( onChildHidden( QtxWorkstackChild* ) ) );
704     disconnect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
705     disconnect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
706   }
707
708   if ( isNull() )
709     deleteLater();
710   else
711     updateState();
712 }
713
714 QList<QtxWorkstackChild*> QtxWorkstackArea::childList() const
715 {
716   return myList;
717 }
718
719 /*!
720   \brief Get all visible child widgets.
721   \return list of visible child widgets
722 */
723 QWidgetList QtxWorkstackArea::widgetList() const
724 {
725   QWidgetList lst;
726   for ( ChildList::const_iterator it = myList.begin(); it != myList.end(); ++it )
727   {
728     QtxWorkstackChild* c = *it;
729     if ( c->visibility() )
730       lst.append( c->widget() );
731   }
732   return lst;
733 }
734
735 /*!
736   \brief Get active child widget.
737   \return active widget
738 */
739 QWidget* QtxWorkstackArea::activeWidget() const
740 {
741   return widget( myBar->tabId( myBar->currentIndex() ) );
742 }
743
744 /*!
745   \brief Set active widget.
746   \param wid widget to be made active
747 */
748 void QtxWorkstackArea::setActiveWidget( QWidget* wid )
749 {
750   myBar->setCurrentIndex( myBar->indexOf( widgetId( wid ) ) );
751 }
752
753 /*!
754   \brief Check if area owns the specified widget.
755   \param wid widget to be checked
756   \return \c true if area contains widget
757 */
758 bool QtxWorkstackArea::contains( QWidget* wid ) const
759 {
760   return child( wid );
761 }
762
763 /*!
764   \brief Check if workarea is active.
765   \return \c true if area is active
766 */
767 bool QtxWorkstackArea::isActive() const
768 {
769   QtxWorkstack* ws = workstack();
770   if ( !ws )
771     return false;
772
773   return ws->activeArea() == this;
774 }
775
776 /*!
777   \brief Update active tab bar state (active/inactive).
778 */
779 void QtxWorkstackArea::updateActiveState()
780 {
781   myBar->setActive( isActive() );
782 }
783
784 /*!
785   \brief Get parent workstack
786   \return workstack owning this workarea
787 */
788 QtxWorkstack* QtxWorkstackArea::workstack() const
789 {
790   QtxWorkstack* ws = 0;
791   QWidget* wid = parentWidget();
792   while ( wid && !ws )
793   {
794     ws = ::qobject_cast<QtxWorkstack*>( wid );
795     wid = wid->parentWidget();
796   }
797   return ws;
798 }
799
800 /*!
801   \brief Custom event filter.
802
803   Process events from child widgets.
804
805   \param o event receiver widget
806   \param e event
807   \return \c true if event should be filtered (stop further processing)
808 */
809 bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
810 {
811   if ( o->isWidgetType() )
812   {
813     QWidget* wid = (QWidget*)o;
814     if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
815     {
816       bool ok = false;
817       while ( !ok && wid && wid != myClose )
818       {
819         ok = wid == this;
820         wid = wid->parentWidget();
821       }
822       if ( ok )
823         QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
824     }
825   }
826   return false;
827 }
828
829 /*!
830   \brief Save the own widgets configuration into data stream.
831 */
832 void QtxWorkstackArea::saveState( QDataStream& stream ) const
833 {
834   stream << QtxWorkstack::AreaMarker;
835   stream << myList.count();
836   stream << myBar->tabId( myBar->currentIndex() );
837   for ( ChildList::const_iterator it = myList.begin(); it != myList.end(); ++it )
838   {
839     QtxWorkstackChild* c = *it;
840
841     stream << QtxWorkstack::WidgetMarker;
842
843     stream << c->widget()->objectName();
844     stream << c->id();
845
846     uchar flags = 0;
847     if ( c->visibility() )
848       flags |= QtxWorkstack::Visible;
849
850     stream << flags;
851   }
852 }
853
854 /*!
855   \brief Restore the widgets configuration from data stream info.
856   \return \c true in successful case.
857 */
858 bool QtxWorkstackArea::restoreState( QDataStream& stream, QMap<QString, QtxWorkstackChild*>& map )
859 {
860   int num = 0;
861   int cur = -1;
862
863   stream >> num;
864   stream >> cur;
865
866   QtxWorkstackChild* curChild = 0;
867   for ( int i = 0; i < num; i++ )
868   {
869     int marker;
870     stream >> marker;
871
872     if ( stream.status() != QDataStream::Ok || marker != QtxWorkstack::WidgetMarker )
873       return false;
874
875     QString name;
876     stream >> name;
877
878     int id = -1;
879     stream >> id;
880
881     uchar flags = 0;
882     stream >> flags;
883
884     QtxWorkstackChild* c = map.contains( name ) ? map[name] : 0;
885     if ( !c )
886     {
887       qWarning( "QtxWorkstack: Restored child widget \"%s\" not found.", (const char*)name.toLatin1() );
888       return false;
889     }
890
891     map.remove( name );
892
893     if ( id == cur )
894       curChild = c;
895
896     QApplication::postEvent( this, new RestoreEvent( (QEvent::Type)RestoreWidget, id, flags, c ) );
897   }
898
899   if ( curChild )
900     QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)MakeCurrent, curChild ) );
901
902   return true;
903 }
904
905 /*!
906   \brief Get rectangle to be drawn when highlighting drop area.
907   \return area drop rectangle
908 */
909 QRect QtxWorkstackArea::floatRect() const
910 {
911   QRect r = myStack->geometry();
912   return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
913 }
914
915 /*!
916   \brief Get rectangle to be drawn when highlighting drop area on tab bar.
917   \param idx tab index
918   \return tab bar drop rectrangle
919 */
920 QRect QtxWorkstackArea::floatTab( const int idx ) const
921 {
922   QRect r = myBar->tabRect( idx );
923   return QRect( myBar->mapToGlobal( r.topLeft() ), r.size() );
924 }
925
926 /*!
927   \brief Get tab index by point.
928   \param p point
929   \return tab covering point or -1 if there is no tab covering point
930 */
931 int QtxWorkstackArea::tabAt( const QPoint& pnt ) const
932 {
933   int idx = -1;
934   QPoint p = myBar->mapFromGlobal( pnt );
935   for ( int i = 0; i < myBar->count() && idx == -1; i++ )
936   {
937     QRect r = myBar->tabRect( i );
938     if ( r.isValid() && r.contains( p ) )
939       idx = i;
940   }
941   return idx;
942 }
943
944 /*!
945   \brief Event handler for custom events.
946   \param e custom event
947 */
948 void QtxWorkstackArea::customEvent( QEvent* e )
949 {
950   WidgetEvent* we = (WidgetEvent*)e;
951
952   switch ( we->type() )
953   {
954   case ActivateWidget:
955     myBar->updateActiveState();
956     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 Set active widget
1799   \param wid widget to activate
1800 */
1801 void QtxWorkstack::setActiveWindow( QWidget* wid )
1802 {
1803   if ( activeArea() )
1804     activeArea()->setActiveWidget( wid );
1805 }
1806
1807 /*!
1808   \brief Split workstack.
1809
1810   Splitting is possible only if there are two or more widgets in the workarea.
1811   This function splits current workarea to two new ones.
1812
1813   \param o splitting orientation (Qt::Orientation)
1814 */
1815 void QtxWorkstack::split( const int o )
1816 {
1817   QtxWorkstackArea* area = myWorkArea;
1818   if ( !area )
1819     area = activeArea();
1820   if ( !area )
1821     return;
1822
1823   if ( area->widgetList().count() < 2 )
1824     return;
1825
1826   QWidget* curWid = area->activeWidget();
1827   if ( !curWid )
1828     return;
1829
1830   QSplitter* s = splitter( area );
1831   QList<QtxWorkstackArea*> areaList;
1832   areas( s, areaList );
1833
1834   QList<QSplitter*> splitList;
1835   splitters( s, splitList );
1836
1837   QSplitter* trg = 0;
1838   if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
1839     trg = s;
1840
1841   if ( !trg )
1842     trg = wrapSplitter( area );
1843
1844   if ( !trg )
1845     return;
1846
1847   trg->setOrientation( (Qt::Orientation)o );
1848
1849   QtxWorkstackArea* newArea = createArea( 0 );
1850   trg->insertWidget( trg->indexOf( area ) + 1, newArea );
1851
1852   area->removeWidget( curWid );
1853   newArea->insertWidget( curWid );
1854
1855   distributeSpace( trg );
1856
1857   curWid->show();
1858   curWid->setFocus();
1859 }
1860
1861 /*!
1862   \brief Split workarea of the given widget on two parts.
1863
1864   Splitting is possible only if there are two or more widgets in the workarea.
1865   This function splits current workarea to two new ones.
1866
1867   \param wid widget belonging to the workstack
1868   \param o splitting orientation type (Qt::Orientation)
1869   \param type splitting type (QtxWorkstack::SplitType)
1870 */
1871 void QtxWorkstack::Split( QWidget* wid, const Qt::Orientation o, const SplitType type )
1872 {
1873   if (!wid) return;
1874
1875   // find area of the given widget
1876   QtxWorkstackArea* area = NULL;
1877   QList<QtxWorkstackArea*> allAreas;
1878   areas(mySplit, allAreas, true);
1879
1880   for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
1881   {
1882     if ( (*it)->contains( wid ) )
1883       area = *it;
1884   }
1885
1886   if ( !area )
1887     return;
1888
1889   QWidget* curWid = area->activeWidget();
1890   if ( !curWid )
1891     return;
1892
1893   QWidgetList wids = area->widgetList();
1894   if ( wids.count() < 2 )
1895     return;
1896
1897   QSplitter* s = splitter( area );
1898   QList<QtxWorkstackArea*> areaList;
1899   areas( s, areaList );
1900
1901   QList<QSplitter*> splitList;
1902   splitters(s, splitList);
1903
1904   QSplitter* trg = 0;
1905   if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
1906     trg = s;
1907
1908   if (!trg) trg = wrapSplitter(area);
1909   if (!trg) return;
1910
1911   trg->setOrientation(o);
1912
1913   QtxWorkstackArea* newArea = createArea(0);
1914   insertWidget(newArea, trg, area);
1915
1916   switch ( type )
1917   {
1918   case SplitStay:
1919     for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
1920     {
1921       QWidget* wid_i = *itr;
1922       if ( wid_i != wid )
1923       {
1924         area->removeWidget( wid_i );
1925         newArea->insertWidget( wid_i );
1926         wid_i->showMaximized();
1927       }
1928     }
1929     break;
1930   case SplitAt:
1931     {
1932       QWidgetList::iterator itr = wids.begin();
1933       for ( ; itr != wids.end() && *itr != wid; ++itr )
1934       {
1935       }
1936       for ( ; itr != wids.end(); ++itr )
1937       {
1938         area->removeWidget( *itr );
1939         newArea->insertWidget( *itr );
1940         (*itr)->showMaximized();
1941       }
1942     }
1943     break;
1944   case SplitMove:
1945     area->removeWidget( wid );
1946     newArea->insertWidget( wid );
1947     wid->showMaximized();
1948     break;
1949   }
1950
1951   distributeSpace( trg );
1952
1953   curWid->show();
1954   curWid->setFocus();
1955 }
1956
1957 /*!
1958  \brief Move widget(s) from the source workarea into the target workarea
1959         or reorder widgets inside one workarea.
1960
1961  Move \a wid2 in target workarea. Put it right after \a wid1.
1962  If \a all parameter is \c true, all widgets from source workarea
1963  will be moved including \a wid2 and source workarea will be deleted then.
1964
1965  If \a wid1 and \a wid2 belongs to one workarea, widgets will be just reordered
1966  in that workarea.
1967
1968  \param wid1 widget from target workarea
1969  \param wid2 widget from source workarea
1970  \param all  if \c true, all widgets from source workarea will
1971              be moved into the target one, else only the \a wid2 will be moved
1972 */
1973 void QtxWorkstack::Attract( QWidget* wid1, QWidget* wid2, const bool all )
1974 {
1975   if ( !wid1 || !wid2 )
1976     return;
1977
1978   // find area of the widgets
1979   QtxWorkstackArea *area1 = 0, *area2 = 0;
1980   QList<QtxWorkstackArea*> allAreas;
1981   areas( mySplit, allAreas, true );
1982   for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !( area1 && area2 ); ++it )
1983   {
1984     if ( (*it)->contains( wid1 ) )
1985       area1 = *it;
1986
1987     if ( (*it)->contains( wid2 ) )
1988       area2 = *it;
1989   }
1990
1991   if ( !area1 || !area2 )
1992     return;
1993
1994   QSplitter* s1 = splitter( area1 );
1995
1996   QWidget* curWid = area1->activeWidget();
1997   if ( !curWid )
1998     return;
1999
2000   if ( area1 == area2 )
2001   {
2002     if ( all )
2003     {
2004       // Set wid1 at first position, wid2 at second
2005       area1->insertWidget( wid1 );
2006       area1->insertWidget( wid2, 1 );
2007       wid1->showMaximized();
2008       wid2->showMaximized();
2009     }
2010     else
2011     {
2012       // Set wid2 right after wid1
2013       area1->removeWidget( wid2 );
2014       int wid1_ind = 0;
2015       QWidgetList wids1 = area1->widgetList();
2016       for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
2017       area1->insertWidget( wid2, wid1_ind + 1 );
2018       wid2->showMaximized();
2019     }
2020   }
2021   else
2022   {
2023     int wid1_ind = 0;
2024     QWidgetList wids1 = area1->widgetList();
2025     for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
2026     if ( all )
2027     {
2028       // Set wid2 right after wid1, other widgets from area2 right after wid2
2029       QWidgetList wids2 = area2->widgetList();
2030       QWidgetList::iterator itr2 = wids2.begin();
2031       for ( int ind = wid1_ind + 1; itr2 != wids2.end(); ++itr2, ++ind )
2032       {
2033         area2->removeWidget( *itr2 );
2034         if ( *itr2 == wid2 )
2035           area1->insertWidget( *itr2, wid1_ind + 1 );
2036         else
2037           area1->insertWidget( *itr2, ind );
2038         (*itr2)->showMaximized();
2039       }
2040     }
2041     else
2042     {
2043       // Set wid2 right after wid1
2044       area2->removeWidget( wid2 );
2045       area1->insertWidget( wid2, wid1_ind + 1 );
2046       wid2->showMaximized();
2047     }
2048   }
2049
2050   distributeSpace( s1 );
2051
2052   area1->setActiveWidget( curWid );
2053
2054   wid2->show();
2055   wid1->setFocus();
2056   curWid->show();
2057   curWid->setFocus();
2058 }
2059
2060 /*!
2061   \brief Calculate sizes of the splitter widget for the workarea.
2062   \internal
2063 */
2064 static void setSizes (QIntList& szList, const int item_ind,
2065                       const int new_near, const int new_this, const int new_farr)
2066 {
2067   // set size to all items before an item # <item_ind>
2068   int cur_pos = 0;
2069   QIntList::iterator its = szList.begin();
2070   for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
2071     *its = new_near;
2072   }
2073   if (its == szList.end()) return;
2074   // set size to item # <item_ind>
2075   *its = new_this;
2076   ++its;
2077   // set size to all items after an item # <item_ind>
2078   for (; its != szList.end(); ++its) {
2079     *its = new_farr;
2080   }
2081 }
2082
2083 /*!
2084   \brief Set position of the widget relatively to its parent splitter.
2085
2086   Orientation of positioning will correspond to the splitter orientation.
2087
2088   \param wid widget
2089   \param pos position relatively to the splitter; value in the range [0..1]
2090 */
2091 void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
2092 {
2093   if ( position < 0.0 || 1.0 < position)
2094     return;
2095
2096   if ( !wid )
2097     return;
2098
2099   // find area of the given widget
2100   QtxWorkstackArea* area = NULL;
2101   QList<QtxWorkstackArea*> allAreas;
2102   areas( mySplit, allAreas, true );
2103   for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
2104   {
2105     if ( (*it)->contains( wid ) )
2106       area = *it;
2107   }
2108
2109   if ( !area )
2110     return;
2111
2112   QSplitter* split = splitter( area );
2113   if ( !split )
2114     return;
2115
2116   // find index of the area in its splitter
2117   int item_ind = -1;
2118   bool isFound = false;
2119   const QObjectList& was = split->children();
2120   for ( QObjectList::const_iterator ito = was.begin(); ito != was.end() && !isFound; ++ito, ++item_ind )
2121   {
2122     if ( *ito == area )
2123       isFound = true;
2124   }
2125
2126   if ( !isFound || item_ind == 0 )
2127     return;
2128
2129   QIntList szList = split->sizes();
2130   int splitter_size = ( split->orientation() == Qt::Horizontal ? split->width() : split->height());
2131   int nb = szList.count();
2132
2133   int new_prev = int( splitter_size * position / item_ind );
2134   if (nb == item_ind) return;
2135   int new_next = int( splitter_size * ( 1.0 - position ) / ( nb - item_ind ) );
2136   setSizes( szList, item_ind, new_prev, new_next, new_next );
2137   split->setSizes( szList );
2138 }
2139
2140 /*!
2141   \brief Set position of the widget relatively to the entire workstack.
2142
2143   If \a o is \c Qt::Horizontal, the horizontal position of \a wid will be changed.
2144   If \a o is \c Qt::Vertical, the vertical position of \a wid will be changed.
2145
2146   \param wid widget
2147   \param o   orientation of positioning (\c Qt::Horizontal or \c Qt::Vertical)
2148   \param pos position relatively to the workstack; value in range [0..1]
2149 */
2150 void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
2151                                         const double position )
2152 {
2153   if ( position < 0.0 || 1.0 < position )
2154     return;
2155
2156   if ( !wid )
2157     return;
2158
2159   int splitter_size = o == Qt::Horizontal ? mySplit->width() : mySplit->height();
2160   int need_pos = int( position * splitter_size );
2161   int splitter_pos = 0;
2162
2163   if ( setPosition( wid, mySplit, o, need_pos, splitter_pos ) != 0 )
2164   {
2165     // impossible to set required position
2166   }
2167 }
2168
2169 /*!
2170   \brief Set accelerator key-combination for the action with specified \a id.
2171   \param id action ID
2172   \param accel action accelerator
2173 */
2174 void QtxWorkstack::setAccel( const int id, const int accel )
2175 {
2176   if ( !myActionsMap.contains( id ) )
2177     return;
2178
2179   myActionsMap[id]->setShortcut( accel );
2180 }
2181
2182 /*!
2183   \brief Get the action's accelerator key-combination.
2184   \param id action ID
2185   \return action accelerator
2186 */
2187 int QtxWorkstack::accel( const int id ) const
2188 {
2189   int res = 0;
2190   if ( myActionsMap.contains( id ) )
2191     res = myActionsMap[id]->shortcut();
2192   return res;
2193 }
2194
2195 /*!
2196   \brief Get icon for the specified action.
2197
2198   If \a id is invalid, null icon is returned.
2199
2200   \param id menu action ID
2201   \return menu item icon
2202 */
2203 QIcon QtxWorkstack::icon( const int id ) const
2204 {
2205   QIcon ico;
2206   if ( myActionsMap.contains( id ) )
2207     ico = myActionsMap[id]->icon();
2208   return ico;
2209 }
2210
2211 /*!
2212   \brief Set menu item icon for the specified action.
2213   \param id menu action ID
2214   \param ico new menu item icon
2215 */
2216 void QtxWorkstack::setIcon( const int id, const QIcon& icon )
2217 {
2218   if ( !myActionsMap.contains( id ) )
2219     return;
2220
2221   myActionsMap[id]->setIcon( icon );
2222 }
2223
2224 /*!
2225   \brief Set actions to be visible in the context popup menu.
2226
2227   Actions, which IDs are set in \a flags parameter, will be shown in the
2228   context popup menu. Other actions will not be shown.
2229
2230   \param flags ORed together actions flags
2231 */
2232 void QtxWorkstack::setMenuActions( const int flags )
2233 {
2234   myActionsMap[SplitVertical]->setVisible( flags & SplitVertical );
2235   myActionsMap[SplitHorizontal]->setVisible( flags & SplitHorizontal );
2236   myActionsMap[Close]->setVisible( flags & Close );
2237   myActionsMap[Rename]->setVisible( flags & Rename );
2238 }
2239
2240 /*!
2241   \brief Set actions to be visible in the context popup menu.
2242
2243   Actions, which IDs are set in \a flags parameter, will be shown in the
2244   context popup menu. Other actions will not be shown.
2245
2246   \param flags ORed together actions flags
2247 */
2248 int QtxWorkstack::menuActions() const
2249 {
2250   int ret = 0;
2251   ret = ret | ( myActionsMap[SplitVertical]->isVisible() ? SplitVertical : 0 );
2252   ret = ret | ( myActionsMap[SplitHorizontal]->isVisible() ? SplitHorizontal : 0 );
2253   ret = ret | ( myActionsMap[Close]->isVisible() ? Close : 0 );
2254   ret = ret | ( myActionsMap[Rename]->isVisible() ? Rename : 0 );
2255   return ret;
2256 }
2257
2258 /*!
2259   \brief Calculate sizes of the splitter widget for the workarea.
2260   \internal
2261 */
2262 static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
2263                            const int item_ind, const int item_rel_pos,
2264                            const int need_pos, const int splitter_pos)
2265 {
2266   if (item_ind == 0) { // cannot move in this splitter
2267     return (need_pos - splitter_pos);
2268   }
2269
2270   int delta = 0;
2271   int new_prev = 0;
2272   int new_this = szList[item_ind];
2273   int new_next = 0;
2274
2275   bool isToCheck = false;
2276
2277   if (need_pos < splitter_pos) {
2278     // Set size of all previous workareas to zero <--
2279     if (item_ind == nb - 1) {
2280       // item iz last in the splitter, it will occupy all the splitter
2281       new_this = splitter_size;
2282     } else {
2283       // recompute size of next items in splitter
2284       new_next = (splitter_size - new_this) / (nb - item_ind - 1);
2285     }
2286     delta = need_pos - splitter_pos;
2287
2288   } else if (need_pos > (splitter_pos + splitter_size)) {
2289     // Set size of all next workareas to zero -->
2290     // recompute size of previous items in splitter
2291     new_this = 0;
2292     new_prev = (splitter_size - new_this) / item_ind;
2293     delta = need_pos - (splitter_pos + splitter_size - new_this);
2294
2295   } else { // required position lays inside this splitter
2296     // Move workarea inside splitter into required position <->
2297     int new_item_rel_pos = need_pos - splitter_pos;
2298     new_prev = new_item_rel_pos / item_ind;
2299     if (need_pos < (splitter_pos + item_rel_pos)) {
2300       // Make previous workareas smaller, next - bigger
2301       // No problem to keep old size of the widget
2302     } else {
2303       // Make previous workareas bigger, next - smaller
2304       if (new_this > splitter_size - new_item_rel_pos) {
2305         new_this = splitter_size - new_item_rel_pos;
2306       }
2307       // jfa to do: in this case fixed size of next widgets could prevent right resizing
2308       isToCheck = true;
2309     }
2310     if (item_ind == nb - 1) {
2311       new_this = splitter_size - new_item_rel_pos;
2312     } else {
2313       new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
2314     }
2315     delta = 0;
2316   }
2317
2318   setSizes (szList, item_ind, new_prev, new_this, new_next);
2319   return delta;
2320 }
2321
2322 /*!
2323   \brief Set position of the widget.
2324
2325   Called from SetRelativePosition() public method.
2326
2327   \param wid   widget to be moved
2328   \param split currently processed splitter (goes from more common
2329                to more particular splitter in recursion calls)
2330   \param o     orientation of positioning
2331   \param need_pos required position of the given widget in pixels
2332                (from top/left side of workstack area)
2333   \param splitter_pos position of the splitter \a split
2334                (from top/left side of workstack area)
2335   \return difference between a required and a distinguished position
2336 */
2337 int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
2338                                const int need_pos, const int splitter_pos )
2339 {
2340   if ( !wid || !split )
2341     return need_pos - splitter_pos;
2342
2343   // Find corresponding sub-splitter.
2344   // Find also index of appropriate item in current splitter.
2345   int cur_ind = 0, item_ind = 0;
2346   bool isBottom = false, isFound = false;
2347   QSplitter* sub_split = NULL;
2348   const QObjectList& objs = split->children();
2349   for ( QObjectList::const_iterator it = objs.begin(); it != objs.end() && !isFound; ++it )
2350   {
2351     QtxWorkstackArea* area = ::qobject_cast<QtxWorkstackArea*>( *it );
2352     if ( area )
2353     {
2354       if ( area->contains( wid ) )
2355       {
2356         item_ind = cur_ind;
2357         isBottom = true;
2358         isFound = true;
2359       }
2360       cur_ind++;
2361     }
2362     else if ( (*it)->inherits( "QSplitter" ) )
2363     {
2364       QList<QtxWorkstackArea*> areaList;
2365       areas( (QSplitter*)(*it), areaList, true );
2366       for ( QList<QtxWorkstackArea*>::iterator ita = areaList.begin(); ita != areaList.end() && !isFound; ++ita )
2367       {
2368         if ( (*ita)->contains( wid ) )
2369         {
2370           item_ind = cur_ind;
2371           isFound = true;
2372           sub_split = (QSplitter*)*it;
2373         }
2374       }
2375       cur_ind++;
2376     }
2377   }
2378
2379   if ( !isFound )
2380     return ( need_pos - splitter_pos );
2381
2382   if ( split->orientation() == o )
2383   {
2384     // Find coordinates of near and far sides of the appropriate item relatively current splitter
2385     int splitter_size = ( o == Qt::Horizontal ? split->width() : split->height() );
2386     QIntList szList = split->sizes();
2387     int nb = szList.count();
2388     int item_rel_pos = 0; // position of near side of item relatively this splitter
2389     for (int i = 0; i < item_ind; i++) {
2390       item_rel_pos += szList[i];
2391     }
2392     int item_size = szList[item_ind]; // size of item
2393     int item_pos = splitter_pos + item_rel_pos;
2394
2395     // Resize splitter items to complete the conditions
2396     if (isBottom) {
2397       // I. Bottom of splitters stack reached
2398
2399       int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
2400       split->setSizes(szList);
2401       // Recompute delta, as some windows can reject given size
2402       int new_item_rel_pos = 0;
2403       QIntList szList1 = split->sizes();
2404       for (int i = 0; i < item_ind; i++) {
2405         new_item_rel_pos += szList1[i];
2406       }
2407       delta = need_pos - (splitter_pos + new_item_rel_pos);
2408       return delta;
2409
2410     } else {
2411       // II. Bottom of splitters stack is not yet reached
2412
2413       if (item_ind == 0) { // cannot move in this splitter
2414         // Process in sub-splitter
2415         return setPosition(wid, sub_split, o, need_pos, splitter_pos);
2416       }
2417
2418       int new_prev = 0;
2419       int new_this = szList[item_ind];
2420       int new_next = 0;
2421
2422       if (need_pos < splitter_pos) {
2423         // Set size of all previous workareas to zero <--
2424         if (item_ind == nb - 1) {
2425           new_this = splitter_size;
2426         } else {
2427           new_next = (splitter_size - new_this) / (nb - item_ind - 1);
2428         }
2429         setSizes (szList, item_ind, new_prev, new_this, new_next);
2430         split->setSizes(szList);
2431         // Recompute splitter_pos, as some windows can reject given size
2432         int new_item_rel_pos = 0;
2433         QIntList szList1 = split->sizes();
2434         for (int i = 0; i < item_ind; i++) {
2435           new_item_rel_pos += szList1[i];
2436         }
2437         // Process in sub-splitter
2438         return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2439       } else if (need_pos > (splitter_pos + splitter_size)) {
2440         // Set size of all next workareas to zero -->
2441         new_prev = (splitter_size - new_this) / item_ind;
2442         setSizes (szList, item_ind, new_prev, new_this, new_next);
2443         split->setSizes(szList);
2444         // Recompute splitter_pos, as some windows can reject given size
2445         int new_item_rel_pos = 0;
2446         QIntList szList1 = split->sizes();
2447         for (int i = 0; i < item_ind; i++) {
2448           new_item_rel_pos += szList1[i];
2449         }
2450         // Process in sub-splitter
2451         return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2452       } else {
2453         // Set appropriate size of all previous/next items <->
2454         int new_item_rel_pos = item_rel_pos;
2455         if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
2456           // Move item inside splitter into required position <->
2457           int new_this = szList[item_ind];
2458           int new_next = 0;
2459           new_item_rel_pos = need_pos - splitter_pos;
2460           if ((item_pos + item_size) < need_pos) {
2461             //new_item_rel_pos = need_pos - (item_pos + item_size);
2462             new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
2463           }
2464           int new_prev = new_item_rel_pos / item_ind;
2465           if (need_pos < (splitter_pos + item_rel_pos)) {
2466             // Make previous workareas smaller, next - bigger
2467             // No problem to keep old size of the widget
2468           } else {
2469             // Make previous workareas bigger, next - smaller
2470             if (new_this > splitter_size - new_item_rel_pos) {
2471               new_this = splitter_size - new_item_rel_pos;
2472             }
2473           }
2474           if (item_ind == nb - 1) {
2475             new_this = splitter_size - new_item_rel_pos;
2476           } else {
2477             new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
2478           }
2479           setSizes (szList, item_ind, new_prev, new_this, new_next);
2480           split->setSizes(szList);
2481           // Recompute new_item_rel_pos, as some windows can reject given size
2482           new_item_rel_pos = 0;
2483           QIntList szList1 = split->sizes();
2484           for (int i = 0; i < item_ind; i++) {
2485             new_item_rel_pos += szList1[i];
2486           }
2487         } else {
2488           // Do nothing
2489         }
2490         // Process in sub-splitter
2491         int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2492         if (add_pos == 0)
2493           return 0;
2494
2495         // this can be if corresponding workarea is first in sub-splitter
2496         // or sub-splitter has another orientation
2497
2498         // Resize ones again to reach precize position <->
2499         int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
2500
2501         // Move workarea inside splitter into required position <->
2502         int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
2503                                      new_item_rel_pos, need_pos_1, splitter_pos);
2504         split->setSizes(szList);
2505         // Recompute new_item_rel_pos, as some windows can reject given size
2506         new_item_rel_pos = 0;
2507         QIntList szList1 = split->sizes();
2508         for (int i = 0; i < item_ind; i++) {
2509           new_item_rel_pos += szList1[i];
2510         }
2511         delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
2512         return delta_1;
2513       }
2514     }
2515   } else {
2516     return setPosition(wid, sub_split, o, need_pos, splitter_pos);
2517   }
2518
2519   return 0;
2520 }
2521
2522 /*!
2523   \brief Redistribute space among widgets equally.
2524   \param split splitter
2525 */
2526 void QtxWorkstack::distributeSpace( QSplitter* split ) const
2527 {
2528   if ( !split )
2529     return;
2530
2531   QIntList szList = split->sizes();
2532   int size = ( split->orientation() == Qt::Horizontal ?
2533                split->width() : split->height() ) / szList.count();
2534   for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
2535     *it = size;
2536   split->setSizes( szList );
2537 }
2538
2539 /*!
2540   \brief Split widgets vertically.
2541 */
2542 void QtxWorkstack::splitVertical()
2543 {
2544   split( Qt::Horizontal );
2545 }
2546
2547 /*!
2548   \brief Split widgets horizontally.
2549 */
2550 void QtxWorkstack::splitHorizontal()
2551 {
2552   split( Qt::Vertical );
2553 }
2554
2555 /*!
2556   \brief Called when user activates "Rename" menu item.
2557
2558   Changes widget title.
2559 */
2560 void QtxWorkstack::onRename()
2561 {
2562   if ( !myWorkWin )
2563     return;
2564
2565   bool ok = false;
2566   QString newName = QInputDialog::getText( topLevelWidget(),  tr( "Rename" ), tr( "Enter new name:" ),
2567                                            QLineEdit::Normal, myWorkWin->windowTitle(), &ok );
2568   if ( ok && !newName.isEmpty() )
2569     myWorkWin->setWindowTitle( newName );
2570 }
2571
2572 /*!
2573   \brief Wrap area into the new splitter.
2574   \param workarea
2575   \return new splitter
2576 */
2577 QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
2578 {
2579   if ( !area )
2580     return 0;
2581
2582   QSplitter* pSplit = splitter( area );
2583   if ( !pSplit )
2584     return 0;
2585
2586   bool upd = pSplit->updatesEnabled();
2587   pSplit->setUpdatesEnabled( false );
2588
2589   QIntList szList = pSplit->sizes();
2590
2591   QSplitter* wrap = new QtxWorkstackSplitter( 0 );
2592   pSplit->insertWidget( pSplit->indexOf( area ) + 1, wrap );
2593   wrap->setVisible( true );
2594   wrap->addWidget( area );
2595
2596   pSplit->setSizes( szList );
2597
2598   pSplit->setUpdatesEnabled( upd );
2599
2600   return wrap;
2601 }
2602
2603 /*!
2604   \brief Reparent and add widget.
2605   \param wid widget
2606   \param pWid parent widget
2607   \param after widget after which \a wid should be added
2608 */
2609 void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
2610 {
2611   if ( !wid || !pWid )
2612     return;
2613
2614   QWidgetList moveList;
2615   const QObjectList& lst = pWid->children();
2616   bool found = false;
2617   for ( QObjectList::const_iterator it = lst.begin(); it != lst.end(); ++it )
2618   {
2619     if ( found && ( (*it)->inherits( "QSplitter" ) ||
2620                     (*it)->inherits( "QtxWorkstackArea" ) ) )
2621       moveList.append( (QWidget*)(*it) );
2622     if ( *it == after )
2623       found = true;
2624   }
2625
2626   QMap<QWidget*, bool> map;
2627   for ( QWidgetList::iterator it = moveList.begin(); it != moveList.end(); ++it )
2628   {
2629     map.insert( *it, (*it)->isVisibleTo( (*it)->parentWidget() ) );
2630     (*it)->setParent( 0 );
2631     (*it)->hide();
2632   }
2633
2634   wid->setParent( pWid );
2635
2636   for ( QWidgetList::iterator itr = moveList.begin(); itr != moveList.end(); ++itr )
2637   {
2638     (*itr)->setParent( pWid );
2639     (*itr)->setShown( map.contains( *itr ) ? map[*itr] : false );
2640   }
2641 }
2642
2643 /*!
2644   \brief Close active window.
2645 */
2646 void QtxWorkstack::onCloseWindow()
2647 {
2648   if ( myWorkWin )
2649     myWorkWin->close();
2650   else if( activeWindow() )
2651     activeWindow()->close();
2652 }
2653
2654 /*!
2655   \brief Called when workarea is destroyed.
2656
2657   Set input focus to the neighbour area.
2658
2659   \param obj workarea being destroyed
2660 */
2661 void QtxWorkstack::onDestroyed( QObject* obj )
2662 {
2663   QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
2664
2665   if ( area == myArea )
2666     myArea = 0;
2667
2668   if ( !myArea )
2669   {
2670     QtxWorkstackArea* cur = neighbourArea( area );
2671     if ( cur )
2672       cur->setFocus();
2673   }
2674
2675   QApplication::postEvent( this, new QEvent( QEvent::User ) );
2676 }
2677
2678 /*!
2679   \brief Called on window activating.
2680   \param area workarea being activated (not used)
2681 */
2682 void QtxWorkstack::onWindowActivated( QWidget* /*area*/ )
2683 {
2684   const QObject* obj = sender();
2685   if ( !obj->inherits( "QtxWorkstackArea" ) )
2686     return;
2687
2688   setActiveArea( (QtxWorkstackArea*)obj );
2689 }
2690
2691 /*!
2692   \brief Called on window deactivating.
2693   \param area workarea being deactivated
2694 */
2695 void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
2696 {
2697   if ( myArea != area )
2698     return;
2699
2700   QList<QtxWorkstackArea*> lst;
2701   areas( mySplit, lst, true );
2702
2703   int idx = lst.indexOf( area );
2704   if ( idx == -1 )
2705     return;
2706
2707   myWin = 0;
2708   myArea = 0;
2709
2710   QtxWorkstackArea* newArea = neighbourArea( area );
2711   if ( newArea && newArea->activeWidget() )
2712     newArea->activeWidget()->setFocus();
2713
2714   QApplication::postEvent( this, new QEvent( QEvent::User ) );
2715 }
2716
2717 /*!
2718   \brief Create and show popup menu for workarea.
2719   \param w workarea
2720   \param p popup position
2721 */
2722 void QtxWorkstack::onContextMenuRequested( QWidget* w, QPoint p )
2723 {
2724   QtxWorkstackArea* anArea = ::qobject_cast<QtxWorkstackArea*>( (QObject*)sender() );
2725   if ( !anArea )
2726     anArea = activeArea();
2727
2728   if ( !anArea )
2729     return;
2730
2731   QWidgetList lst = anArea->widgetList();
2732   if ( lst.isEmpty() )
2733     return;
2734
2735   myWorkWin = w;
2736   myWorkArea = anArea;
2737
2738   QMenu* pm = new QMenu();
2739
2740   if ( lst.count() > 1 )
2741   {
2742     if ( !myActionsMap[SplitVertical]->isEnabled() )
2743       myActionsMap[SplitVertical]->setEnabled(true);
2744     pm->addAction( myActionsMap[SplitVertical] );
2745     if ( !myActionsMap[SplitHorizontal]->isEnabled() )
2746       myActionsMap[SplitHorizontal]->setEnabled(true);
2747     pm->addAction( myActionsMap[SplitHorizontal] );
2748     pm->addSeparator();
2749   }
2750
2751   if ( w )
2752   {
2753     if ( myActionsMap[Close]->isEnabled() )
2754       pm->addAction( myActionsMap[Close] );
2755     if ( myActionsMap[Rename]->isEnabled() )
2756       pm->addAction( myActionsMap[Rename] );
2757   }
2758
2759   Qtx::simplifySeparators( pm );
2760
2761   if ( !pm->actions().isEmpty() )
2762     pm->exec( p );
2763
2764   delete pm;
2765
2766   myWorkWin = 0;
2767   myWorkArea = 0;
2768 }
2769
2770 /*!
2771   \brief Add child widget.
2772   \param w widget
2773   \param f widget flags
2774   \return child widget container
2775 */
2776 QWidget* QtxWorkstack::addWindow( QWidget* w, Qt::WindowFlags f )
2777 {
2778   if ( !w )
2779     return 0;
2780
2781   return targetArea()->insertWidget( w, -1, f );
2782 }
2783
2784 /*!
2785   \brief Handle custom events.
2786   \param e custom event (not used)
2787 */
2788 void QtxWorkstack::customEvent( QEvent* /*e*/ )
2789 {
2790   updateState();
2791 }
2792
2793 /*!
2794   \brief Get splitter corresponding to the workarea.
2795   \param workarea
2796   \return splitter corresponding to the workarea
2797 */
2798 QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
2799 {
2800   if ( !area )
2801     return 0;
2802
2803   QSplitter* split = 0;
2804
2805   QWidget* wid = area->parentWidget();
2806   if ( wid && wid->inherits( "QSplitter" ) )
2807     split = (QSplitter*)wid;
2808
2809   return split;
2810 }
2811
2812 /*!
2813   \brief Get list of child splitters.
2814   \param split parent splitter
2815   \param splitList list to be filled with child splitters
2816   \param rec if \c true, perform recursive search of children
2817 */
2818 void QtxWorkstack::splitters( QSplitter* split, QList<QSplitter*>& splitList, const bool rec ) const
2819 {
2820   if ( !split )
2821     return;
2822
2823   const QObjectList& objs = split->children();
2824   for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
2825   {
2826     if ( rec )
2827       splitters( (QSplitter*)*it, splitList, rec );
2828     if ( (*it)->inherits( "QSplitter" ) )
2829       splitList.append( (QSplitter*)*it );
2830   }
2831 }
2832
2833 /*!
2834   \brief Get list of child workareas.
2835   \param split parent splitter
2836   \param areaList list to be filled with child workareas
2837   \param rec if \c true, perform recursive search of children
2838 */
2839 void QtxWorkstack::areas( QSplitter* split, QList<QtxWorkstackArea*>& areaList, const bool rec ) const
2840 {
2841   if ( !split )
2842     return;
2843
2844   const QObjectList& objs = split->children();
2845   for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
2846   {
2847     if ( (*it)->inherits( "QtxWorkstackArea" ) )
2848       areaList.append( (QtxWorkstackArea*)*it );
2849     else if ( rec && (*it)->inherits( "QSplitter" ) )
2850       areas( (QSplitter*)*it, areaList, rec );
2851   }
2852 }
2853
2854 /*!
2855   \brief Get active workarea.
2856   \return active workarea
2857 */
2858 QtxWorkstackArea* QtxWorkstack::activeArea() const
2859 {
2860   return myArea;
2861 }
2862
2863 /*!
2864   \brief Get target area (for which the current operation should be done).
2865
2866   Returns active workarea or current area (if there is no active workarea).
2867   If there are no workareas, create new workarea and return it.
2868
2869   \return workarea
2870 */
2871 QtxWorkstackArea* QtxWorkstack::targetArea()
2872 {
2873   QtxWorkstackArea* area = activeArea();
2874   if ( !area )
2875     area = currentArea();
2876   if ( !area )
2877   {
2878     QList<QtxWorkstackArea*> lst;
2879     areas( mySplit, lst );
2880     if ( !lst.isEmpty() )
2881       area = lst.first();
2882   }
2883
2884   if ( !area )
2885     area = createArea( mySplit );
2886
2887   return area;
2888 }
2889
2890 /*!
2891   \brief Get current workarea.
2892
2893   Current workarea is that one which has input focus.
2894
2895   \return current area
2896 */
2897 QtxWorkstackArea* QtxWorkstack::currentArea() const
2898 {
2899   QtxWorkstackArea* area = 0;
2900   QWidget* wid = focusWidget();
2901   while ( wid && !area )
2902   {
2903     if ( wid->inherits( "QtxWorkstackArea" ) )
2904       area = (QtxWorkstackArea*)wid;
2905     wid = wid->parentWidget();
2906   }
2907
2908   return area;
2909 }
2910
2911 /*!
2912   \brief Create new workarea.
2913   \param parent parent widget
2914   \return created workarea
2915 */
2916 QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
2917 {
2918   QtxWorkstackArea* area = new QtxWorkstackArea( parent );
2919
2920   connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
2921   connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
2922   connect( area, SIGNAL( contextMenuRequested( QWidget*, QPoint ) ),
2923            this, SLOT( onContextMenuRequested( QWidget*, QPoint ) ) );
2924   connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
2925
2926   return area;
2927 }
2928
2929 /*!
2930   \brief Set active workarea.
2931   \param workarea
2932 */
2933 void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
2934 {
2935   QWidget* oldCur = myWin;
2936
2937   QtxWorkstackArea* oldArea = myArea;
2938
2939   myArea = area;
2940
2941   if ( myArea != oldArea )
2942   {
2943     if ( oldArea )
2944       oldArea->updateActiveState();
2945     if ( myArea )
2946       myArea->updateActiveState();
2947   }
2948
2949   if ( myArea )
2950     myWin = myArea->activeWidget();
2951
2952   if ( myWin && oldCur != myWin )
2953     emit windowActivated( myWin );
2954 }
2955
2956 /*!
2957   \brief Get workarea which is nearest to \a area.
2958   \param area area for which neighbour is searched
2959   \return neighbour area (or 0 if not found)
2960 */
2961 QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
2962 {
2963   QList<QtxWorkstackArea*> lst;
2964   areas( mySplit, lst, true );
2965   int pos = lst.indexOf( area );
2966   if ( pos < 0 )
2967     return 0;
2968
2969   QtxWorkstackArea* na = 0;
2970   for ( int i = pos - 1; i >= 0 && !na; i-- )
2971   {
2972     if ( !lst.at( i )->isEmpty() )
2973       na = lst.at( i );
2974   }
2975
2976   for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
2977   {
2978     if ( !lst.at( j )->isEmpty() )
2979         na = lst.at( j );
2980   }
2981   return na;
2982 }
2983
2984 /*!
2985   \brief Get workarea covering point.
2986   \return workarea
2987   \param p point
2988 */
2989 QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
2990 {
2991   QtxWorkstackArea* area = 0;
2992   QList<QtxWorkstackArea*> lst;
2993   areas( mySplit, lst, true );
2994   for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end() && !area; ++it )
2995   {
2996     QtxWorkstackArea* cur = *it;
2997     QRect r = cur->geometry();
2998     if ( cur->parentWidget() )
2999       r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
3000     if ( r.contains( p ) )
3001       area = cur;
3002   }
3003   return area;
3004 }
3005
3006 /*!
3007   \brief Update internal state.
3008 */
3009 void QtxWorkstack::updateState()
3010 {
3011   updateState( mySplit );
3012 }
3013
3014 /*!
3015   \brief Update splitter state.
3016   \param split splitter to be updated
3017 */
3018 void QtxWorkstack::updateState( QSplitter* split )
3019 {
3020   QList<QSplitter*> recList;
3021   splitters( split, recList, false );
3022   for ( QList<QSplitter*>::iterator itr = recList.begin(); itr != recList.end(); ++itr )
3023     updateState( *itr );
3024
3025   QList<QSplitter*> splitList;
3026   splitters( split, splitList, false );
3027
3028   QList<QtxWorkstackArea*> areaList;
3029   areas( split, areaList, false );
3030
3031   bool vis = false;
3032   for ( QList<QtxWorkstackArea*>::iterator it = areaList.begin(); it != areaList.end(); ++it )
3033   {
3034     if ( (*it)->isEmpty() )
3035       (*it)->hide();
3036     else
3037     {
3038       (*it)->show();
3039       vis = true;
3040     }
3041   }
3042
3043   if ( split == mySplit )
3044     return;
3045
3046   for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end() && !vis; ++iter )
3047     vis = (*iter)->isVisibleTo( (*iter)->parentWidget() );
3048
3049   if ( areaList.isEmpty() && splitList.isEmpty() )
3050     delete split;
3051   else
3052     split->setVisible( vis );
3053 }
3054
3055 /*!
3056   \brief Dump workstack configuration to the state description array.
3057   \param version number
3058   \return state byte array.
3059 */
3060 QByteArray QtxWorkstack::saveState( int version ) const
3061 {
3062   QByteArray data;
3063
3064   QDataStream stream( &data, QIODevice::WriteOnly );
3065   stream << QtxWorkstack::VersionMarker;
3066   stream << version;
3067   saveState( stream );
3068
3069   return data;
3070 }
3071
3072 /*!
3073   \brief Restore workstack configuration from the state description array.
3074   \param version number
3075   \return restore performing state
3076 */
3077 bool QtxWorkstack::restoreState( const QByteArray& state, int version )
3078 {
3079   if ( state.isEmpty() )
3080     return false;
3081
3082   QByteArray sd = state;
3083   QDataStream stream( &sd, QIODevice::ReadOnly );
3084   int marker, ver;
3085   stream >> marker;
3086   stream >> ver;
3087   if ( stream.status() != QDataStream::Ok || marker != QtxWorkstack::VersionMarker || ver != version )
3088     return false;
3089
3090   return restoreState( stream );
3091 }
3092
3093 void QtxWorkstack::saveState( QDataStream& stream ) const
3094 {
3095   mySplit->saveState( stream );
3096 }
3097
3098 bool QtxWorkstack::restoreState( QDataStream& stream )
3099 {
3100   QMap<QString, QtxWorkstackChild*> map;
3101   QList<QtxWorkstackArea*> areaList;
3102   areas( mySplit, areaList, true );
3103   for ( QList<QtxWorkstackArea*>::const_iterator it = areaList.begin(); it != areaList.end(); ++it )
3104   {
3105     QtxWorkstackArea* area = *it;
3106     QList<QtxWorkstackChild*> childList = area->childList();
3107     for ( QList<QtxWorkstackChild*>::iterator itr = childList.begin(); itr != childList.end(); ++itr )
3108     {
3109       QtxWorkstackChild* c = *itr;
3110       if ( !c->widget() )
3111         continue;
3112
3113       map.insert( c->widget()->objectName(), c );
3114
3115       qDebug( "QtxWorkstack::restoreState: found widget \"%s\"", (const char*)c->widget()->objectName().toLatin1() );
3116     }
3117   }
3118
3119   int marker;
3120   stream >> marker;
3121   if ( stream.status() != QDataStream::Ok || marker != QtxWorkstack::SplitMarker )
3122     return false;
3123
3124   QtxWorkstackSplitter* split = new QtxWorkstackSplitter( this );
3125   if ( layout() )
3126     layout()->addWidget( split );
3127
3128   bool ok = split->restoreState( stream, map );
3129   if ( !ok )
3130     delete split;
3131   else
3132   {
3133     mySplit->deleteLater();
3134     mySplit = split;
3135
3136     QList<QtxWorkstackArea*> aList;
3137     areas( mySplit, aList, true );
3138
3139     QtxWorkstackArea* a = !aList.isEmpty() ? aList.first() : 0;
3140     for ( QMap<QString, QtxWorkstackChild*>::const_iterator it = map.begin(); it != map.end(); ++it )
3141     {
3142       QtxWorkstackChild* c = it.value();
3143       if ( c->widget() )
3144         c->widget()->setVisible( false );
3145       if ( a )
3146         a->insertChild( c );
3147       else
3148         c->setVisible( false );
3149     }
3150   }
3151
3152   return ok;
3153 }
3154
3155 /*!
3156   \brief Set resize mode of all splitters opaque or transparent.
3157   \param opaque opaque mode
3158 */
3159 void QtxWorkstack::setOpaqueResize( bool opaque )
3160 {
3161   QList<QSplitter*> splitList;
3162   splitters( mySplit, splitList, true );
3163   splitList << mySplit;
3164   foreach( QSplitter* split, splitList )
3165     split->setOpaqueResize( opaque );
3166 }
3167
3168 /*!
3169   \brief Get resize mode of all splitters: opaque (\c true) or transparent (\c false).
3170   \return current opaque mode
3171 */
3172 bool QtxWorkstack::opaqueResize() const
3173 {
3174   return mySplit->opaqueResize();
3175 }
3176
3177
3178 /*!
3179   \fn void QtxWorkstack::windowActivated( QWidget* w )
3180   \brief Emitted when the workstack's child widget \w is activated.
3181   \param w widget being activated
3182 */
3183
3184 /*!
3185   \brief Gets area containing given widget
3186   \param wid widget
3187   \return pointer to QtxWorkstackArea* object
3188 */
3189 QtxWorkstackArea* QtxWorkstack::wgArea( QWidget* wid ) const
3190 {
3191   QtxWorkstackArea* resArea = 0;
3192
3193   QList<QtxWorkstackArea*> areaList;
3194   areas( mySplit, areaList, true );
3195
3196   QList<QtxWorkstackArea*>::ConstIterator it;
3197   for ( it = areaList.begin(); it != areaList.end() && !resArea; ++it )
3198   {
3199     if ( (*it)->contains( wid ) )
3200       resArea = *it;
3201   }
3202
3203   return resArea;
3204 }
3205
3206 /*!
3207   \brief Moves the first widget to the same area which the second widget belongs to
3208   \param wid widget to be moved
3209   \param wid_to widget specified the destination area
3210   \param before specifies whether the first widget has to be moved before or after
3211          the second widget
3212   \return TRUE if operation is completed successfully, FALSE otherwise
3213 */
3214 bool QtxWorkstack::move( QWidget* wid, QWidget* wid_to, const bool before )
3215 {
3216   if ( wid && wid_to )
3217   {
3218     QtxWorkstackArea* area_src = wgArea( wid );
3219     QtxWorkstackArea* area_to = wgArea( wid_to );
3220     if ( area_src && area_to )
3221     {
3222       // find index of the second widget
3223       QWidgetList wgList = area_to->widgetList();
3224       QWidgetList::ConstIterator it;
3225       int idx = 0;
3226       for ( it = wgList.begin(); it != wgList.begin(); ++it, idx++ )
3227       {
3228         if ( *it == wid_to )
3229           break;
3230       }
3231
3232       if ( idx < wgList.count() ) // paranoidal check
3233       {
3234         if ( !before )
3235           idx++;
3236         area_src->removeWidget( wid, true );
3237         area_to->insertWidget( wid, idx );
3238         wid->showMaximized();
3239         return true;
3240       }
3241     }
3242   }
3243   return false;
3244 }
3245
3246 /*!
3247   \brief Group all windows in one area
3248   \return TRUE if operation is completed successfully, FALSE otherwise
3249 */
3250 void QtxWorkstack::stack()
3251 {
3252   QWidgetList wgList = windowList();
3253   if ( !wgList.count() )
3254     return; // nothing to do
3255
3256   QtxWorkstackArea* area_to = 0;
3257   QWidgetList::ConstIterator it;
3258   for ( it = wgList.begin(); it != wgList.end(); ++it )
3259   {
3260     QtxWorkstackArea* area_src = 0;
3261     if ( !area_to )
3262     {
3263       area_to = wgArea( *it );
3264       area_src = area_to;
3265     }
3266     else
3267       area_src = wgArea( *it );
3268
3269     if ( area_src != area_to )
3270     {
3271       area_src->removeWidget( *it, true );
3272       area_to->insertWidget( *it, -1 );
3273       (*it)->showMaximized();
3274     }
3275   }
3276 }
3277
3278 QAction* QtxWorkstack::action( const int id ) const
3279 {
3280   return myActionsMap.contains( id ) ? myActionsMap[id] : 0;
3281 }