Salome HOME
Updated copyright comment
[modules/gui.git] / src / Qtx / QtxWorkstack.cxx
index 25358a51b36199d7da0b323b40181f01e84dbeac..6a5ba33f5e7173d72d6b2ab67fdc874155fc9128 100644 (file)
+// Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
+//
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
 // File:      QtxWorkstack.cxx
 // Author:    Sergey TELKOV
-
+//
 #include "QtxWorkstack.h"
 
-#include <qstyle.h>
-#include <qaction.h>
-#include <qlayout.h>
-#include <qpixmap.h>
-#include <qiconset.h>
-#include <qpainter.h>
-#include <qsplitter.h>
-#include <qpopupmenu.h>
-#include <qobjectlist.h>
-#include <qpushbutton.h>
-#include <qwidgetstack.h>
-#include <qapplication.h>
+#include "QtxAction.h"
+
+#include <QMenu>
+#include <QStyle>
+#include <QRegExp>
+#include <QLayout>
+#include <QPainter>
+#include <QDataStream>
+#include <QFocusEvent>
+#include <QMouseEvent>
+#include <QRubberBand>
+#include <QApplication>
+#include <QStyleOption>
+#include <QInputDialog>
+#include <QStackedWidget>
+#include <QAbstractButton>
 
 /*!
-    Class: QtxWorkstack [Public]
-    Descr:
+  \class QtxWorkstackArea::WidgetEvent
+  \internal
+  \brief Internal class used to forward child widgets events to the workarea
 */
 
-QtxWorkstack::QtxWorkstack( QWidget* parent )
-: QWidget( parent ),
-myWin( 0 ),
-myArea( 0 )
+class QtxWorkstackArea::WidgetEvent : public QEvent
 {
-  myActionsMap.insert( SplitVertical,   new QAction( tr( "Split vertically" ),   0, this ));
-  myActionsMap.insert( SplitHorizontal, new QAction( tr( "Split horizontally" ), 0, this ));
-  myActionsMap.insert( Close,           new QAction( tr( "Close" ),              0, this ));                  
+public:
+  WidgetEvent( Type t, QtxWorkstackChild* w = 0 ) : QEvent( t ), myChild( w ) {};
 
-  connect( myActionsMap[SplitVertical], SIGNAL( activated() ), this, SLOT( splitVertical() ) );
-  connect( myActionsMap[SplitHorizontal], SIGNAL( activated() ), this, SLOT( splitHorizontal() ) );
-  connect( myActionsMap[Close], SIGNAL( activated() ), this, SLOT( onCloseWindow() ) );
+  QtxWorkstackChild* child() const { return myChild; }
 
-  QVBoxLayout* base = new QVBoxLayout( this );
-  mySplit = new QSplitter( this );
-  mySplit->setChildrenCollapsible( false );
-  base->addWidget( mySplit );
-}
+private:
+  QtxWorkstackChild* myChild;   // event receiver widget
+};
 
-QtxWorkstack::~QtxWorkstack()
+/*!
+  \class QtxWorkstackArea::RestoreEvent
+  \internal
+  \brief Internal class used to forward restore info events to the workarea
+*/
+
+class QtxWorkstackArea::RestoreEvent : public QtxWorkstackArea::WidgetEvent
+{
+public:
+  RestoreEvent( Type t, int id, int f, QtxWorkstackChild* w )
+  : WidgetEvent( t, w ), myId( id ), myFlags( f ) {};
+
+  int                id() const { return myId; }
+  int                flags() const { return myFlags; }
+
+private:
+  int                myId;
+  int                myFlags;
+};
+
+/*!
+  \class QtxWorkstackDrag
+  \internal
+  \brief Workstack drag object
+*/
+
+/*!
+  \brief Constructor.
+  \param ws parent workstack
+  \param child child widget container
+*/
+QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
+: QObject( 0 ),
+  myWS( ws ),
+  myChild( child ),
+  myTab( -1 ),
+  myArea( 0 ),
+  myTabRect( 0 ),
+  myAreaRect( 0 )
 {
+  QApplication::instance()->installEventFilter( this );
 }
 
-QWidgetList QtxWorkstack::windowList() const
+/*!
+  \brief Destructor.
+*/
+QtxWorkstackDrag::~QtxWorkstackDrag()
 {
-  QPtrList<QtxWorkstackArea> lst;
-  areas( mySplit, lst, true );
+  QApplication::instance()->removeEventFilter( this );
 
-  QWidgetList widList;
-  for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current(); ++it )
+  endDrawRect();
+}
+
+/*!
+  \brief Custom event filter.
+  \param o event receiver widget
+  \param e event
+  \return \c true if event should be filtered (stop further processing)
+*/
+bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
+{
+  switch ( e->type() )
   {
-    QWidgetList wids = it.current()->widgetList();
-    for ( QWidgetListIt itr( wids ); itr.current(); ++itr )
-      widList.append( itr.current() );
+  case QEvent::MouseMove:
+    updateTarget( ((QMouseEvent*)e)->globalPos() );
+    break;
+  case QEvent::MouseButtonRelease:
+    drawRect();
+    endDrawRect();
+    dropWidget();
+    deleteLater();
+    break;
+  default:
+    return false;
   }
-
-  return widList;
+  return true;
 }
 
-QWidgetList QtxWorkstack::splitWindowList() const
+/*!
+  \brief Detect and set dropping target widget.
+  \param p current dragging position
+*/
+void QtxWorkstackDrag::updateTarget( const QPoint& p )
 {
-  return myArea ? myArea->widgetList() : QWidgetList();
+  int tab = -1;
+  QtxWorkstackArea* area = detectTarget( p, tab );
+  setTarget( area, tab );
 }
 
-QWidget* QtxWorkstack::activeWindow() const
+/*!
+  \brief Detect dropping target.
+  \param p current dragging position
+  \param tab resulting target tab page index
+  \return target workarea or 0 if there is no correct drop target
+*/
+QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
 {
-  return myWin;
+  if ( p.isNull() )
+    return 0;
+
+  QtxWorkstackArea* area = myWS->areaAt( p );
+  if ( area )
+    tab = area->tabAt( p );
+  return area;
 }
 
-void QtxWorkstack::split( const int o )
+/*!
+  \brief Set dropping target.
+  \param area new target workarea
+  \param tab target workarea's tab page index
+*/
+void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
 {
-  QtxWorkstackArea* area = activeArea();
-  if ( !area )
+  if ( !area || ( myArea == area && tab == myTab ) )
     return;
 
-  if ( area->widgetList().count() < 2 )
-    return;
+  startDrawRect();
 
-  QWidget* curWid = area->activeWidget();
-  if ( !curWid )
-    return;
+  if ( myArea )
+    drawRect();
 
-  QSplitter* s = splitter( area );
-  QPtrList<QtxWorkstackArea> areaList;
-  areas( s, areaList );
+  myTab = tab;
+  myArea = area;
 
-  QPtrList<QSplitter> splitList;
-  splitters( s, splitList );
+  if ( myArea )
+    drawRect();
+}
 
-  QSplitter* trg = 0;
-  if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
-    trg = s;
+/*!
+  \brief Called when drop operation is finished.
 
-  if ( !trg )
-    trg = wrapSplitter( area );
+  Inserts dropped widget to the target workarea.
+*/
+void QtxWorkstackDrag::dropWidget()
+{
+  if ( myArea )
+    myArea->insertChild( myChild, myTab );
+}
 
-  if ( !trg )
+/*!
+  \brief Draw floating rectangle.
+*/
+void QtxWorkstackDrag::drawRect()
+{
+  if ( !myArea )
     return;
 
-  trg->setOrientation( (Orientation)o );
+  QRect r = myArea->floatRect();
+  int m = 2;
 
-  QtxWorkstackArea* newArea = createArea( 0 );
-  insertWidget( newArea, trg, area );
+  r.setTop( r.top() + m + 2 );
+  r.setLeft( r.left() + m + 2 );
+  r.setRight( r.right() - m - 2 );
+  r.setBottom( r.bottom() - m - 2 );
 
-  area->removeWidget( curWid );
-  newArea->insertWidget( curWid );
+  if ( myAreaRect )
+  {
+    myAreaRect->setGeometry( r );
+    myAreaRect->setVisible( r.isValid() );
+  }
 
-  distributeSpace( trg );
+  QRect tr = myArea->floatTab( myTab );
 
-  curWid->show();
-  curWid->setFocus();
+  tr.setTop( tr.top() + m );
+  tr.setLeft( tr.left() + m );
+  tr.setRight( tr.right() - m );
+  tr.setBottom( tr.bottom() - m );
+
+  if ( myTabRect )
+  {
+    myTabRect->setGeometry( tr );
+    myTabRect->setVisible( tr.isValid() );
+  }
 }
 
 /*!
-* \brief Split workarea of the given widget on two parts.
-* \param wid  - widget, belonging to this workstack
-* \param o    - orientation of splitting (Qt::Horizontal or Qt::Vertical)
-* \param type - type of splitting, see <VAR>SplitType</VAR> enumeration
+  \brief Delete rubber band on the end on the dragging operation.
 */
-void QtxWorkstack::Split (QWidget* wid, const Qt::Orientation o, const SplitType type)
+void QtxWorkstackDrag::endDrawRect()
 {
-  if (!wid) return;
-
-  // find area of the given widget
-  QtxWorkstackArea* area = NULL;
-  QPtrList<QtxWorkstackArea> allAreas;
-  areas(mySplit, allAreas, true);
+  delete myAreaRect;
+  myAreaRect = 0;
 
-  QPtrListIterator<QtxWorkstackArea> it (allAreas);
-  for (; it.current() && !area; ++it) {
-    if (it.current()->contains(wid))
-      area = it.current();
-  }
-  if (!area) return;
+  delete myTabRect;
+  myTabRect = 0;
+}
 
-  QWidgetList wids = area->widgetList();
-  if (wids.count() < 2)
-    return;
+/*!
+  \brief Create rubber band to be drawn on the dragging operation.
+*/
+void QtxWorkstackDrag::startDrawRect()
+{
+  if ( !myTabRect )
+    myTabRect = new QRubberBand( QRubberBand::Rectangle );
 
-  QSplitter* s = splitter(area);
-  QPtrList<QtxWorkstackArea> areaList;
-  areas(s, areaList);
+  myTabRect->hide();
 
-  QPtrList<QSplitter> splitList;
-  splitters(s, splitList);
+  if ( !myAreaRect )
+    myAreaRect = new QRubberBand( QRubberBand::Rectangle );
 
-  QSplitter* trg = 0;
-  if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
-    trg = s;
+  myAreaRect->hide();
+}
 
-  if (!trg) trg = wrapSplitter(area);
-  if (!trg) return;
 
-  trg->setOrientation(o);
+/*
+  \class CloseButton
+  \brief Workstack area close button.
+  \internal
+*/
+class CloseButton : public QAbstractButton
+{
+public:
+  CloseButton( QWidget* );
 
-  QtxWorkstackArea* newArea = createArea(0);
-  insertWidget(newArea, trg, area);
+  QSize        sizeHint() const;
+  QSize        minimumSizeHint() const;
 
-  switch (type) {
-  case SPLIT_STAY:
-    {
-      QWidgetListIt itr (wids);
-      for (; itr.current(); ++itr)
-      {
-        QWidget* wid_i = itr.current();
-        if (wid_i != wid) {
-          area->removeWidget(wid_i);
-          newArea->insertWidget(wid_i);
-        }
-      }
-    }
-    break;
-  case SPLIT_AT:
-    {
-      QWidgetListIt itr (wids);
-      for (; itr.current() && itr.current() != wid; ++itr) {
-      }
-      for (; itr.current(); ++itr) {
-        area->removeWidget(itr.current());
-        newArea->insertWidget(itr.current());
-      }
-    }
-    break;
-  case SPLIT_MOVE:
-    area->removeWidget(wid);
-    newArea->insertWidget(wid);
-    break;
-  }
+  void enterEvent( QEvent* );
+  void leaveEvent( QEvent* );
+  void paintEvent( QPaintEvent* );
+};
 
-  distributeSpace(trg);
+/*!
+  \brief Constructor
+  \internal
+  \param parent parent widget
+*/
+CloseButton::CloseButton( QWidget* parent )
+: QAbstractButton( parent )
+{
+ setFocusPolicy( Qt::NoFocus );
 }
 
 /*!
-* \brief Put given widget on top of its workarea
-* \param wid - widget, belonging to this workstack
+  \brief Get appropriate size for the button.
+  \internal
+  \return size value
 */
-/*
-void QtxWorkstack::OnTop (QWidget* wid)
+QSize CloseButton::sizeHint() const
 {
-  if ( !wid )
-    return;
-
-  // find area of the given widget
-  QtxWorkstackArea* area = 0;
-  QPtrList<QtxWorkstackArea> allAreas;
-  areas( mySplit, allAreas, true );
-  for ( QPtrListIterator<QtxWorkstackArea> it( allAreas ); it.current() && !area; ++it )
+  ensurePolished();
+  int dim = 0;
+  if( !icon().isNull() )
   {
-    if ( it.current()->contains( wid ) )
-      area = it.current();
+    const QPixmap pm = icon().pixmap( style()->pixelMetric( QStyle::PM_SmallIconSize ),
+                                      QIcon::Normal );
+    dim = qMax( pm.width(), pm.height() );
   }
+  return QSize( dim + 4, dim + 4 );
+}
 
-  if ( area )
-    area->setActiveWidget( wid );
+/*!
+  \brief Get minimum appropriate size for the button.
+  \internal
+  \return minimum size value
+*/
+QSize CloseButton::minimumSizeHint() const
+{
+  return sizeHint();
 }
+
+/*!
+  \brief Process mouse enter event.
+  \internal
+  \param event mouse enter event
 */
+void CloseButton::enterEvent( QEvent *event )
+{
+  if ( isEnabled() )
+    update();
+  QAbstractButton::enterEvent( event );
+}
 
 /*!
-* \brief Move widget(s) from source workarea into target workarea
-*        or just reorder widgets inside one workarea.
-* \param wid1 - widget from target workarea
-* \param wid2 - widget from source workarea
-* \param all  - if this parameter is TRUE, all widgets from source workarea will
-*               be moved into the target one, else only the \a wid2 will be moved
-*
-* Move \a wid2 in target workarea. Put it right after \a wid1.
-* If value of boolean argument is TRUE, all widgets from source workarea
-* will be moved together with \a wid2, source workarea will be deleted.
-* If \a wid1 and \a wid2 belongs to one workarea, simple reordering will take place.
+  \brief Process mouse leave event.
+  \internal
+  \param event mouse leave event
 */
-void QtxWorkstack::Attract ( QWidget* wid1, QWidget* wid2, const bool all )
+void CloseButton::leaveEvent( QEvent *event )
 {
-  if ( !wid1 || !wid2 )
-    return;
+  if( isEnabled() )
+    update();
+  QAbstractButton::leaveEvent( event );
+}
 
-  // find area of the widgets
-  QtxWorkstackArea *area1 = NULL, *area2 = NULL;
-  QPtrList<QtxWorkstackArea> allAreas;
-  areas(mySplit, allAreas, true);
-  QPtrListIterator<QtxWorkstackArea> it (allAreas);
-  for (; it.current() && !(area1 && area2); ++it) {
-    if (it.current()->contains(wid1))
-      area1 = it.current();
-    if (it.current()->contains(wid2))
-      area2 = it.current();
-  }
-  if (!area1 || !area2) return;
+/*!
+  \brief Process paint event.
+  \internal
+  \param event paint event
+*/
+void CloseButton::paintEvent( QPaintEvent* )
+{
+  QPainter p( this );
 
-  QWidget* curWid = area1->activeWidget();
-  if (!curWid) return;
+  QRect r = rect();
+  QStyleOption opt;
+  opt.init( this );
+  opt.state |= QStyle::State_AutoRaise;
+  if ( isEnabled() && underMouse() && !isChecked() && !isDown() )
+    opt.state |= QStyle::State_Raised;
+  if ( isChecked() )
+    opt.state |= QStyle::State_On;
+  if ( isDown() )
+    opt.state |= QStyle::State_Sunken;
+  style()->drawPrimitive( QStyle::PE_PanelButtonTool, &opt, &p, this );
 
-  if (area1 == area2) {
-    if (all) {
-      // Set wid1 at first position, wid2 at second
-      area1->insertWidget(wid1);
-      area1->insertWidget(wid2, 1);
-    } else {
-      // Set wid2 right after wid1
-      area1->removeWidget(wid2);
-      int wid1_ind = 0;
-      QWidgetList wids1 = area1->widgetList();
-      QWidgetListIt itr1 (wids1);
-      for (; itr1.current() && itr1.current() != wid1; ++itr1, ++wid1_ind);
-      area1->insertWidget(wid2, wid1_ind + 1);
-    }
-  } else {
-    int wid1_ind = 0;
-    QWidgetList wids1 = area1->widgetList();
-    QWidgetListIt itr1 (wids1);
-    for (; itr1.current() && itr1.current() != wid1; ++itr1, ++wid1_ind);
+  int shiftHorizontal = opt.state & QStyle::State_Sunken ? style()->pixelMetric( QStyle::PM_ButtonShiftHorizontal, &opt, this ) : 0;
+  int shiftVertical = opt.state & QStyle::State_Sunken ? style()->pixelMetric( QStyle::PM_ButtonShiftVertical, &opt, this ) : 0;
 
-    if (all) {
-      // Set wid2 right after wid1, other widgets from area2 right after wid2
-      QWidgetList wids2 = area2->widgetList();
-      QWidgetListIt itr2 (wids2);
-      for (int ind = wid1_ind + 1; itr2.current(); ++itr2, ++ind)
-      {
-        area2->removeWidget(itr2.current());
-        if (itr2.current() == wid2) {
-          area1->insertWidget(itr2.current(), wid1_ind + 1);
-        } else {
-          area1->insertWidget(itr2.current(), ind);
-        }
-      }
-    } else {
-      // Set wid2 right after wid1
-      area2->removeWidget(wid2);
-      area1->insertWidget(wid2, wid1_ind + 1);
-    }
-  }
+  r.adjust( 2, 2, -2, -2 );
+  r.translate( shiftHorizontal, shiftVertical );
 
-  area1->setActiveWidget( curWid );
+  QPixmap pm = icon().pixmap( style()->pixelMetric( QStyle::PM_SmallIconSize ), isEnabled() ?
+                              underMouse() ? QIcon::Active : QIcon::Normal
+                              : QIcon::Disabled,
+                              isDown() ? QIcon::On : QIcon::Off );
+  style()->drawItemPixmap( &p, r, Qt::AlignCenter, pm );
 }
 
-static void setSizes (QIntList& szList, const int item_ind,
-                      const int new_near, const int new_this, const int new_farr)
+
+/*!
+  \class QtxWorkstackSplitter
+  \internal
+  \brief Workstack splitter.
+*/
+
+/*!
+  \brief Constructor.
+  \param parent parent widget
+*/
+QtxWorkstackSplitter::QtxWorkstackSplitter( QWidget* parent )
+: QSplitter( parent )
 {
-  // set size to all items before an item # <item_ind>
-  int cur_pos = 0;
-  QIntList::iterator its = szList.begin();
-  for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
-    *its = new_near;
-  }
-  if (its == szList.end()) return;
-  // set size to item # <item_ind>
-  *its = new_this;
-  ++its;
-  // set size to all items after an item # <item_ind>
-  for (; its != szList.end(); ++its) {
-    *its = new_farr;
-  }
+  setChildrenCollapsible( false );
 }
 
 /*!
-* \brief Set position of the widget relatively its splitter.
-* \param wid - widget to set position of
-* \param pos - position relatively splitter. Value in range [0..1].
-*
-* Orientation of positioning will correspond to the splitter orientation.
+  \brief Destructor.
 */
-void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
+QtxWorkstackSplitter::~QtxWorkstackSplitter()
 {
-  if ( position < 0.0 || 1.0 < position)
-    return;
-
-  if ( !wid )
-    return;
-
-  // find area of the given widget
-  QtxWorkstackArea* area = NULL;
-  QPtrList<QtxWorkstackArea> allAreas;
-  areas(mySplit, allAreas, true);
-  for ( QPtrListIterator<QtxWorkstackArea> it( allAreas );
-       it.current() && !area;
-       ++it )
-  {
-    if (it.current()->contains(wid))
-      area = it.current();
-  }
+}
 
-  if ( !area )
-    return;
+/*!
+  \brief Get parent workstack
+  \return workstack owning this workarea
+*/
+QtxWorkstack* QtxWorkstackSplitter::workstack() const
+{
+  QtxWorkstack* ws = 0;
+  QWidget* wid = parentWidget();
+  while ( wid && !ws )
+  {
+    ws = ::qobject_cast<QtxWorkstack*>( wid );
+    wid = wid->parentWidget();
+  }
+  return ws;
+}
 
-  QSplitter* split = splitter( area );
-  if ( !split )
-    return;
+/*!
+  \brief Save the widget area configuration into data stream.
+*/
+void QtxWorkstackSplitter::saveState( QDataStream& stream ) const
+{
+  stream << QtxWorkstack::SplitMarker;
 
-  // find index of the area in its splitter
-  int item_ind = -1;
-  bool isFound = false;
-  const QObjectList* was = split->children();
-  for (QObjectListIt ito (*was); ito.current() && !isFound; ++ito, ++item_ind)
+  uchar flags = 0;
+  if ( orientation() == Qt::Horizontal )
+    flags |= QtxWorkstack::Horizontal;
+
+  stream << flags;
+  stream << count();
+
+  QList<int> sz = sizes();
+  for ( QList<int>::const_iterator it = sz.begin(); it != sz.end(); ++it )
+    stream << *it;
+
+  for ( int i = 0; i < count(); i++ )
   {
-    if (ito.current() == area)
-      isFound = true;
+    QWidget* wid = widget( i );
+    QtxWorkstackSplitter* split = ::qobject_cast<QtxWorkstackSplitter*>( wid );
+    if ( split )
+      split->saveState( stream );
+    else
+    {
+      QtxWorkstackArea* area = ::qobject_cast<QtxWorkstackArea*>( wid );
+      if ( area )
+              area->saveState( stream );
+    }
   }
-  if (!isFound || item_ind == 0)
-    return;
+}
 
-  QIntList szList = split->sizes();
-  int splitter_size = (split->orientation() == Horizontal ?
-                       split->width() : split->height());
-  int nb = szList.count();
+/*!
+  \brief Restore the widget area configuration from data stream info.
+  \return \c true in successful case.
+*/
+bool QtxWorkstackSplitter::restoreState( QDataStream& stream, QMap<QString, QtxWorkstackChild*>& map )
+{
+  int num = 0;
+  uchar flags = 0;
+
+  stream >> flags;
+  stream >> num;
+
+  setOrientation( flags & QtxWorkstack::Horizontal ? Qt::Horizontal : Qt::Vertical );
+
+  QList<int> sz;
+  for ( int s = 0; s < num; s++ )
+  {
+    int sn = 0;
+    stream >> sn;
+    sz.append( sn );
+  }
+
+  bool ok = true;
+  for ( int i = 0; i < num && ok; i++ )
+  {
+    int marker;
+    stream >> marker;
+
+    if ( stream.status() != QDataStream::Ok )
+      return false;
+
+    if ( marker == QtxWorkstack::SplitMarker )
+    {
+      QtxWorkstackSplitter* split = new QtxWorkstackSplitter( this );
+      addWidget( split );
+      split->setVisible( true );
+
+      ok = split->restoreState( stream, map );
+    }
+    else if ( marker == QtxWorkstack::AreaMarker )
+    {
+      QtxWorkstack* ws = workstack();
+      QtxWorkstackArea* area = ws->createArea( this );
+      addWidget( area );
+      area->setVisible( true );
+
+      ok = area->restoreState( stream, map );
+    }
+    else
+      return false;
+  }
+
+  if ( ok )
+    setSizes( sz );
 
-  int new_prev = int(splitter_size * position / item_ind);
-  int new_next  = int(splitter_size * (1.0 - position) / (nb - item_ind));
-  setSizes (szList, item_ind, new_prev, new_next, new_next);
-  split->setSizes(szList);
+  return ok;
 }
 
 /*!
-* \brief Set position of the widget relatively the entire workstack.
-* \param wid - widget to set position of
-* \param o   - orientation of positioning (Qt::Horizontal or Qt::Vertical).
-*              If o = Qt::Horizontal, horizontal position of \a wid will be changed.
-*              If o = Qt::Vertical, vertical position of \a wid will be changed.
-* \param pos - position relatively workstack. Value in range [0..1].
+  \class QtxWorkstackArea
+  \internal
+  \brief Workstack widget workarea.
 */
-void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
-                                        const double position )
+
+/*!
+  \brief Constructor.
+  \param parent parent widget
+*/
+QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
+: QFrame( parent )
 {
-  if ( position < 0.0 || 1.0 < position )
-    return;
+  setFrameStyle( QFrame::Panel | QFrame::Sunken );
+
+  QVBoxLayout* base = new QVBoxLayout( this );
+  base->setMargin( frameWidth() );
+  base->setSpacing( 0 );
+
+  myTop = new QWidget( this );
+  base->addWidget( myTop );
+
+  QHBoxLayout* tl = new QHBoxLayout( myTop );
+  tl->setMargin( 0 );
 
+  myBar = new QtxWorkstackTabBar( myTop );
+  tl->addWidget( myBar, 1 );
+
+  CloseButton* close = new CloseButton( myTop );
+  close->setIcon( style()->standardIcon( QStyle::SP_TitleBarCloseButton ) );
+  myClose = close;
+  tl->addWidget( myClose );
+
+  myStack = new QStackedWidget( this );
+
+  base->addWidget( myStack, 1 );
+
+  connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
+  connect( myBar, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
+  connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
+  connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
+
+  updateState();
+
+  updateActiveState();
+
+  QApplication::instance()->installEventFilter( this );
+}
+
+/*!
+  \brief Destructor.
+*/
+QtxWorkstackArea::~QtxWorkstackArea()
+{
+  QApplication::instance()->removeEventFilter( this );
+}
+
+/*!
+  \brief Check if workarea contains any widgets.
+  \return \c true if area is null (havn't any child widgets)
+*/
+bool QtxWorkstackArea::isNull() const
+{
+  return myList.isEmpty();
+}
+
+/*!
+  \brief Check if workarea contains visible widgets.
+  \return \c true if area is empty (all child widgets are removed or now shown)
+*/
+bool QtxWorkstackArea::isEmpty() const
+{
+  bool res = false;
+  for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !res; ++it )
+    res = (*it)->visibility();
+  return !res;
+}
+
+/*!
+  \brief Add widget to the workarea.
+  \param wid widget to be added
+  \param idx position in the area widget to be added to
+  \param f widget flags
+  \return child widget container object (or 0 if index is invalid)
+*/
+QtxWorkstackChild* QtxWorkstackArea::insertWidget( QWidget* wid, const int idx, Qt::WindowFlags f )
+{
   if ( !wid )
+    return 0;
+
+  QtxWorkstackChild* c = child( wid );
+  if ( !c )
+    c = new QtxWorkstackChild( wid, myStack, f );
+
+  insertChild( c, idx );
+
+  return c;
+}
+
+void QtxWorkstackArea::insertChild( QtxWorkstackChild* child, const int idx )
+{
+  if ( !child )
     return;
 
-  int splitter_size = o == Horizontal ? mySplit->width() : mySplit->height();
-  int need_pos = int( position * splitter_size );
-  int splitter_pos = 0;
+  QtxWorkstackArea* a = child->area();
+  if ( a && a != this )
+    a->removeChild( child, false );
 
-  if ( setPosition( wid, mySplit, o, need_pos, splitter_pos ) != 0 )
+  int pos = myList.indexOf( child );
+  if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
+    return;
+
+  bool found = myList.contains( child );
+
+  myList.removeAll( child );
+  pos = idx < 0 ? myList.count() : idx;
+  myList.insert( qMin( pos, (int)myList.count() ), child );
+
+  if ( !found )
   {
-    // impossible to set required position
+    bool hasId = false;
+    for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !hasId; ++it )
+      hasId = (*it)->id() == child->id();
+
+    if ( hasId || child->id() < 0 )
+      child->setId( generateId() );
+
+    connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
+    connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
+    connect( child, SIGNAL( hidden( QtxWorkstackChild* ) ), this, SLOT( onChildHidden( QtxWorkstackChild* ) ) );
+    connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
+    connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
   }
+
+  updateState();
+
+  setWidgetActive( child->widget() );
+  child->widget()->setFocus();
 }
 
 /*!
-* \brief Sets the action's accelerator key to accel. 
-* \param id - the key of the action in the actions map.
-* \param accel - action's accelerator key.
+  \brief Create and show popup menu for the area.
+  \param p mouse pointer position at which popup menu should be shown
 */
-void QtxWorkstack::setAccel( const int id, const int accel )
+void QtxWorkstackArea::onContextMenuRequested( QPoint p )
 {
-  if ( !myActionsMap.contains( id ) )
+  const QtxWorkstackTabBar* bar = ::qobject_cast<const QtxWorkstackTabBar*>( sender() );
+  if ( !bar )
     return;
 
-  myActionsMap[id]->setAccel( accel );
+  QWidget* wid = 0;
+  int idx = tabAt( p );
+  if ( idx != -1 )
+    wid = widget( myBar->tabId( idx ) );
+
+  emit contextMenuRequested( wid, p );
 }
 
 /*!
-* \brief Returns the action's accelerator key.
-* \param id - the key of the action in the actions map.
-* \retval int  - action's accelerator key.
+  \brief Remove widget from workarea.
+  \param wid widget to be removed
+  \param del if \c true the widget should be also deleted
 */
-int QtxWorkstack::accel( const int id ) const
+void QtxWorkstackArea::removeWidget( QWidget* wid, const bool del )
 {
-  int res = 0;
-  if ( myActionsMap.contains( id ) )
-    res = myActionsMap[id]->accel();
-  return res;
+  removeChild( child( wid ), del );
 }
 
-static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
-                           const int item_ind, const int item_rel_pos,
-                           const int need_pos, const int splitter_pos)
+/*!
+  \brief Remove child from workarea.
+  \param child child to be removed
+  \param del if \c true the widget should be also deleted
+*/
+void QtxWorkstackArea::removeChild( QtxWorkstackChild* child, const bool del )
 {
-  if (item_ind == 0) { // cannot move in this splitter
-    return (need_pos - splitter_pos);
-  }
-
-  int delta = 0;
-  int new_prev = 0;
-  int new_this = szList[item_ind];
-  int new_next = 0;
+  if ( !myList.contains( child ) )
+    return;
 
-  bool isToCheck = false;
+  myStack->removeWidget( child );
 
-  if (need_pos < splitter_pos) {
-    // Set size of all previous workareas to zero <--
-    if (item_ind == nb - 1) {
-      // item iz last in the splitter, it will occupy all the splitter
-      new_this = splitter_size;
-    } else {
-      // recompute size of next items in splitter
-      new_next = (splitter_size - new_this) / (nb - item_ind - 1);
-    }
-    delta = need_pos - splitter_pos;
+  if ( myBar->indexOf( child->id() ) != -1 )
+    myBar->removeTab( myBar->indexOf( child->id() ) );
 
-  } else if (need_pos > (splitter_pos + splitter_size)) {
-    // Set size of all next workareas to zero -->
-    // recompute size of previous items in splitter
-    new_this = 0;
-    new_prev = (splitter_size - new_this) / item_ind;
-    delta = need_pos - (splitter_pos + splitter_size - new_this);
+  myList.removeAll( child );
 
-  } else { // required position lays inside this splitter
-    // Move workarea inside splitter into required position <->
-    int new_item_rel_pos = need_pos - splitter_pos;
-    new_prev = new_item_rel_pos / item_ind;
-    if (need_pos < (splitter_pos + item_rel_pos)) {
-      // Make previous workareas smaller, next - bigger
-      // No problem to keep old size of the widget
-    } else {
-      // Make previous workareas bigger, next - smaller
-      if (new_this > splitter_size - new_item_rel_pos) {
-        new_this = splitter_size - new_item_rel_pos;
-      }
-      // jfa to do: in this case fixed size of next widgets could prevent right resizing
-      isToCheck = true;
-    }
-    if (item_ind == nb - 1) {
-      new_this = splitter_size - new_item_rel_pos;
-    } else {
-      new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
-    }
-    delta = 0;
+  if ( del )
+    delete child;
+  else if ( child->widget() )
+  {
+    disconnect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
+    disconnect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
+    disconnect( child, SIGNAL( hidden( QtxWorkstackChild* ) ), this, SLOT( onChildHidden( QtxWorkstackChild* ) ) );
+    disconnect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
+    disconnect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
   }
 
-  setSizes (szList, item_ind, new_prev, new_this, new_next);
-  return delta;
+  if ( isNull() )
+    deleteLater();
+  else
+    updateState();
+}
+
+QList<QtxWorkstackChild*> QtxWorkstackArea::childList() const
+{
+  return myList;
 }
 
 /*!
-* \brief Set position of given widget.
-* \param wid          - widget to be moved
-* \param split        - currently processed splitter (goes from more common
-*                       to more particular splitter in recursion calls)
-* \param o            - orientation of positioning
-* \param need_pos     - required position of the given widget in pixels
-*                       (from top/left side of workstack area)
-* \param splitter_pos - position of the splitter \a split
-*                       (from top/left side of workstack area)
-* \retval int - returns difference between a required and a distinguished position.
-* 
-* Internal method. Recursively calls itself.
-* Is called from <VAR>SetRelativePosition</VAR> public method.
+  \brief Get all visible child widgets.
+  \return list of visible child widgets
 */
-int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
-                               const int need_pos, const int splitter_pos )
+QWidgetList QtxWorkstackArea::widgetList() const
 {
-  if ( !wid || !split )
-    return need_pos - splitter_pos;
-
-  // Find corresponding sub-splitter.
-  // Find also index of appropriate item in current splitter.
-  int cur_ind = 0, item_ind = 0;
-  bool isBottom = false, isFound = false;
-  QSplitter* sub_split = NULL;
-  const QObjectList* objs = split->children();
-  if ( objs )
+  QWidgetList lst;
+  for ( ChildList::const_iterator it = myList.begin(); it != myList.end(); ++it )
   {
-    for (QObjectListIt it (*objs); it.current() && !isFound; ++it)
-    {
-      if (it.current()->inherits( "QtxWorkstackArea")) {
-        if (((QtxWorkstackArea*)it.current())->contains(wid)) {
-          item_ind = cur_ind;
-          isBottom = true;
-          isFound = true;
-        }
-        cur_ind++;
-      } else if (it.current()->inherits("QSplitter")) {
-        QPtrList<QtxWorkstackArea> areaList;
-        areas((QSplitter*)it.current(), areaList, true);
-        for (QPtrListIterator<QtxWorkstackArea> ita (areaList);
-             ita.current() && !isFound;
-             ++ita)
-        {
-          if (ita.current()->contains(wid)) {
-            item_ind = cur_ind;
-            isFound = true;
-            sub_split = (QSplitter*)it.current();
-          }
-        }
-        cur_ind++;
-      }
-    }
+    QtxWorkstackChild* c = *it;
+    if ( c->visibility() )
+      lst.append( c->widget() );
   }
-  if (!isFound)
-    return (need_pos - splitter_pos);
-
-  if (split->orientation() == o) {
-    // Find coordinates of near and far sides of the appropriate item relatively current splitter
-    int splitter_size = (o == Horizontal ? split->width() : split->height());
-    QIntList szList = split->sizes();
-    int nb = szList.count();
-    int item_rel_pos = 0; // position of near side of item relatively this splitter
-    for (int i = 0; i < item_ind; i++) {
-      item_rel_pos += szList[i];
-    }
-    int item_size = szList[item_ind]; // size of item
-    int item_pos = splitter_pos + item_rel_pos;
-
-    // Resize splitter items to complete the conditions
-    if (isBottom) {
-      // I. Bottom of splitters stack reached
+  return lst;
+}
 
-      int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
-      split->setSizes(szList);
-      // Recompute delta, as some windows can reject given size
-      int new_item_rel_pos = 0;
-      QIntList szList1 = split->sizes();
-      for (int i = 0; i < item_ind; i++) {
-        new_item_rel_pos += szList1[i];
-      }
-      delta = need_pos - (splitter_pos + new_item_rel_pos);
-      return delta;
+/*!
+  \brief Get active child widget.
+  \return active widget
+*/
+QWidget* QtxWorkstackArea::activeWidget() const
+{
+  return widget( myBar->tabId( myBar->currentIndex() ) );
+}
 
-    } else {
-      // II. Bottom of splitters stack is not yet reached
+/*!
+  \brief Set active widget.
+  \param wid widget to be made active
+*/
+void QtxWorkstackArea::setActiveWidget( QWidget* wid )
+{
+  myBar->setCurrentIndex( myBar->indexOf( widgetId( wid ) ) );
+}
 
-      if (item_ind == 0) { // cannot move in this splitter
-        // Process in sub-splitter
-        return setPosition(wid, sub_split, o, need_pos, splitter_pos);
-      }
-
-      int new_prev = 0;
-      int new_this = szList[item_ind];
-      int new_next = 0;
-
-      if (need_pos < splitter_pos) {
-        // Set size of all previous workareas to zero <--
-        if (item_ind == nb - 1) {
-          new_this = splitter_size;
-        } else {
-          new_next = (splitter_size - new_this) / (nb - item_ind - 1);
-        }
-        setSizes (szList, item_ind, new_prev, new_this, new_next);
-        split->setSizes(szList);
-        // Recompute splitter_pos, as some windows can reject given size
-        int new_item_rel_pos = 0;
-        QIntList szList1 = split->sizes();
-        for (int i = 0; i < item_ind; i++) {
-          new_item_rel_pos += szList1[i];
-        }
-        // Process in sub-splitter
-        return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
-      } else if (need_pos > (splitter_pos + splitter_size)) {
-        // Set size of all next workareas to zero -->
-        new_prev = (splitter_size - new_this) / item_ind;
-        setSizes (szList, item_ind, new_prev, new_this, new_next);
-        split->setSizes(szList);
-        // Recompute splitter_pos, as some windows can reject given size
-        int new_item_rel_pos = 0;
-        QIntList szList1 = split->sizes();
-        for (int i = 0; i < item_ind; i++) {
-          new_item_rel_pos += szList1[i];
-        }
-        // Process in sub-splitter
-        return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
-      } else {
-        // Set appropriate size of all previous/next items <->
-        int new_item_rel_pos = item_rel_pos;
-        if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
-          // Move item inside splitter into required position <->
-          int new_this = szList[item_ind];
-          int new_next = 0;
-          new_item_rel_pos = need_pos - splitter_pos;
-          if ((item_pos + item_size) < need_pos) {
-            //new_item_rel_pos = need_pos - (item_pos + item_size);
-            new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
-          }
-          int new_prev = new_item_rel_pos / item_ind;
-          if (need_pos < (splitter_pos + item_rel_pos)) {
-            // Make previous workareas smaller, next - bigger
-            // No problem to keep old size of the widget
-          } else {
-            // Make previous workareas bigger, next - smaller
-            if (new_this > splitter_size - new_item_rel_pos) {
-              new_this = splitter_size - new_item_rel_pos;
-            }
-          }
-          if (item_ind == nb - 1) {
-            new_this = splitter_size - new_item_rel_pos;
-          } else {
-            new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
-          }
-          setSizes (szList, item_ind, new_prev, new_this, new_next);
-          split->setSizes(szList);
-          // Recompute new_item_rel_pos, as some windows can reject given size
-          new_item_rel_pos = 0;
-          QIntList szList1 = split->sizes();
-          for (int i = 0; i < item_ind; i++) {
-            new_item_rel_pos += szList1[i];
-          }
-        } else {
-          // Do nothing
-        }
-        // Process in sub-splitter
-        int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
-        if (add_pos == 0)
-          return 0;
-
-        // this can be if corresponding workarea is first in sub-splitter
-        // or sub-splitter has another orientation
-
-        // Resize ones again to reach precize position <->
-        int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
-
-        // Move workarea inside splitter into required position <->
-        int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
-                                     new_item_rel_pos, need_pos_1, splitter_pos);
-        split->setSizes(szList);
-        // Recompute new_item_rel_pos, as some windows can reject given size
-        new_item_rel_pos = 0;
-        QIntList szList1 = split->sizes();
-        for (int i = 0; i < item_ind; i++) {
-          new_item_rel_pos += szList1[i];
-        }
-        delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
-        return delta_1;
-      }
-    }
-  } else {
-    return setPosition(wid, sub_split, o, need_pos, splitter_pos);
-  }
-
-  return 0;
+/*!
+  \brief Check if area owns the specified widget.
+  \param wid widget to be checked
+  \return \c true if area contains widget
+*/
+bool QtxWorkstackArea::contains( QWidget* wid ) const
+{
+  return child( wid );
 }
 
-void QtxWorkstack::distributeSpace( QSplitter* split ) const
+/*!
+  \brief Check if workarea is active.
+  \return \c true if area is active
+*/
+bool QtxWorkstackArea::isActive() const
 {
-  if ( !split )
-    return;
+  QtxWorkstack* ws = workstack();
+  if ( !ws )
+    return false;
 
-  QIntList szList = split->sizes();
-  int size = ( split->orientation() == Horizontal ?
-               split->width() : split->height() ) / szList.count();
-  for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
-    *it = size;
-  split->setSizes( szList );
+  return ws->activeArea() == this;
 }
 
-void QtxWorkstack::splitVertical()
+/*!
+  \brief Update active tab bar state (active/inactive).
+*/
+void QtxWorkstackArea::updateActiveState()
 {
-  split( Qt::Vertical );
+  myBar->setActive( isActive() );
 }
 
-void QtxWorkstack::splitHorizontal()
+/*!
+  \brief Get parent workstack
+  \return workstack owning this workarea
+*/
+QtxWorkstack* QtxWorkstackArea::workstack() const
 {
-  split( Qt::Horizontal );
+  QtxWorkstack* ws = 0;
+  QWidget* wid = parentWidget();
+  while ( wid && !ws )
+  {
+    ws = ::qobject_cast<QtxWorkstack*>( wid );
+    wid = wid->parentWidget();
+  }
+  return ws;
 }
 
-QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
-{
-  if ( !area )
-    return 0;
-
-  QSplitter* pSplit = splitter( area );
-  if ( !pSplit )
-    return 0;
-
-  bool upd = pSplit->isUpdatesEnabled();
-  pSplit->setUpdatesEnabled( false );
-
-  QIntList szList = pSplit->sizes();
-
-  QSplitter* wrap = new QSplitter( 0 );
-#if defined QT_VERSION && QT_VERSION >= 0x30200
-  wrap->setChildrenCollapsible( false );
-#endif
-  insertWidget( wrap, pSplit, area );
-  area->reparent( wrap, QPoint( 0, 0 ), true );
-
-  pSplit->setSizes( szList );
-
-  pSplit->setUpdatesEnabled( upd );
+/*!
+  \brief Custom event filter.
 
-  return wrap;
-}
+  Process events from child widgets.
 
-void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
+  \param o event receiver widget
+  \param e event
+  \return \c true if event should be filtered (stop further processing)
+*/
+bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
 {
-  if ( !wid || !pWid )
-    return;
-
-  QWidgetList moveList;
-  const QObjectList* lst = pWid->children();
-  if ( lst )
+  if ( o->isWidgetType() )
   {
-    bool found = false;
-    for ( QObjectListIt it( *lst ); it.current(); ++it )
+    QWidget* wid = (QWidget*)o;
+    if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
     {
-      if ( found && ( it.current()->inherits( "QSplitter" ) ||
-                      it.current()->inherits( "QtxWorkstackArea" ) ) )
-        moveList.append( (QWidget*)it.current() );
-      if ( it.current() == after )
-        found = true;
+      bool ok = false;
+      while ( !ok && wid && wid != myClose )
+      {
+        ok = wid == this;
+        wid = wid->parentWidget();
+      }
+      if ( ok )
+        QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
     }
   }
-
-  QMap<QWidget*, bool> map;
-  for ( QWidgetListIt it( moveList ); it.current(); ++it )
-  {
-    map.insert( it.current(), it.current()->isVisibleTo( it.current()->parentWidget() ) );
-    it.current()->reparent( 0, QPoint( 0, 0 ), false );
-  }
-
-  wid->reparent( pWid, QPoint( 0, 0 ), true );
-
-  for ( QWidgetListIt itr( moveList ); itr.current(); ++itr )
-    itr.current()->reparent( pWid, QPoint( 0, 0 ), map.contains( itr.current() ) ? map[itr.current()] : false );
+  return false;
 }
 
 /*!
-* \brief Closes the active window.
+  \brief Save the own widgets configuration into data stream.
 */
-void QtxWorkstack::onCloseWindow()
+void QtxWorkstackArea::saveState( QDataStream& stream ) const
 {
-  if ( activeWindow() )
-    activeWindow()->close();
-}
+  stream << QtxWorkstack::AreaMarker;
+  stream << myList.count();
+  stream << myBar->tabId( myBar->currentIndex() );
+  for ( ChildList::const_iterator it = myList.begin(); it != myList.end(); ++it )
+  {
+    QtxWorkstackChild* c = *it;
 
-void QtxWorkstack::onDestroyed( QObject* obj )
-{
-  QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
+    stream << QtxWorkstack::WidgetMarker;
 
-  if ( area == myArea )
-    myArea = 0;
+    stream << c->widget()->objectName();
+    stream << c->id();
 
-  if ( !myArea )
-  {
-    QtxWorkstackArea* cur = neighbourArea( area );
-    if ( cur )
-      cur->setFocus();
-  }
+    uchar flags = 0;
+    if ( c->visibility() )
+      flags |= QtxWorkstack::Visible;
 
-  QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
+    stream << flags;
+  }
 }
 
-void QtxWorkstack::onWindowActivated( QWidget* wid )
+/*!
+  \brief Restore the widgets configuration from data stream info.
+  \return \c true in successful case.
+*/
+bool QtxWorkstackArea::restoreState( QDataStream& stream, QMap<QString, QtxWorkstackChild*>& map )
 {
-  const QObject* obj = sender();
-  if ( !obj->inherits( "QtxWorkstackArea" ) )
-    return;
+  int num = 0;
+  int cur = -1;
 
-  setActiveArea( (QtxWorkstackArea*)obj );
-}
+  stream >> num;
+  stream >> cur;
 
-void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
-{
-  if ( myArea != area )
-    return;
+  QtxWorkstackChild* curChild = 0;
+  for ( int i = 0; i < num; i++ )
+  {
+    int marker;
+    stream >> marker;
 
-  QPtrList<QtxWorkstackArea> lst;
-  areas( mySplit, lst, true );
+    if ( stream.status() != QDataStream::Ok || marker != QtxWorkstack::WidgetMarker )
+      return false;
 
-  int idx = lst.find( area );
-  if ( idx == -1 )
-    return;
+    QString name;
+    stream >> name;
 
-  myWin = 0;
-  myArea = 0;
+    int id = -1;
+    stream >> id;
 
-  QtxWorkstackArea* newArea = neighbourArea( area );
-  if ( newArea && newArea->activeWidget() )
-    newArea->activeWidget()->setFocus();
+    uchar flags = 0;
+    stream >> flags;
 
-  QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
-}
+    QtxWorkstackChild* c = map.contains( name ) ? map[name] : 0;
+    if ( !c )
+    {
+      qWarning( "QtxWorkstack: Restored child widget \"%s\" not found.", (const char*)name.toUtf8() );
+      return false;
+    }
 
-void QtxWorkstack::onContextMenuRequested( QPoint p )
-{
-  if ( !activeArea() )
-    return;
+    map.remove( name );
 
-  QWidgetList lst = activeArea()->widgetList();
-  if ( lst.isEmpty() )
-    return;
+    if ( id == cur )
+      curChild = c;
 
-  QPopupMenu* pm = new QPopupMenu();
-  
-  if ( lst.count() > 1 )
-  {
-    myActionsMap[SplitVertical]->addTo( pm );
-    myActionsMap[SplitHorizontal]->addTo( pm );
-    pm->insertSeparator();
+    QApplication::postEvent( this, new RestoreEvent( (QEvent::Type)RestoreWidget, id, flags, c ) );
   }
-  myActionsMap[Close]->addTo( pm );
-  
-  pm->exec( p );
 
-  delete pm;
+  if ( curChild )
+    QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)MakeCurrent, curChild ) );
+
+  return true;
 }
 
-void QtxWorkstack::childEvent( QChildEvent* e )
+/*!
+  \brief Show/Hide tab bar.
+*/
+void QtxWorkstackArea::showTabBar( bool visible)
 {
-  if ( e->inserted() && e->child()->isWidgetType() )
-  {
-         QWidget* w = (QWidget*)e->child();
-         if ( w && w != mySplit )
-    {
-      targetArea()->insertWidget( w );
-      return;
-    }
-  }
-  QWidget::childEvent( e );
+  myTop->setVisible(visible);
+  myBar->setVisible(visible);
 }
 
-void QtxWorkstack::customEvent( QCustomEvent* e )
+
+/*!
+  \brief Get rectangle to be drawn when highlighting drop area.
+  \return area drop rectangle
+*/
+QRect QtxWorkstackArea::floatRect() const
 {
-  updateState();
+  QRect r = myStack->geometry();
+  return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
 }
 
-QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
+/*!
+  \brief Get rectangle to be drawn when highlighting drop area on tab bar.
+  \param idx tab index
+  \return tab bar drop rectrangle
+*/
+QRect QtxWorkstackArea::floatTab( const int idx ) const
 {
-  if ( !area )
-    return 0;
-
-  QSplitter* split = 0;
-
-  QWidget* wid = area->parentWidget();
-  if ( wid && wid->inherits( "QSplitter" ) )
-    split = (QSplitter*)wid;
-
-  return split;
+  QRect r = myBar->tabRect( idx );
+  return QRect( myBar->mapToGlobal( r.topLeft() ), r.size() );
 }
 
-void QtxWorkstack::splitters( QSplitter* split, QPtrList<QSplitter>& splitList, const bool rec ) const
+/*!
+  \brief Get tab index by point.
+  \param p point
+  \return tab covering point or -1 if there is no tab covering point
+*/
+int QtxWorkstackArea::tabAt( const QPoint& pnt ) const
 {
-  if ( !split )
-    return;
-
-  const QObjectList* objs = split->children();
-  if ( objs )
+  int idx = -1;
+  QPoint p = myBar->mapFromGlobal( pnt );
+  for ( int i = 0; i < myBar->count() && idx == -1; i++ )
   {
-    for ( QObjectListIt it( *objs ); it.current(); ++it )
-    {
-      if ( rec )
-        splitters( (QSplitter*)it.current(), splitList, rec );
-      if ( it.current()->inherits( "QSplitter" ) )
-        splitList.append( (QSplitter*)it.current() );
-    }
+    QRect r = myBar->tabRect( i );
+    if ( r.isValid() && r.contains( p ) )
+      idx = i;
   }
+  return idx;
 }
 
-void QtxWorkstack::areas( QSplitter* split, QPtrList<QtxWorkstackArea>& areaList, const bool rec ) const
+/*!
+  \brief Event handler for custom events.
+  \param e custom event
+*/
+void QtxWorkstackArea::customEvent( QEvent* e )
 {
-  if ( !split )
-    return;
+  WidgetEvent* we = (WidgetEvent*)e;
 
-  const QObjectList* objs = split->children();
-  if ( objs )
+  switch ( (int)we->type() )
   {
-    for ( QObjectListIt it( *objs ); it.current(); ++it )
+  case ActivateWidget:
+    myBar->updateActiveState();
+    // IMN 27/03/2015: This workaround caused by the bug INT PAL 0052623: OCC view blinking when
+    // using polyline sketcher which is reproduced on Unix systems with qt-4.8.4.
+    myStack->setUpdatesEnabled( false );
+    updateCurrent();
+    myStack->setUpdatesEnabled( true );
+    emit activated( activeWidget() );
+    break;
+  case FocusWidget:
+    if ( activeWidget() )
+    {
+      if ( !activeWidget()->focusWidget() )
+        activeWidget()->setFocus();
+      else
+      {
+        if ( activeWidget()->focusWidget()->hasFocus() )
+        {
+          QFocusEvent in( QEvent::FocusIn );
+                QApplication::sendEvent( this, &in );
+              }
+        else
+        {
+          activeWidget()->focusWidget()->setFocus();
+                myBar->updateActiveState();
+              }
+      }
+    }
+    break;
+  case MakeCurrent:
+    if ( we->child()->widget() )
+      setActiveWidget( we->child()->widget() );
+    break;
+  case RestoreWidget:
+    if ( we->child() )
     {
-      if ( it.current()->inherits( "QtxWorkstackArea" ) )
-        areaList.append( (QtxWorkstackArea*)it.current() );
-      else if ( rec && it.current()->inherits( "QSplitter" ) )
-        areas( (QSplitter*)it.current(), areaList, rec );
+      QtxWorkstackChild* c = we->child();
+      RestoreEvent* re = (RestoreEvent*)we;
+      if ( c->widget() )
+        c->widget()->setVisible( re->flags() & QtxWorkstack::Visible );
+      c->setId( re->id() );
+      insertChild( c );
     }
+    break;
+  default:
+    break;
   }
 }
 
-QtxWorkstackArea* QtxWorkstack::activeArea() const
-{
-  return myArea;
-}
-
-QtxWorkstackArea* QtxWorkstack::targetArea()
+/*!
+  \brief Customize focus in event handler.
+  \param e focus in event
+*/
+void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
 {
-  QtxWorkstackArea* area = activeArea();
-  if ( !area )
-    area = currentArea();
-  if ( !area )
-  {
-    QPtrList<QtxWorkstackArea> lst;
-    areas( mySplit, lst );
-    if ( !lst.isEmpty() )
-      area = lst.first();
-  }
+  QFrame::focusInEvent( e );
 
-  if ( !area )
-    area = createArea( mySplit );
+  myBar->updateActiveState();
 
-  return area;
+  emit activated( activeWidget() );
 }
 
-QtxWorkstackArea* QtxWorkstack::currentArea() const
+/*!
+  \brief Customize mouse press event handler.
+  \param e mouse press event
+*/
+void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
 {
-  QtxWorkstackArea* area = 0;
-  QWidget* wid = focusWidget();
-  while ( wid && !area )
-  {
-    if ( wid->inherits( "QtxWorkstackArea" ) )
-      area = (QtxWorkstackArea*)wid;
-    wid = wid->parentWidget();
-  }
+  QFrame::mousePressEvent( e );
 
-  return area;
+  emit activated( activeWidget() );
 }
 
-QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
+/*!
+  \brief Called when user presses "Close" button.
+*/
+void QtxWorkstackArea::onClose()
 {
-  QtxWorkstackArea* area = new QtxWorkstackArea( parent );
-
-  connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
-  connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
-  connect( area, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
-  connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
-
-  return area;
+  QWidget* wid = activeWidget();
+  if ( wid )
+    wid->close();
 }
 
-void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
+/*!
+  \brief Called when user selects any tab page.
+  \param idx tab page index (not used)
+*/
+void QtxWorkstackArea::onCurrentChanged( int /*idx*/ )
 {
-  QWidget* oldCur = myWin;
-
-  QtxWorkstackArea* oldArea = myArea;
-
-  myArea = area;
-
-  if ( myArea != oldArea )
-  {
-    if ( oldArea )
-      oldArea->updateActiveState();
-    if ( myArea )
-      myArea->updateActiveState();
-  }
-
-  if ( myArea )
-    myWin = myArea->activeWidget();
+  updateCurrent();
 
-  if ( myWin && oldCur != myWin )
-    emit windowActivated( myWin );
+  emit activated( activeWidget() );
 }
 
-QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
+/*!
+  \brief Called when user starts tab page dragging.
+*/
+void QtxWorkstackArea::onDragActiveTab()
 {
-  QPtrList<QtxWorkstackArea> lst;
-  areas( mySplit, lst, true );
-  int pos = lst.find( area );
-  if ( pos < 0 )
-    return 0;
+  QtxWorkstackChild* c = child( activeWidget() );
+  if ( !c )
+    return;
 
-  QtxWorkstackArea* na = 0;
-  for ( int i = pos - 1; i >= 0 && !na; i-- )
-  {
-    if ( !lst.at( i )->isEmpty() )
-      na = lst.at( i );
-  }
+  new QtxWorkstackDrag( workstack(), c );
+}
 
-  for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
-  {
-    if ( !lst.at( j )->isEmpty() )
-        na = lst.at( j );
-  }
-  return na;
+/*!
+  \brief Called when area's child widget container is destroyed.
+  \param obj widget container being destroyed
+*/
+void QtxWorkstackArea::onChildDestroyed( QObject* obj )
+{
+  removeChild( (QtxWorkstackChild*)obj, false );
 }
 
-QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
+/*!
+  \brief Called when child widget container is shown.
+  \param c child widget container being shown
+*/
+void QtxWorkstackArea::onChildShown( QtxWorkstackChild* /*c*/ )
 {
-  QtxWorkstackArea* area = 0;
-  QPtrList<QtxWorkstackArea> lst;
-  areas( mySplit, lst, true );
-  for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current() && !area; ++it )
-  {
-    QtxWorkstackArea* cur = it.current();
-    QRect r = cur->geometry();
-    if ( cur->parentWidget() )
-      r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
-    if ( r.contains( p ) )
-      area = cur;
-  }
-  return area;
+  updateState();
 }
 
-void QtxWorkstack::updateState()
+/*!
+  \brief Called when child widget container is hidden.
+  \param c child widget container being hidden
+*/
+void QtxWorkstackArea::onChildHidden( QtxWorkstackChild* /*c*/ )
 {
-  updateState( mySplit );
+  updateState();
 }
 
-void QtxWorkstack::updateState( QSplitter* split )
+/*!
+  \brief Called when child widget container is activated.
+  \param c child widget container being activated
+*/
+void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
 {
-  QPtrList<QSplitter> recList;
-  splitters( split, recList, false );
-  for ( QPtrListIterator<QSplitter> itr( recList ); itr.current(); ++itr )
-    updateState( itr.current() );
+  setWidgetActive( c->widget() );
+}
 
-  QPtrList<QSplitter> splitList;
-  splitters( split, splitList, false );
+/*!
+  \brief Called when child widget container's title is changed.
+  \param c child widget container which title is changed
+*/
+void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
+{
+  updateTab( c->widget() );
+}
 
-  QPtrList<QtxWorkstackArea> areaList;
-  areas( split, areaList, false );
+/*!
+  \brief Update current child widget container.
 
-  bool vis = false;
-  for ( QPtrListIterator<QtxWorkstackArea> it( areaList ); it.current(); ++it )
-  {
-    if ( it.current()->isEmpty() )
-      it.current()->hide();
-    else
-    {
-      it.current()->show();
-      vis = true;
-    }
-  }
+  Raises widget when active tab page is changed.
+*/
+void QtxWorkstackArea::updateCurrent()
+{
+  QWidget* cur = child( myBar->tabId( myBar->currentIndex() ) );
+  if ( cur )
+    myStack->setCurrentWidget( cur );
+}
 
-  if ( split == mySplit )
+/*!
+  \brief Update tab bar.
+  \param wid tab page widget
+*/
+void QtxWorkstackArea::updateTab( QWidget* wid )
+{
+  int idx = myBar->indexOf( widgetId( wid ) );
+  if ( idx < 0 )
     return;
 
-  for ( QPtrListIterator<QSplitter> iter( splitList ); iter.current() && !vis; ++iter )
-    vis = iter.current()->isVisibleTo( iter.current()->parentWidget() );
-
-  if ( areaList.isEmpty() && splitList.isEmpty() )
-    delete split;
-  else if ( vis )
-    split->show();
-  else
-    split->hide();
+  myBar->setTabIcon( idx, wid->windowIcon() );
+  myBar->setTabText( idx, wid->windowTitle() );
 }
 
 /*!
-    Class: QtxWorkstackArea [Internal]
-    Descr:
+  \brief Get child widget by specified identifier.
+  \param id widget ID
+  \return widget or 0, if identifier in invalid
 */
-
-QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
-: QWidget( parent )
+QWidget* QtxWorkstackArea::widget( const int id ) const
 {
-  QVBoxLayout* base = new QVBoxLayout( this );
-
-  QHBox* top = new QHBox( this );
-  base->addWidget( top );
-
-  myBar = new QtxWorkstackTabBar( top );
-
-  QPushButton* close = new QPushButton( top );
-  close->setPixmap( style().stylePixmap( QStyle::SP_TitleBarCloseButton ) );
-  close->setAutoDefault( true );
-  close->setFlat( true );
-  myClose = close;
-
-  top->setStretchFactor( myBar, 1 );
-
-  myStack = new QWidgetStack( this );
-
-  base->addWidget( myStack, 1 );
+  QtxWorkstackChild* c = child( id );
 
-  connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
-  connect( myBar, SIGNAL( selected( int ) ), this, SLOT( onSelected( int ) ) );
-  connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
-  connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SIGNAL( contextMenuRequested( QPoint ) ) );
-
-  updateState();
-
-  updateActiveState();
-
-  qApp->installEventFilter( this );
+  return c ? c->widget() : 0;
 }
 
-QtxWorkstackArea::~QtxWorkstackArea()
+/*!
+  \brief Get child widget identifier.
+  \param wid widget
+  \return widget ID or -1 if widget is not found
+*/
+int QtxWorkstackArea::widgetId( QWidget* wid ) const
 {
-  qApp->removeEventFilter( this );
+  QtxWorkstackChild* c = child( wid );
+
+  return c ? c->id() : -1;
 }
 
-bool QtxWorkstackArea::isEmpty() const
+/*!
+  \brief Set active child widget.
+  \param wid widget to be set active
+*/
+void QtxWorkstackArea::setWidgetActive( QWidget* wid )
 {
-  bool res = false;
-  for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !res; ++it )
-    res = it.data().vis;
-  return !res;
+  int id = widgetId( wid );
+  if ( id < 0 )
+    return;
+
+  myBar->setCurrentIndex( myBar->indexOf( id ) );
 }
 
-void QtxWorkstackArea::insertWidget( QWidget* wid, const int idx )
+/*!
+  \brief Update internal state.
+*/
+void QtxWorkstackArea::updateState()
 {
-  if ( !wid )
-    return;
+  bool updBar = myBar->updatesEnabled();
+  bool updStk = myStack->updatesEnabled();
+  myBar->setUpdatesEnabled( false );
+  myStack->setUpdatesEnabled( false );
 
-  int pos = myList.find( wid );
-  if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
-    return;
+  bool block = myBar->signalsBlocked();
+  myBar->blockSignals( true );
 
-  myList.removeRef( wid );
-  pos = idx < 0 ? myList.count() : idx;
-  myList.insert( QMIN( pos, (int)myList.count() ), wid );
-  if ( !myInfo.contains( wid ) )
+  QWidget* prev = activeWidget();
+
+  int idx = 0;
+  for ( ChildList::iterator it = myList.begin(); it != myList.end(); ++it )
   {
-    QtxWorkstackChild* child = new QtxWorkstackChild( wid, myStack );
-    myChild.insert( wid, child );
-    myInfo.insert( wid, WidgetInfo() );
-    myInfo[wid].id = generateId();
-    myInfo[wid].vis = wid->isVisibleTo( wid->parentWidget() );
+    QtxWorkstackChild* cont = *it;
+    QWidget* wid = cont->widget();;
+    int id = cont->id();
 
-    connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
-    connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
-    connect( child, SIGNAL( hided( QtxWorkstackChild* ) ), this, SLOT( onChildHided( QtxWorkstackChild* ) ) );
-    connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
-    connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
-  }
+    if ( id < 0 )
+      continue;
 
-  updateState();
+    bool vis = cont->visibility();
 
-  setWidgetActive( wid );
-}
+    int cIdx = myBar->indexOf( id );
+    if ( cIdx != -1 && ( !vis || myBar->indexOf( id ) != idx ) )
+      myBar->removeTab( cIdx );
 
-void QtxWorkstackArea::removeWidget( QWidget* wid )
-{
-  if ( !myList.contains( wid ) )
-    return;
+    if ( myBar->indexOf( id ) == -1 && vis )
+      myBar->setTabId( myBar->insertTab( idx, wid->windowTitle() ), id );
 
-  if ( myBar->tab( widgetId( wid ) ) )
-    myBar->removeTab( myBar->tab( widgetId( wid ) ) );
-  myStack->removeWidget( child( wid ) );
+    updateTab( wid );
 
-  myList.remove( wid );
-  myInfo.remove( wid );
-  myChild.remove( wid );
+    if ( !vis )
+      myStack->removeWidget( cont );
+    else if ( myStack->indexOf( cont ) < 0 )
+      myStack->addWidget( cont );
 
-  delete child( wid );
+    if ( vis )
+      idx++;
+  }
 
-  if ( myList.isEmpty() )
-    delete this;
-  else
-    updateState();
-}
+  int curId = widgetId( prev );
+  if ( myBar->indexOf( curId ) < 0 )
+  {
+    QtxWorkstackChild* c = 0;
+    int pos = myList.indexOf( child( prev ) );
+    for ( int i = pos - 1; i >= 0 && !c; i-- )
+    {
+      if ( myList.at( i )->visibility() )
+        c = myList.at( i );
+    }
 
-QWidgetList QtxWorkstackArea::widgetList() const
-{
-  QWidgetList lst;
-  for ( QWidgetListIt it( myList ); it.current(); ++it )
+    for ( int j = pos + 1; j < (int)myList.count() && !c; j++ )
+    {
+      if ( myList.at( j )->visibility() )
+        c = myList.at( j );
+    }
+
+    if ( c )
+      curId = c->id();
+  }
+
+  myBar->setCurrentIndex( myBar->indexOf( curId ) );
+
+  myBar->blockSignals( block );
+
+  updateCurrent();
+
+  myBar->updateActiveState();
+
+  myBar->setUpdatesEnabled( updBar );
+  myStack->setUpdatesEnabled( updStk );
+  if ( updBar )
+    myBar->update();
+  if ( updStk )
+    myStack->update();
+
+  QResizeEvent re( myBar->size(), myBar->size() );
+  QApplication::sendEvent( myBar, &re );
+
+  myBar->updateGeometry();
+
+  if ( isEmpty() )
+  {
+    hide();
+    emit deactivated( this );
+  }
+  else
   {
-    if ( widgetVisibility( it.current() ) )
-      lst.append( it.current() );
+    show();
+    if ( prev != activeWidget() )
+      emit activated( activeWidget() );
   }
-  return lst;
 }
 
-QWidget* QtxWorkstackArea::activeWidget() const
+/*!
+  \brief Generate unique widget identifier.
+  \return first non shared widget ID
+*/
+int QtxWorkstackArea::generateId() const
 {
-  return widget( myBar->currentTab() );
-}
+  QMap<int, int> map;
 
-void QtxWorkstackArea::setActiveWidget( QWidget* wid )
-{
-  myBar->setCurrentTab( widgetId( wid ) );
+  for ( ChildList::const_iterator it = myList.begin(); it != myList.end(); ++it )
+    map.insert( (*it)->id(), 0 );
+
+  int id = 0;
+  while ( map.contains( id ) )
+    id++;
+
+  return id;
 }
 
-bool QtxWorkstackArea::contains( QWidget* wid ) const
+/*!
+  \brief Get child widget container.
+  \param wid child widget
+  \return child widget container corresponding to the \a wid
+*/
+QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
 {
-  return myList.contains( wid );
+  QtxWorkstackChild* res = 0;
+  for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !res; ++it )
+  {
+    if ( (*it)->widget() == wid )
+      res = *it;
+  }
+  return res;
 }
 
-void QtxWorkstackArea::show()
+QtxWorkstackChild* QtxWorkstackArea::child( const int id ) const
 {
-  QMap<QWidget*, bool> map;
-  for ( QWidgetListIt it( myList ); it.current(); ++it )
+  QtxWorkstackChild* c = 0;
+  for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !c; ++it )
   {
-    map.insert( it.current(), isBlocked( it.current() ) );
-    setBlocked( it.current(), true );
+    if ( (*it)->id() == id )
+      c = *it;
   }
+  return c;
+}
 
-  QWidget::show();
+/*!
+  \fn void QtxWorkstackArea::activated( QWidget* w )
+  \brief Emitted when child widget is activated.
+  \param w child widget being activated
+*/
 
-  for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
-    setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
-}
+/*!
+  \fn void QtxWorkstackArea::contextMenuRequested( QWidget* w, QPoint p )
+  \brief Emitted when context popup menu is requested.
+  \param w child widget popup menu requested for
+  \param p point popup menu to be shown at
+*/
+
+/*!
+  \fn void QtxWorkstackArea::deactivated( QtxWorkstackArea* wa )
+  \brief Emitted when workarea is deactivated.
+  \param wa workarea being deactivated
+*/
 
-void QtxWorkstackArea::hide()
+/*!
+  \class QtxWorkstackChild
+  \internal
+  \brief Workarea child widget container.
+*/
+
+/*!
+  \brief Constructor.
+  \param wid child widget
+  \param parent parent widget
+  \param f widget flags
+*/
+QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent, Qt::WindowFlags f )
+: QWidget( parent ),
+  myId( 0 ),
+  myWidget( wid )
 {
-  QMap<QWidget*, bool> map;
-  for ( QWidgetListIt it( myList ); it.current(); ++it )
+  if ( myWidget )
   {
-    map.insert( it.current(), isBlocked( it.current() ) );
-    setBlocked( it.current(), true );
+    myWidget->setParent( this, f );
+    myWidget->installEventFilter( this );
+    if ( myWidget->focusProxy() )
+      myWidget->focusProxy()->installEventFilter( this );
+    myWidget->setVisible( myWidget->isVisibleTo( myWidget->parentWidget() ) );
+
+    QVBoxLayout* base = new QVBoxLayout( this );
+    base->setMargin( 0 );
+    base->addWidget( myWidget );
+
+    connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
   }
+}
+
+/*!
+  \brief Destructor.
+*/
+QtxWorkstackChild::~QtxWorkstackChild()
+{
+  QApplication::instance()->removeEventFilter( this );
+
+  if ( !widget() )
+    return;
+
+  disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
 
-  QWidget::hide();
+  widget()->hide();
+  widget()->removeEventFilter( this );
+  if ( widget()->focusProxy() )
+    widget()->focusProxy()->removeEventFilter( this );
 
-  for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
-    setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
+  widget()->setParent( 0 );
 }
 
-bool QtxWorkstackArea::isActive() const
+/*!
+  \brief Get child widget.
+  \return child widget
+*/
+QWidget* QtxWorkstackChild::widget() const
 {
-  QtxWorkstack* ws = workstack();
-  if ( !ws )
-    return false;
+  return myWidget;
+}
 
-  return ws->activeArea() == this;
+/*!
+  \brief Returns the id.
+*/
+int QtxWorkstackChild::id() const
+{
+  return myId;
 }
 
-void QtxWorkstackArea::updateActiveState()
+/*!
+  \brief Sets the id.
+*/
+void QtxWorkstackChild::setId( const int id )
 {
-  myBar->setActive( isActive() );
+  myId = id;
 }
 
-QtxWorkstack* QtxWorkstackArea::workstack() const
+/*!
+  \brief Returns true if this child window should be visible.
+*/
+bool QtxWorkstackChild::visibility()
 {
-  QtxWorkstack* ws = 0;
-  QWidget* wid = parentWidget();
-  while ( wid && !ws )
+  return myWidget ? myWidget->isVisibleTo( this ) : false;
+}
+
+QtxWorkstackArea* QtxWorkstackChild::area() const
+{
+  QtxWorkstackArea* a = 0;
+  QWidget* w = parentWidget();
+  while ( !a && w )
   {
-    if ( wid->inherits( "QtxWorkstack" ) )
-      ws = (QtxWorkstack*)wid;
-    wid = wid->parentWidget();
+    a = ::qobject_cast<QtxWorkstackArea*>( w );
+    w = w->parentWidget();
   }
-  return ws;
+
+  return a;
 }
 
-bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
+/*!
+  \brief Custom event filter.
+
+  Process events from child widgets.
+
+  \param o event receiver widget
+  \param e event
+  \return \c true if event should be filtered (stop further processing)
+*/
+bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
 {
   if ( o->isWidgetType() )
   {
-    QWidget* wid = (QWidget*)o;
-    if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
-    {
-      bool ok = false;
-      while ( !ok && wid && wid != myClose )
-      {
-        ok = wid == this;
-        wid = wid->parentWidget();
-      }
-      if ( ok )
-        QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
-    }
+    if ( e->type() == QEvent::WindowTitleChange || e->type() == QEvent::WindowIconChange )
+      emit captionChanged( this );
+
+    if ( !e->spontaneous() && e->type() == QEvent::ShowToParent )
+      emit shown( this );
+
+    if ( !e->spontaneous() && e->type() == QEvent::HideToParent )
+      emit hidden( this );
+
+    if ( e->type() == QEvent::FocusIn )
+      emit activated( this );
   }
-  return false;
+  return QWidget::eventFilter( o, e );
 }
 
-QRect QtxWorkstackArea::floatRect() const
+/*!
+  \brief Called when child widget is destroyed.
+  \param obj child widget being destroyed
+*/
+void QtxWorkstackChild::onDestroyed( QObject* /*obj*/ )
 {
-  QRect r = myStack->geometry();
-  return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
+  deleteLater();
 }
 
-QRect QtxWorkstackArea::floatTab( const int idx ) const
+/*!
+  \brief Customize child event handler.
+  \param e child event
+*/
+void QtxWorkstackChild::childEvent( QChildEvent* e )
 {
-  return myBar->tabRect( idx );
+  if ( e->removed() && e->child() == widget() )
+  {
+    myWidget = 0;
+    deleteLater();
+  }
+  QWidget::childEvent( e );
 }
 
-int QtxWorkstackArea::tabAt( const QPoint& p ) const
+/*!
+  \fn void QtxWorkstackChild::shown( QtxWorkstackChild* w )
+  \brief Emitted when child widget is shown.
+  \param w child widget container
+*/
+
+/*!
+  \fn void QtxWorkstackChild::hidden( QtxWorkstackChild* w )
+  \brief Emitted when child widget is hidden.
+  \param w child widget container
+*/
+
+/*!
+  \fn void QtxWorkstackChild::activated( QtxWorkstackChild* w )
+  \brief Emitted when child widget is activated.
+  \param w child widget container
+*/
+
+/*!
+  \fn void QtxWorkstackChild::captionChanged( QtxWorkstackChild* w )
+  \brief Emitted when child widget's title is changed.
+  \param w child widget container
+*/
+
+/*!
+  \class QtxWorkstackTabBar
+  \internal
+  \brief Workstack tab bar widget
+*/
+
+/*!
+  \brief Constructor.
+  \param parent parent widget
+*/
+QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
+: QTabBar( parent ),
+  myId( -1 ),myActive(false)
 {
-  int idx = -1;
-  for ( int i = 0; i < myBar->count() && idx == -1; i++ )
-  {
-    QRect r = myBar->tabRect( i );
-    if ( r.isValid() && r.contains( p ) )
-      idx = i;
-  }
-  return idx;
+  setDrawBase( true );
+  setElideMode( Qt::ElideNone );
+
+  connect( this, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
 }
 
-void QtxWorkstackArea::customEvent( QCustomEvent* e )
+/*!
+  \brief Destructor.
+*/
+QtxWorkstackTabBar::~QtxWorkstackTabBar()
 {
-  switch ( e->type() )
-  {
-  case ActivateWidget:
-    emit activated( activeWidget() );
-    break;
-  case FocusWidget:
-    if ( activeWidget() && !activeWidget()->focusWidget() )
-      activeWidget()->setFocus();
-    break;
-  case RemoveWidget:
-    removeWidget( (QWidget*)e->data() );
-    break;
-  }
 }
 
-void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
+/*!
+  \brief Get tab page identifier.
+  \param index tab page index
+  \return tab page ID or -1 if \a index is out of range
+*/
+int QtxWorkstackTabBar::tabId( const int index ) const
 {
-  QWidget::focusInEvent( e );
+  QVariant v = tabData( index );
+  if ( !v.canConvert( QVariant::Int ) )
+    return -1;
+  return v.toInt();
+}
 
-  emit activated( activeWidget() );
+/*!
+  \brief Set tab page identifier.
+  \param index tab page index
+  \param id tab page ID
+*/
+void QtxWorkstackTabBar::setTabId( const int index, const int id )
+{
+  setTabData( index, id );
 }
 
-void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
+/*!
+  \brief Get tab page index by specified identifier.
+  \param id tab page ID
+  \return tab page index or -1 if not found
+*/
+int QtxWorkstackTabBar::indexOf( const int id ) const
 {
-  QWidget::mousePressEvent( e );
+  int index = -1;
+  for ( int i = 0; i < (int)count() && index < 0; i++ )
+  {
+    if ( tabId( i ) == id )
+      index = i;
+  }
+  return index;
+}
 
-  emit activated( activeWidget() );
+/*!
+  \brief Check if the tab bar is active.
+  \return \c true if tab bar is active
+*/
+bool QtxWorkstackTabBar::isActive() const
+{
+  return myActive;
 }
 
-void QtxWorkstackArea::onClose()
+/*!
+  \brief Set tab bar active/inactive.
+  \param on new active state
+*/
+void QtxWorkstackTabBar::setActive( const bool on )
 {
-  QWidget* wid = activeWidget();
-  if ( wid )
-    wid->close();
+  if ( myActive == on )
+    return;
+
+  myActive = on;
+  updateActiveState();
 }
 
-void QtxWorkstackArea::onSelected( int id )
+/*!
+  \brief Update tab bar according to the 'active' state.
+*/
+void QtxWorkstackTabBar::updateActiveState()
 {
-  updateCurrent();
+  QColor bc = palette().color( QPalette::Text );
+  QColor ac = isActive() ? palette().color( QPalette::Highlight ) : bc;
+  for ( int i = 0; i < (int)count(); i++ )
+    setTabTextColor( i, currentIndex() == i ? ac : bc );
+}
 
-  emit activated( activeWidget() );
+/*!
+  \brief Called when current tab page is changed.
+  \param idx tab page index (not used)
+*/
+void QtxWorkstackTabBar::onCurrentChanged( int /*index*/ )
+{
+  updateActiveState();
 }
 
-void QtxWorkstackArea::onDragActiveTab()
+/*!
+  \brief Customize mouse move event handler.
+  \param e mouse event
+*/
+void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
+{
+  if ( myId != -1 && !tabRect( indexOf( myId ) ).contains( e->pos() ) )
+  {
+    myId = -1;
+    emit dragActiveTab();
+  }
+
+  QTabBar::mouseMoveEvent( e );
+}
+
+/*!
+  \brief Customize mouse press event handler.
+  \param e mouse event
+*/
+void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
+{
+  QTabBar::mousePressEvent( e );
+
+  if ( e->button() == Qt::LeftButton )
+    myId = tabId( currentIndex() );
+}
+
+/*!
+  \brief Customize mouse release event handler.
+  \param e mouse event
+*/
+void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
+{
+  QTabBar::mouseReleaseEvent( e );
+
+  myId = -1;
+
+  if ( e->button() == Qt::RightButton )
+    emit contextMenuRequested( e->globalPos() );
+}
+
+/*!
+  \brief Customize context menu event handler.
+  \param e context menu event
+*/
+void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
+{
+  if ( e->reason() != QContextMenuEvent::Mouse )
+    emit contextMenuRequested( e->globalPos() );
+}
+
+/*!
+  \brief Process widget change state events (style, palette, enable state changing, etc).
+  \param e change event (not used)
+*/
+void QtxWorkstackTabBar::changeEvent( QEvent* /*e*/ )
+{
+  updateActiveState();
+}
+
+/*
+void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
+{
+  if ( currentTab() != t->identifier() )
+  {
+    QFont fnt = p->font();
+    fnt.setUnderline( false );
+    p->setFont( fnt );
+  }
+  QTabBar::paintLabel( p, br, t, has_focus );
+}
+*/
+
+/*!
+  \fn void QtxWorkstackTabBar::dragActiveTab()
+  \brief Emitted when dragging operation is started.
+*/
+
+/*!
+  \fn void QtxWorkstackTabBar::contextMenuRequested( QPoint p )
+  \brief Emitted when context popup menu is requested.
+  \param p point popup menu to be shown at
+*/
+
+/*!
+  \class QtxWorkstack
+  \brief Workstack widget.
+
+  Organizes the child widgets in the tabbed space.
+  Allows splitting the working area to arrange the child widgets in
+  arbitrary way. Any widgets can be moved to another working area with
+  drag-n-drop operation.
+
+  This widget can be used as workspace of the application main window,
+  for example, as kind of implementation of multi-document interface.
+*/
+
+/*!
+  \brief Constructor.
+  \param parent parent widget
+*/
+QtxWorkstack::QtxWorkstack( QWidget* parent )
+: QWidget( parent ),
+  myWin( 0 ),
+  myArea( 0 ),
+  myWorkWin( 0 ),
+  myWorkArea( 0 )
+{
+  myActionsMap.insert( SplitVertical,   new QtxAction( QString(), tr( "Split vertically" ), 0, this ) );
+  myActionsMap.insert( SplitHorizontal, new QtxAction( QString(), tr( "Split horizontally" ), 0, this ) );
+  myActionsMap.insert( Close,           new QtxAction( QString(), tr( "Close" ), 0, this ) );
+  myActionsMap.insert( Rename,          new QtxAction( QString(), tr( "Rename" ), 0, this ) );
+
+  connect( myActionsMap[SplitVertical], SIGNAL( triggered( bool ) ), this, SLOT( splitVertical() ) );
+  connect( myActionsMap[SplitHorizontal], SIGNAL( triggered( bool ) ), this, SLOT( splitHorizontal() ) );
+  connect( myActionsMap[Close], SIGNAL( triggered( bool ) ), this, SLOT( onCloseWindow() ) );
+  connect( myActionsMap[Rename], SIGNAL( triggered( bool ) ), this, SLOT( onRename() ) );
+
+  // Action shortcut will work when action added in any widget.
+  for ( QMap<int, QAction*>::iterator it = myActionsMap.begin(); it != myActionsMap.end(); ++it )
+  {
+    addAction( it.value() );
+    it.value()->setShortcutContext( Qt::ApplicationShortcut );
+  }
+
+  QVBoxLayout* base = new QVBoxLayout( this );
+  base->setMargin( 0 );
+
+  mySplit = new QtxWorkstackSplitter( this );
+  base->addWidget( mySplit );
+}
+
+/*!
+  \brief Destructor.
+*/
+QtxWorkstack::~QtxWorkstack()
+{
+}
+
+/*!
+  \brief Get list of all widgets in all areas or in specified area which given
+         widget belongs to
+  \param wid widget specifying area if it is equal to null when widgets of all
+         areas are retuned
+  \return list of widgets
+*/
+QWidgetList QtxWorkstack::windowList( QWidget* wid ) const
+{
+  QList<QtxWorkstackArea*> lst;
+  if ( !wid )
+  {
+    areas( mySplit, lst, true );
+  }
+  else
+  {
+    QtxWorkstackArea* area = wgArea( wid );
+    if ( area )
+      lst.append( area );
+  }
+
+  QWidgetList widList;
+  for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end(); ++it )
+  {
+    QWidgetList wids = (*it)->widgetList();
+    for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
+      widList.append( *itr );
+  }
+
+  return widList;
+}
+
+/*!
+  \brief Get all child widgets in the active workarea.
+  \return list of widgets in active workarea
+*/
+QWidgetList QtxWorkstack::splitWindowList() const
+{
+  return myArea ? myArea->widgetList() : QWidgetList();
+}
+
+/*!
+  \brief Get active widget.
+  \return active widget
+*/
+QWidget* QtxWorkstack::activeWindow() const
+{
+  return myWin;
+}
+
+/*!
+  \brief Set active widget
+  \param wid widget to activate
+*/
+void QtxWorkstack::setActiveWindow( QWidget* wid )
+{
+  if ( activeArea() )
+    activeArea()->setActiveWidget( wid );
+}
+
+/*!
+  \brief Split workstack.
+
+  Splitting is possible only if there are two or more widgets in the workarea.
+  This function splits current workarea to two new ones.
+
+  \param o splitting orientation (Qt::Orientation)
+*/
+void QtxWorkstack::split( const int o )
+{
+  QtxWorkstackArea* area = myWorkArea;
+  if ( !area )
+    area = activeArea();
+  if ( !area )
+    return;
+
+  if ( area->widgetList().count() < 2 )
+    return;
+
+  QWidget* curWid = area->activeWidget();
+  if ( !curWid )
+    return;
+
+  QSplitter* s = splitter( area );
+  QList<QtxWorkstackArea*> areaList;
+  areas( s, areaList );
+
+  QList<QSplitter*> splitList;
+  splitters( s, splitList );
+
+  QSplitter* trg = 0;
+  if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
+    trg = s;
+
+  if ( !trg )
+    trg = wrapSplitter( area );
+
+  if ( !trg )
+    return;
+
+  trg->setOrientation( (Qt::Orientation)o );
+
+  QtxWorkstackArea* newArea = createArea( 0 );
+  trg->insertWidget( trg->indexOf( area ) + 1, newArea );
+
+  area->removeWidget( curWid );
+  newArea->insertWidget( curWid );
+
+  distributeSpace( trg );
+
+  curWid->show();
+  curWid->setFocus();
+}
+
+/*!
+  \brief Split workarea of the given widget on two parts.
+
+  Splitting is possible only if there are two or more widgets in the workarea.
+  This function splits current workarea to two new ones.
+
+  \param wid widget belonging to the workstack
+  \param o splitting orientation type (Qt::Orientation)
+  \param type splitting type (QtxWorkstack::SplitType)
+*/
+void QtxWorkstack::Split( QWidget* wid, const Qt::Orientation o, const SplitType type )
+{
+  if (!wid) return;
+
+  // find area of the given widget
+  QtxWorkstackArea* area = NULL;
+  QList<QtxWorkstackArea*> allAreas;
+  areas(mySplit, allAreas, true);
+
+  for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
+  {
+    if ( (*it)->contains( wid ) )
+      area = *it;
+  }
+
+  if ( !area )
+    return;
+
+  QWidget* curWid = area->activeWidget();
+  if ( !curWid )
+    return;
+
+  QWidgetList wids = area->widgetList();
+  if ( wids.count() < 2 )
+    return;
+
+  QSplitter* s = splitter( area );
+  QList<QtxWorkstackArea*> areaList;
+  areas( s, areaList );
+
+  QList<QSplitter*> splitList;
+  splitters(s, splitList);
+
+  QSplitter* trg = 0;
+  if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
+    trg = s;
+
+  if (!trg) trg = wrapSplitter(area);
+  if (!trg) return;
+
+  trg->setOrientation(o);
+
+  QtxWorkstackArea* newArea = createArea(0);
+  insertWidget(newArea, trg, area);
+
+  switch ( type )
+  {
+  case SplitStay:
+    for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
+    {
+      QWidget* wid_i = *itr;
+      if ( wid_i != wid )
+      {
+        area->removeWidget( wid_i );
+        newArea->insertWidget( wid_i );
+        wid_i->showMaximized();
+      }
+    }
+    break;
+  case SplitAt:
+    {
+      QWidgetList::iterator itr = wids.begin();
+      for ( ; itr != wids.end() && *itr != wid; ++itr )
+      {
+      }
+      for ( ; itr != wids.end(); ++itr )
+      {
+        area->removeWidget( *itr );
+        newArea->insertWidget( *itr );
+        (*itr)->showMaximized();
+      }
+    }
+    break;
+  case SplitMove:
+    area->removeWidget( wid );
+    newArea->insertWidget( wid );
+    wid->showMaximized();
+    break;
+  }
+
+  distributeSpace( trg );
+
+  curWid->show();
+  curWid->setFocus();
+}
+
+/*!
+ \brief Move widget(s) from the source workarea into the target workarea
+        or reorder widgets inside one workarea.
+
+ Move \a wid2 in target workarea. Put it right after \a wid1.
+ If \a all parameter is \c true, all widgets from source workarea
+ will be moved including \a wid2 and source workarea will be deleted then.
+
+ If \a wid1 and \a wid2 belongs to one workarea, widgets will be just reordered
+ in that workarea.
+
+ \param wid1 widget from target workarea
+ \param wid2 widget from source workarea
+ \param all  if \c true, all widgets from source workarea will
+             be moved into the target one, else only the \a wid2 will be moved
+*/
+void QtxWorkstack::Attract( QWidget* wid1, QWidget* wid2, const bool all )
+{
+  if ( !wid1 || !wid2 )
+    return;
+
+  // find area of the widgets
+  QtxWorkstackArea *area1 = 0, *area2 = 0;
+  QList<QtxWorkstackArea*> allAreas;
+  areas( mySplit, allAreas, true );
+  for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !( area1 && area2 ); ++it )
+  {
+    if ( (*it)->contains( wid1 ) )
+      area1 = *it;
+
+    if ( (*it)->contains( wid2 ) )
+      area2 = *it;
+  }
+
+  if ( !area1 || !area2 )
+    return;
+
+  QSplitter* s1 = splitter( area1 );
+
+  QWidget* curWid = area1->activeWidget();
+  if ( !curWid )
+    return;
+
+  if ( area1 == area2 )
+  {
+    if ( all )
+    {
+      // Set wid1 at first position, wid2 at second
+      area1->insertWidget( wid1 );
+      area1->insertWidget( wid2, 1 );
+      wid1->showMaximized();
+      wid2->showMaximized();
+    }
+    else
+    {
+      // Set wid2 right after wid1
+      area1->removeWidget( wid2 );
+      int wid1_ind = 0;
+      QWidgetList wids1 = area1->widgetList();
+      for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
+      area1->insertWidget( wid2, wid1_ind + 1 );
+      wid2->showMaximized();
+    }
+  }
+  else
+  {
+    int wid1_ind = 0;
+    QWidgetList wids1 = area1->widgetList();
+    for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
+    if ( all )
+    {
+      // Set wid2 right after wid1, other widgets from area2 right after wid2
+      QWidgetList wids2 = area2->widgetList();
+      QWidgetList::iterator itr2 = wids2.begin();
+      for ( int ind = wid1_ind + 1; itr2 != wids2.end(); ++itr2, ++ind )
+      {
+        area2->removeWidget( *itr2 );
+        if ( *itr2 == wid2 )
+          area1->insertWidget( *itr2, wid1_ind + 1 );
+        else
+          area1->insertWidget( *itr2, ind );
+        (*itr2)->showMaximized();
+      }
+    }
+    else
+    {
+      // Set wid2 right after wid1
+      area2->removeWidget( wid2 );
+      area1->insertWidget( wid2, wid1_ind + 1 );
+      wid2->showMaximized();
+    }
+  }
+
+  distributeSpace( s1 );
+
+  area1->setActiveWidget( curWid );
+
+  wid2->show();
+  wid1->setFocus();
+  curWid->show();
+  curWid->setFocus();
+}
+
+/*!
+  \brief Calculate sizes of the splitter widget for the workarea.
+  \internal
+*/
+static void setSizes (QIntList& szList, const int item_ind,
+                      const int new_near, const int new_this, const int new_farr)
+{
+  // set size to all items before an item # <item_ind>
+  int cur_pos = 0;
+  QIntList::iterator its = szList.begin();
+  for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
+    *its = new_near;
+  }
+  if (its == szList.end()) return;
+  // set size to item # <item_ind>
+  *its = new_this;
+  ++its;
+  // set size to all items after an item # <item_ind>
+  for (; its != szList.end(); ++its) {
+    *its = new_farr;
+  }
+}
+
+/*!
+  \brief Set position of the widget relatively to its parent splitter.
+
+  Orientation of positioning will correspond to the splitter orientation.
+
+  \param wid widget
+  \param pos position relatively to the splitter; value in the range [0..1]
+*/
+void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
+{
+  if ( position < 0.0 || 1.0 < position)
+    return;
+
+  if ( !wid )
+    return;
+
+  // find area of the given widget
+  QtxWorkstackArea* area = NULL;
+  QList<QtxWorkstackArea*> allAreas;
+  areas( mySplit, allAreas, true );
+  for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
+  {
+    if ( (*it)->contains( wid ) )
+      area = *it;
+  }
+
+  if ( !area )
+    return;
+
+  QSplitter* split = splitter( area );
+  if ( !split )
+    return;
+
+  // find index of the area in its splitter
+  int item_ind = -1;
+  bool isFound = false;
+  const QObjectList& was = split->children();
+  for ( QObjectList::const_iterator ito = was.begin(); ito != was.end() && !isFound; ++ito, ++item_ind )
+  {
+    if ( *ito == area )
+      isFound = true;
+  }
+
+  if ( !isFound || item_ind == 0 )
+    return;
+
+  QIntList szList = split->sizes();
+  int splitter_size = ( split->orientation() == Qt::Horizontal ? split->width() : split->height());
+  int nb = szList.count();
+
+  int new_prev = int( splitter_size * position / item_ind );
+  if (nb == item_ind) return;
+  int new_next = int( splitter_size * ( 1.0 - position ) / ( nb - item_ind ) );
+  setSizes( szList, item_ind, new_prev, new_next, new_next );
+  split->setSizes( szList );
+}
+
+/*!
+  \brief Set position of the widget relatively to the entire workstack.
+
+  If \a o is \c Qt::Horizontal, the horizontal position of \a wid will be changed.
+  If \a o is \c Qt::Vertical, the vertical position of \a wid will be changed.
+
+  \param wid widget
+  \param o   orientation of positioning (\c Qt::Horizontal or \c Qt::Vertical)
+  \param pos position relatively to the workstack; value in range [0..1]
+*/
+void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
+                                        const double position )
+{
+  if ( position < 0.0 || 1.0 < position )
+    return;
+
+  if ( !wid )
+    return;
+
+  int splitter_size = o == Qt::Horizontal ? mySplit->width() : mySplit->height();
+  int need_pos = int( position * splitter_size );
+  int splitter_pos = 0;
+
+  if ( setPosition( wid, mySplit, o, need_pos, splitter_pos ) != 0 )
+  {
+    // impossible to set required position
+  }
+}
+
+/*!
+  \brief Set accelerator key-combination for the action with specified \a id.
+  \param id action ID
+  \param accel action accelerator
+*/
+void QtxWorkstack::setAccel( const int id, const int accel )
+{
+  if ( !myActionsMap.contains( id ) )
+    return;
+
+  myActionsMap[id]->setShortcut( accel );
+}
+
+/*!
+  \brief Get the action's accelerator key-combination.
+  \param id action ID
+  \return action accelerator
+*/
+int QtxWorkstack::accel( const int id ) const
+{
+  int res = 0;
+  if ( myActionsMap.contains( id ) )
+    res = myActionsMap[id]->shortcut()[0];
+  return res;
+}
+
+/*!
+  \brief Get icon for the specified action.
+
+  If \a id is invalid, null icon is returned.
+
+  \param id menu action ID
+  \return menu item icon
+*/
+QIcon QtxWorkstack::icon( const int id ) const
+{
+  QIcon ico;
+  if ( myActionsMap.contains( id ) )
+    ico = myActionsMap[id]->icon();
+  return ico;
+}
+
+/*!
+  \brief Set menu item icon for the specified action.
+  \param id menu action ID
+  \param ico new menu item icon
+*/
+void QtxWorkstack::setIcon( const int id, const QIcon& icon )
+{
+  if ( !myActionsMap.contains( id ) )
+    return;
+
+  myActionsMap[id]->setIcon( icon );
+}
+
+/*!
+  \brief Set actions to be visible in the context popup menu.
+
+  Actions, which IDs are set in \a flags parameter, will be shown in the
+  context popup menu. Other actions will not be shown.
+
+  \param flags ORed together actions flags
+*/
+void QtxWorkstack::setMenuActions( const int flags )
+{
+  myActionsMap[SplitVertical]->setVisible( flags & SplitVertical );
+  myActionsMap[SplitHorizontal]->setVisible( flags & SplitHorizontal );
+  myActionsMap[Close]->setVisible( flags & Close );
+  myActionsMap[Rename]->setVisible( flags & Rename );
+}
+
+/*!
+  \brief Set actions to be visible in the context popup menu.
+
+  Actions, which IDs are set in \a flags parameter, will be shown in the
+  context popup menu. Other actions will not be shown.
+
+  \param flags ORed together actions flags
+*/
+int QtxWorkstack::menuActions() const
+{
+  int ret = 0;
+  ret = ret | ( myActionsMap[SplitVertical]->isVisible() ? SplitVertical : 0 );
+  ret = ret | ( myActionsMap[SplitHorizontal]->isVisible() ? SplitHorizontal : 0 );
+  ret = ret | ( myActionsMap[Close]->isVisible() ? Close : 0 );
+  ret = ret | ( myActionsMap[Rename]->isVisible() ? Rename : 0 );
+  return ret;
+}
+
+/*!
+  \brief Calculate sizes of the splitter widget for the workarea.
+  \internal
+*/
+static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
+                           const int item_ind, const int item_rel_pos,
+                           const int need_pos, const int splitter_pos)
+{
+  if (item_ind == 0) { // cannot move in this splitter
+    return (need_pos - splitter_pos);
+  }
+
+  int delta = 0;
+  int new_prev = 0;
+  int new_this = szList[item_ind];
+  int new_next = 0;
+
+  if (need_pos < splitter_pos) {
+    // Set size of all previous workareas to zero <--
+    if (item_ind == nb - 1) {
+      // item iz last in the splitter, it will occupy all the splitter
+      new_this = splitter_size;
+    } else {
+      // recompute size of next items in splitter
+      new_next = (splitter_size - new_this) / (nb - item_ind - 1);
+    }
+    delta = need_pos - splitter_pos;
+
+  } else if (need_pos > (splitter_pos + splitter_size)) {
+    // Set size of all next workareas to zero -->
+    // recompute size of previous items in splitter
+    new_this = 0;
+    new_prev = (splitter_size - new_this) / item_ind;
+    delta = need_pos - (splitter_pos + splitter_size - new_this);
+
+  } else { // required position lays inside this splitter
+    // Move workarea inside splitter into required position <->
+    int new_item_rel_pos = need_pos - splitter_pos;
+    new_prev = new_item_rel_pos / item_ind;
+    if (need_pos < (splitter_pos + item_rel_pos)) {
+      // Make previous workareas smaller, next - bigger
+      // No problem to keep old size of the widget
+    } else {
+      // Make previous workareas bigger, next - smaller
+      if (new_this > splitter_size - new_item_rel_pos) {
+        new_this = splitter_size - new_item_rel_pos;
+      }
+      // jfa to do: in this case fixed size of next widgets could prevent right resizing
+    }
+    if (item_ind == nb - 1) {
+      new_this = splitter_size - new_item_rel_pos;
+    } else {
+      new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
+    }
+    delta = 0;
+  }
+
+  setSizes (szList, item_ind, new_prev, new_this, new_next);
+  return delta;
+}
+
+/*!
+  \brief Set position of the widget.
+
+  Called from SetRelativePosition() public method.
+
+  \param wid   widget to be moved
+  \param split currently processed splitter (goes from more common
+               to more particular splitter in recursion calls)
+  \param o     orientation of positioning
+  \param need_pos required position of the given widget in pixels
+               (from top/left side of workstack area)
+  \param splitter_pos position of the splitter \a split
+               (from top/left side of workstack area)
+  \return difference between a required and a distinguished position
+*/
+int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
+                               const int need_pos, const int splitter_pos )
+{
+  if ( !wid || !split )
+    return need_pos - splitter_pos;
+
+  // Find corresponding sub-splitter.
+  // Find also index of appropriate item in current splitter.
+  int cur_ind = 0, item_ind = 0;
+  bool isBottom = false, isFound = false;
+  QSplitter* sub_split = NULL;
+  const QObjectList& objs = split->children();
+  for ( QObjectList::const_iterator it = objs.begin(); it != objs.end() && !isFound; ++it )
+  {
+    QtxWorkstackArea* area = ::qobject_cast<QtxWorkstackArea*>( *it );
+    if ( area )
+    {
+      if ( area->contains( wid ) )
+      {
+        item_ind = cur_ind;
+        isBottom = true;
+        isFound = true;
+      }
+      cur_ind++;
+    }
+    else if ( (*it)->inherits( "QSplitter" ) )
+    {
+      QList<QtxWorkstackArea*> areaList;
+      areas( (QSplitter*)(*it), areaList, true );
+      for ( QList<QtxWorkstackArea*>::iterator ita = areaList.begin(); ita != areaList.end() && !isFound; ++ita )
+      {
+        if ( (*ita)->contains( wid ) )
+        {
+          item_ind = cur_ind;
+          isFound = true;
+          sub_split = (QSplitter*)*it;
+        }
+      }
+      cur_ind++;
+    }
+  }
+
+  if ( !isFound )
+    return ( need_pos - splitter_pos );
+
+  if ( split->orientation() == o )
+  {
+    // Find coordinates of near and far sides of the appropriate item relatively current splitter
+    int splitter_size = ( o == Qt::Horizontal ? split->width() : split->height() );
+    QIntList szList = split->sizes();
+    int nb = szList.count();
+    int item_rel_pos = 0; // position of near side of item relatively this splitter
+    for (int i = 0; i < item_ind; i++) {
+      item_rel_pos += szList[i];
+    }
+    int item_size = szList[item_ind]; // size of item
+    int item_pos = splitter_pos + item_rel_pos;
+
+    // Resize splitter items to complete the conditions
+    if (isBottom) {
+      // I. Bottom of splitters stack reached
+
+      int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
+      split->setSizes(szList);
+      // Recompute delta, as some windows can reject given size
+      int new_item_rel_pos = 0;
+      QIntList szList1 = split->sizes();
+      for (int i = 0; i < item_ind; i++) {
+        new_item_rel_pos += szList1[i];
+      }
+      delta = need_pos - (splitter_pos + new_item_rel_pos);
+      return delta;
+
+    } else {
+      // II. Bottom of splitters stack is not yet reached
+
+      if (item_ind == 0) { // cannot move in this splitter
+        // Process in sub-splitter
+        return setPosition(wid, sub_split, o, need_pos, splitter_pos);
+      }
+
+      int new_prev = 0;
+      int new_this = szList[item_ind];
+      int new_next = 0;
+
+      if (need_pos < splitter_pos) {
+        // Set size of all previous workareas to zero <--
+        if (item_ind == nb - 1) {
+          new_this = splitter_size;
+        } else {
+          new_next = (splitter_size - new_this) / (nb - item_ind - 1);
+        }
+        setSizes (szList, item_ind, new_prev, new_this, new_next);
+        split->setSizes(szList);
+        // Recompute splitter_pos, as some windows can reject given size
+        int new_item_rel_pos = 0;
+        QIntList szList1 = split->sizes();
+        for (int i = 0; i < item_ind; i++) {
+          new_item_rel_pos += szList1[i];
+        }
+        // Process in sub-splitter
+        return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
+      } else if (need_pos > (splitter_pos + splitter_size)) {
+        // Set size of all next workareas to zero -->
+        new_prev = (splitter_size - new_this) / item_ind;
+        setSizes (szList, item_ind, new_prev, new_this, new_next);
+        split->setSizes(szList);
+        // Recompute splitter_pos, as some windows can reject given size
+        int new_item_rel_pos = 0;
+        QIntList szList1 = split->sizes();
+        for (int i = 0; i < item_ind; i++) {
+          new_item_rel_pos += szList1[i];
+        }
+        // Process in sub-splitter
+        return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
+      } else {
+        // Set appropriate size of all previous/next items <->
+        int new_item_rel_pos = item_rel_pos;
+        if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
+          // Move item inside splitter into required position <->
+          int new_this = szList[item_ind];
+          int new_next = 0;
+          new_item_rel_pos = need_pos - splitter_pos;
+          if ((item_pos + item_size) < need_pos) {
+            //new_item_rel_pos = need_pos - (item_pos + item_size);
+            new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
+          }
+          int new_prev = new_item_rel_pos / item_ind;
+          if (need_pos < (splitter_pos + item_rel_pos)) {
+            // Make previous workareas smaller, next - bigger
+            // No problem to keep old size of the widget
+          } else {
+            // Make previous workareas bigger, next - smaller
+            if (new_this > splitter_size - new_item_rel_pos) {
+              new_this = splitter_size - new_item_rel_pos;
+            }
+          }
+          if (item_ind == nb - 1) {
+            new_this = splitter_size - new_item_rel_pos;
+          } else {
+            new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
+          }
+          setSizes (szList, item_ind, new_prev, new_this, new_next);
+          split->setSizes(szList);
+          // Recompute new_item_rel_pos, as some windows can reject given size
+          new_item_rel_pos = 0;
+          QIntList szList1 = split->sizes();
+          for (int i = 0; i < item_ind; i++) {
+            new_item_rel_pos += szList1[i];
+          }
+        } else {
+          // Do nothing
+        }
+        // Process in sub-splitter
+        int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
+        if (add_pos == 0)
+          return 0;
+
+        // this can be if corresponding workarea is first in sub-splitter
+        // or sub-splitter has another orientation
+
+        // Resize ones again to reach precize position <->
+        int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
+
+        // Move workarea inside splitter into required position <->
+        int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
+                                     new_item_rel_pos, need_pos_1, splitter_pos);
+        split->setSizes(szList);
+        // Recompute new_item_rel_pos, as some windows can reject given size
+        new_item_rel_pos = 0;
+        QIntList szList1 = split->sizes();
+        for (int i = 0; i < item_ind; i++) {
+          new_item_rel_pos += szList1[i];
+        }
+        delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
+        return delta_1;
+      }
+    }
+  } else {
+    return setPosition(wid, sub_split, o, need_pos, splitter_pos);
+  }
+
+  return 0;
+}
+
+/*!
+  \brief Redistribute space among widgets equally.
+  \param split splitter
+*/
+void QtxWorkstack::distributeSpace( QSplitter* split ) const
+{
+  if ( !split )
+    return;
+
+  QIntList szList = split->sizes();
+  int size = ( split->orientation() == Qt::Horizontal ?
+               split->width() : split->height() ) / szList.count();
+  for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
+    *it = size;
+  split->setSizes( szList );
+}
+
+/*!
+  \brief Split widgets vertically.
+*/
+void QtxWorkstack::splitVertical()
+{
+  split( Qt::Horizontal );
+}
+
+/*!
+  \brief Split widgets horizontally.
+*/
+void QtxWorkstack::splitHorizontal()
+{
+  split( Qt::Vertical );
+}
+
+/*!
+  \brief Called when user activates "Rename" menu item.
+
+  Changes widget title.
+*/
+void QtxWorkstack::onRename()
 {
-  QtxWorkstackChild* c = child( activeWidget() );
-  if ( !c )
+  if ( !myWorkWin )
     return;
 
-  new QtxWorkstackDrag( workstack(), c );
+  bool ok = false;
+  QString newName = QInputDialog::getText( topLevelWidget(),  tr( "Rename" ), tr( "Enter new name:" ),
+                                           QLineEdit::Normal, myWorkWin->windowTitle(), &ok );
+  if ( ok && !newName.isEmpty() )
+    myWorkWin->setWindowTitle( newName );
 }
 
-void QtxWorkstackArea::onChildDestroyed( QObject* obj )
+/*!
+  \brief Wrap area into the new splitter.
+  \param workarea
+  \return new splitter
+*/
+QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
 {
-  QtxWorkstackChild* child = (QtxWorkstackChild*)obj;
-  myStack->removeWidget( child );
+  if ( !area )
+    return 0;
 
-  QWidget* wid = 0;
-  for ( ChildMap::ConstIterator it = myChild.begin(); it != myChild.end() && !wid; ++it )
-  {
-    if ( it.data() == child )
-      wid = it.key();
-  }
+  QSplitter* pSplit = splitter( area );
+  if ( !pSplit )
+    return 0;
 
-  myChild.remove( wid );
+  bool upd = pSplit->updatesEnabled();
+  pSplit->setUpdatesEnabled( false );
 
-  QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)RemoveWidget, wid ) );
-}
+  QIntList szList = pSplit->sizes();
 
-void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
-{
-  setWidgetShown( c->widget(), true );
-}
+  QSplitter* wrap = new QtxWorkstackSplitter( 0 );
+  pSplit->insertWidget( pSplit->indexOf( area ) + 1, wrap );
+  wrap->setVisible( true );
+  wrap->addWidget( area );
 
-void QtxWorkstackArea::onChildHided( QtxWorkstackChild* c )
-{
-  setWidgetShown( c->widget(), false );
-}
+  pSplit->setSizes( szList );
 
-void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
-{
-  setWidgetActive( c->widget() );
-}
+  pSplit->setUpdatesEnabled( upd );
 
-void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
-{
-  updateTab( c->widget() );
+  return wrap;
 }
 
-void QtxWorkstackArea::updateCurrent()
+/*!
+  \brief Reparent and add widget.
+  \param wid widget
+  \param pWid parent widget
+  \param after widget after which \a wid should be added
+*/
+void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
 {
+  if ( !wid || !pWid )
+    return;
+
+  QWidgetList moveList;
+  const QObjectList& lst = pWid->children();
+  bool found = false;
+  for ( QObjectList::const_iterator it = lst.begin(); it != lst.end(); ++it )
+  {
+    if ( found && ( (*it)->inherits( "QSplitter" ) ||
+                    (*it)->inherits( "QtxWorkstackArea" ) ) )
+      moveList.append( (QWidget*)(*it) );
+    if ( *it == after )
+      found = true;
+  }
+
   QMap<QWidget*, bool> map;
-  for ( QWidgetListIt it( myList ); it.current(); ++it )
+  for ( QWidgetList::iterator it = moveList.begin(); it != moveList.end(); ++it )
   {
-    map.insert( it.current(), isBlocked( it.current() ) );
-    setBlocked( it.current(), true );
+    map.insert( *it, (*it)->isVisibleTo( (*it)->parentWidget() ) );
+    (*it)->setParent( 0 );
+    (*it)->hide();
   }
 
-  myStack->raiseWidget( myBar->currentTab() );
+  wid->setParent( pWid );
 
-  for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
-    setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
+  for ( QWidgetList::iterator itr = moveList.begin(); itr != moveList.end(); ++itr )
+  {
+    (*itr)->setParent( pWid );
+    (*itr)->setVisible( map.contains( *itr ) ? map[*itr] : false );
+  }
 }
 
-void QtxWorkstackArea::updateTab( QWidget* wid )
+/*!
+  \brief Close active window.
+*/
+void QtxWorkstack::onCloseWindow()
 {
-  QTab* tab = myBar->tab( widgetId( wid ) );
-  if ( !tab )
-    return;
+  if ( myWorkWin )
+    myWorkWin->close();
+  else if( activeWindow() )
+    activeWindow()->close();
+}
 
-  QIconSet icoSet;
-  if ( wid->icon() )
-    icoSet = QIconSet( *wid->icon() );
+/*!
+  \brief Called when workarea is destroyed.
 
-  tab->setIconSet( icoSet );
-  tab->setText( wid->caption() );
-}
+  Set input focus to the neighbour area.
 
-QWidget* QtxWorkstackArea::widget( const int id ) const
+  \param obj workarea being destroyed
+*/
+void QtxWorkstack::onDestroyed( QObject* obj )
 {
-  QWidget* wid = 0;
-  for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !wid; ++it )
+  QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
+
+  if ( area == myArea )
+    myArea = 0;
+
+  if ( !myArea )
   {
-    if ( it.data().id == id )
-      wid = it.key();
+    QtxWorkstackArea* cur = neighbourArea( area );
+    if ( cur )
+      cur->setFocus();
   }
-  return wid;
-}
-
-int QtxWorkstackArea::widgetId( QWidget* wid ) const
-{
-  int id = -1;
-  if ( myInfo.contains( wid ) )
-    id = myInfo[wid].id;
-  return id;
-}
 
-bool QtxWorkstackArea::widgetVisibility( QWidget* wid ) const
-{
-  bool res = false;
-  if ( myInfo.contains( wid ) )
-    res = myInfo[wid].vis;
-  return res;
+  QApplication::postEvent( this, new QEvent( QEvent::User ) );
 }
 
-void QtxWorkstackArea::setWidgetActive( QWidget* wid )
+/*!
+  \brief Called on window activating.
+  \param area workarea being activated (not used)
+*/
+void QtxWorkstack::onWindowActivated( QWidget* /*area*/ )
 {
-  int id = widgetId( wid );
-  if ( id < 0 )
+  const QObject* obj = sender();
+  if ( !obj->inherits( "QtxWorkstackArea" ) )
     return;
 
-  myBar->setCurrentTab( id );
+  setActiveArea( (QtxWorkstackArea*)obj );
 }
 
-void QtxWorkstackArea::setWidgetShown( QWidget* wid, const bool on )
+/*!
+  \brief Called on window deactivating.
+  \param area workarea being deactivated
+*/
+void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
 {
-  if ( isBlocked( wid ) || !myInfo.contains( wid ) || myInfo[wid].vis == on )
+  if ( myArea != area )
     return;
 
-  myInfo[wid].vis = on;
-  updateState();
-}
-
-void QtxWorkstackArea::updateState()
-{
-  bool updBar = myBar->isUpdatesEnabled();
-  bool updStk = myStack->isUpdatesEnabled();
-  myBar->setUpdatesEnabled( false );
-  myStack->setUpdatesEnabled( false );
-
-  bool block = myBar->signalsBlocked();
-  myBar->blockSignals( true );
-
-  QWidget* prev = activeWidget();
-
-  int idx = 0;
-  for ( QWidgetListIt it( myList ); it.current(); ++it )
-  {
-    QWidget* wid = it.current();
-    int id = widgetId( wid );
+  QList<QtxWorkstackArea*> lst;
+  areas( mySplit, lst, true );
 
-    if ( id < 0 )
-      continue;
+  int idx = lst.indexOf( area );
+  if ( idx == -1 )
+    return;
 
-    bool vis = widgetVisibility( wid );
+  myWin = 0;
+  myArea = 0;
 
-    if ( myBar->tab( id ) && ( !vis || myBar->indexOf( id ) != idx ) )
-      myBar->removeTab( myBar->tab( id ) );
+  QtxWorkstackArea* newArea = neighbourArea( area );
+  if ( newArea && newArea->activeWidget() )
+    newArea->activeWidget()->setFocus();
 
-    if ( !myBar->tab( id ) && vis )
-    {
-      QTab* tab = new QTab( wid->caption() );
-      myBar->insertTab( tab, idx );
-      tab->setIdentifier( id );
-    }
+  QApplication::postEvent( this, new QEvent( QEvent::User ) );
+}
 
-    updateTab( wid );
+/*!
+  \brief Create and show popup menu for workarea.
+  \param w workarea
+  \param p popup position
+*/
+void QtxWorkstack::onContextMenuRequested( QWidget* w, QPoint p )
+{
+  QtxWorkstackArea* anArea = ::qobject_cast<QtxWorkstackArea*>( (QObject*)sender() );
+  if ( !anArea )
+    anArea = activeArea();
 
-    bool block = isBlocked( wid );
-    setBlocked( wid, true );
+  if ( !anArea )
+    return;
 
-    QtxWorkstackChild* cont = child( wid );
+  QWidgetList lst = anArea->widgetList();
+  if ( lst.isEmpty() )
+    return;
 
-    if ( !vis )
-      myStack->removeWidget( cont );
-    else if ( !myStack->widget( id ) )
-      myStack->addWidget( cont, id );
+  myWorkWin = w;
+  myWorkArea = anArea;
 
-    if ( vis )
-      idx++;
+  QMenu* pm = new QMenu();
 
-    setBlocked( wid, block );
+  if ( lst.count() > 1 )
+  {
+    if ( !myActionsMap[SplitVertical]->isEnabled() )
+      myActionsMap[SplitVertical]->setEnabled(true);
+    pm->addAction( myActionsMap[SplitVertical] );
+    if ( !myActionsMap[SplitHorizontal]->isEnabled() )
+      myActionsMap[SplitHorizontal]->setEnabled(true);
+    pm->addAction( myActionsMap[SplitHorizontal] );
+    pm->addSeparator();
   }
 
-  int curId = widgetId( prev );
-  if ( !myBar->tab( curId ) )
+  if ( w )
   {
-    QWidget* wid = 0;
-    int pos = myList.find( prev );
-    for ( int i = pos - 1; i >= 0 && !wid; i-- )
-    {
-      if ( widgetVisibility( myList.at( i ) ) )
-        wid = myList.at( i );
-    }
-
-    for ( int j = pos + 1; j < (int)myList.count() && !wid; j++ )
-    {
-      if ( widgetVisibility( myList.at( j ) ) )
-        wid = myList.at( j );
-    }
-
-    if ( wid )
-      curId = widgetId( wid );
+    if ( myActionsMap[Close]->isEnabled() )
+      pm->addAction( myActionsMap[Close] );
+    if ( myActionsMap[Rename]->isEnabled() )
+      pm->addAction( myActionsMap[Rename] );
   }
 
-  myBar->setCurrentTab( curId );
+  Qtx::simplifySeparators( pm );
 
-  myBar->blockSignals( block );
+  if ( !pm->actions().isEmpty() )
+    pm->exec( p );
 
-  updateCurrent();
+  delete pm;
 
-  myBar->setUpdatesEnabled( updBar );
-  myStack->setUpdatesEnabled( updStk );
-  if ( updBar )
-    myBar->update();
-  if ( updStk )
-    myStack->update();
+  myWorkWin = 0;
+  myWorkArea = 0;
+}
 
-  QResizeEvent re( myBar->size(), myBar->size() );
-  QApplication::sendEvent( myBar, &re );
+/*!
+  \brief Add child widget.
+  \param w widget
+  \param f widget flags
+  \return child widget container
+*/
+QWidget* QtxWorkstack::addWindow( QWidget* w, Qt::WindowFlags f )
+{
+  if ( !w )
+    return 0;
 
-  if ( isEmpty() )
-  {
-    hide();
-    emit deactivated( this );
-  }
-  else
-  {
-    show();
-    if ( prev != activeWidget() )
-      emit activated( activeWidget() );
-  }
+  return targetArea()->insertWidget( w, -1, f );
 }
 
-int QtxWorkstackArea::generateId() const
+/*!
+  \brief Handle custom events.
+  \param e custom event (not used)
+*/
+void QtxWorkstack::customEvent( QEvent* /*e*/ )
 {
-  QMap<int, int> map;
+  updateState();
+}
+
+/*!
+  \brief Get splitter corresponding to the workarea.
+  \param workarea
+  \return splitter corresponding to the workarea
+*/
+QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
+{
+  if ( !area )
+    return 0;
 
-  for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end(); ++it )
-    map.insert( it.data().id, 0 );
+  QSplitter* split = 0;
 
-  int id = 0;
-  while ( map.contains( id ) )
-    id++;
+  QWidget* wid = area->parentWidget();
+  if ( wid && wid->inherits( "QSplitter" ) )
+    split = (QSplitter*)wid;
 
-  return id;
+  return split;
 }
 
-bool QtxWorkstackArea::isBlocked( QWidget* wid ) const
+/*!
+  \brief Get list of child splitters.
+  \param split parent splitter
+  \param splitList list to be filled with child splitters
+  \param rec if \c true, perform recursive search of children
+*/
+void QtxWorkstack::splitters( QSplitter* split, QList<QSplitter*>& splitList, const bool rec ) const
 {
-  return myBlock.contains( wid );
+  if ( !split )
+    return;
+
+  const QObjectList& objs = split->children();
+  for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
+  {
+    if ( rec )
+      splitters( (QSplitter*)*it, splitList, rec );
+    if ( (*it)->inherits( "QSplitter" ) )
+      splitList.append( (QSplitter*)*it );
+  }
 }
 
-void QtxWorkstackArea::setBlocked( QWidget* wid, const bool on )
+/*!
+  \brief Get list of child workareas.
+  \param split parent splitter
+  \param areaList list to be filled with child workareas
+  \param rec if \c true, perform recursive search of children
+*/
+void QtxWorkstack::areas( QSplitter* split, QList<QtxWorkstackArea*>& areaList, const bool rec ) const
 {
-  if ( on )
-    myBlock.insert( wid, 0 );
-  else
-    myBlock.remove( wid );
+  if ( !split )
+    return;
+
+  const QObjectList& objs = split->children();
+  for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
+  {
+    if ( (*it)->inherits( "QtxWorkstackArea" ) )
+      areaList.append( (QtxWorkstackArea*)*it );
+    else if ( rec && (*it)->inherits( "QSplitter" ) )
+      areas( (QSplitter*)*it, areaList, rec );
+  }
 }
 
-QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
+/*!
+  \brief Get active workarea.
+  \return active workarea
+*/
+QtxWorkstackArea* QtxWorkstack::activeArea() const
 {
-  QtxWorkstackChild* res = 0;
-  if ( myChild.contains( wid ) )
-    res = myChild[wid];
-  return res;
+  return myArea;
 }
 
 /*!
-    Class: QtxWorkstackChild [Internal]
-    Descr:
-*/
+  \brief Get target area (for which the current operation should be done).
+
+  Returns active workarea or current area (if there is no active workarea).
+  If there are no workareas, create new workarea and return it.
 
-QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent )
-: QHBox( parent ),
-myWidget( wid )
+  \return workarea
+*/
+QtxWorkstackArea* QtxWorkstack::targetArea()
 {
-  myWidget->reparent( this, QPoint( 0, 0 ), myWidget->isVisibleTo( myWidget->parentWidget() ) );
-  myWidget->installEventFilter( this );
+  QtxWorkstackArea* area = activeArea();
+  if ( !area )
+    area = currentArea();
+  if ( !area )
+  {
+    QList<QtxWorkstackArea*> lst;
+    areas( mySplit, lst );
+    if ( !lst.isEmpty() )
+      area = lst.first();
+  }
+
+  if ( !area )
+    area = createArea( mySplit );
 
-  connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
+  return area;
 }
 
-QtxWorkstackChild::~QtxWorkstackChild()
-{
-  qApp->removeEventFilter( this );
+/*!
+  \brief Get current workarea.
 
-  if ( !widget() )
-    return;
+  Current workarea is that one which has input focus.
 
-  widget()->removeEventFilter( this );
-  widget()->reparent( 0, QPoint( 0, 0 ), false );
-  disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
+  \return current area
+*/
+QtxWorkstackArea* QtxWorkstack::currentArea() const
+{
+  QtxWorkstackArea* area = 0;
+  QWidget* wid = focusWidget();
+  while ( wid && !area )
+  {
+    if ( wid->inherits( "QtxWorkstackArea" ) )
+      area = (QtxWorkstackArea*)wid;
+    wid = wid->parentWidget();
+  }
+
+  return area;
 }
 
-QWidget* QtxWorkstackChild::widget() const
-{
-  return myWidget;
+/*!
+  \brief Create new workarea.
+  \param parent parent widget
+  \return created workarea
+*/
+QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
+{
+  QtxWorkstackArea* area = new QtxWorkstackArea( parent );
+
+  connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
+  connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
+  connect( area, SIGNAL( contextMenuRequested( QWidget*, QPoint ) ),
+           this, SLOT( onContextMenuRequested( QWidget*, QPoint ) ) );
+  connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
+
+  return area;
 }
 
-bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
+/*!
+  \brief Set active workarea.
+  \param workarea
+*/
+void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
 {
-  if ( o->isWidgetType() )
-  {
-    if ( e->type() == QEvent::CaptionChange || e->type() == QEvent::IconChange )
-      emit captionChanged( this );
+  QWidget* oldCur = myWin;
 
-    if ( !e->spontaneous() && ( e->type() == QEvent::Show || e->type() == QEvent::ShowToParent ) )
-      emit shown( this );
+  QtxWorkstackArea* oldArea = myArea;
 
-    if ( !e->spontaneous() && ( e->type() == QEvent::Hide || e->type() == QEvent::HideToParent ) )
-      emit hided( this );
+  myArea = area;
 
-    if ( e->type() == QEvent::FocusIn )
-      emit activated( this );
+  if ( myArea != oldArea )
+  {
+    if ( oldArea )
+      oldArea->updateActiveState();
+    if ( myArea )
+      myArea->updateActiveState();
   }
-  return QHBox::eventFilter( o, e );
-}
 
-void QtxWorkstackChild::onDestroyed( QObject* obj )
-{
-  if ( obj != widget() )
-    return;
+  if ( myArea )
+    myWin = myArea->activeWidget();
 
-  myWidget = 0;
-  deleteLater();
+  if ( myWin && oldCur != myWin )
+    emit windowActivated( myWin );
 }
 
-void QtxWorkstackChild::childEvent( QChildEvent* e )
+/*!
+  \brief Get workarea which is nearest to \a area.
+  \param area area for which neighbour is searched
+  \return neighbour area (or 0 if not found)
+*/
+QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
 {
-  if ( e->type() == QEvent::ChildRemoved && e->child() == widget() )
+  QList<QtxWorkstackArea*> lst;
+  areas( mySplit, lst, true );
+  int pos = lst.indexOf( area );
+  if ( pos < 0 )
+    return 0;
+
+  QtxWorkstackArea* na = 0;
+  for ( int i = pos - 1; i >= 0 && !na; i-- )
   {
-    myWidget = 0;
-    deleteLater();
+    if ( !lst.at( i )->isEmpty() )
+      na = lst.at( i );
+  }
+
+  for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
+  {
+    if ( !lst.at( j )->isEmpty() )
+        na = lst.at( j );
   }
-  QHBox::childEvent( e );
+  return na;
 }
 
 /*!
-    Class: QtxWorkstackTabBar [Internal]
-    Descr:
+  \brief Get workarea covering point.
+  \return workarea
+  \param p point
 */
-
-QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
-: QTabBar( parent ),
-myId( -1 )
+QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
 {
+  QtxWorkstackArea* area = 0;
+  QList<QtxWorkstackArea*> lst;
+  areas( mySplit, lst, true );
+  for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end() && !area; ++it )
+  {
+    QtxWorkstackArea* cur = *it;
+    QRect r = cur->geometry();
+    if ( cur->parentWidget() )
+      r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
+    if ( r.contains( p ) )
+      area = cur;
+  }
+  return area;
 }
 
-QtxWorkstackTabBar::~QtxWorkstackTabBar()
+/*!
+  \brief Update internal state.
+*/
+void QtxWorkstack::updateState()
 {
+  updateState( mySplit );
 }
 
-void QtxWorkstackTabBar::setActive( const bool on )
+/*!
+  \brief Update splitter state.
+  \param split splitter to be updated
+*/
+void QtxWorkstack::updateState( QSplitter* split )
 {
-  QFont aFont = font();
-  aFont.setUnderline( on );
-  setFont( aFont );
+  QList<QSplitter*> recList;
+  splitters( split, recList, false );
+  for ( QList<QSplitter*>::iterator itr = recList.begin(); itr != recList.end(); ++itr )
+    updateState( *itr );
 
-  update();
-}
+  QList<QSplitter*> splitList;
+  splitters( split, splitList, false );
 
-QRect QtxWorkstackTabBar::tabRect( const int idx ) const
-{
-  QRect r;
-  QTab* t = tabAt( idx );
-  if ( t )
-  {
-    r = t->rect();
-    r.setLeft( QMAX( r.left(), 0 ) );
+  QList<QtxWorkstackArea*> areaList;
+  areas( split, areaList, false );
 
-    int x1 = tabAt( 0 )->rect().left();
-    int x2 = tabAt( count() - 1 )->rect().right();
+  bool vis = false;
+  for ( QList<QtxWorkstackArea*>::iterator it = areaList.begin(); it != areaList.end(); ++it )
+  {
+    if ( (*it)->isEmpty() )
+      (*it)->hide();
+    else
+    {
+      (*it)->show();
+      vis = true;
+    }
+  }
 
-    int bw = 0;
-    if ( QABS( x2 - x1 ) > width() )
-#if defined QT_VERSION && QT_VERSION >= 0x30300
-      bw = 2 * style().pixelMetric( QStyle::PM_TabBarScrollButtonWidth, this );
-#else
-      bw = 2 * 16;
-#endif
+  if ( split == mySplit )
+    return;
 
-    int limit = width() - bw;
-    r.setRight( QMIN( r.right(), limit ) );
+  for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end() && !vis; ++iter )
+    vis = (*iter)->isVisibleTo( (*iter)->parentWidget() );
 
-    r = QRect( mapToGlobal( r.topLeft() ), r.size() );
-  }
-  return r;
+  if ( areaList.isEmpty() && splitList.isEmpty() )
+    delete split;
+  else
+    split->setVisible( vis );
 }
 
-void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
+/*!
+  \brief Dump workstack configuration to the state description array.
+  \param version number
+  \return state byte array.
+*/
+QByteArray QtxWorkstack::saveState( int version ) const
 {
-  if ( myId != -1 && !tab( myId )->rect().contains( e->pos() ) )
-  {
-    myId = -1;
-    emit dragActiveTab();
-  }
-
-  QTabBar::mouseMoveEvent( e );
-}
+  QByteArray data;
 
-void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
-{
-  QTabBar::mousePressEvent( e );
+  QDataStream stream( &data, QIODevice::WriteOnly );
+  stream << QtxWorkstack::VersionMarker;
+  stream << version;
+  saveState( stream );
 
-  if ( e->button() == LeftButton )
-    myId = currentTab();
+  return data;
 }
 
-void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
+/*!
+  \brief Restore workstack configuration from the state description array.
+  \param version number
+  \return restore performing state
+*/
+bool QtxWorkstack::restoreState( const QByteArray& state, int version )
 {
-  QTabBar::mouseReleaseEvent( e );
+  if ( state.isEmpty() )
+    return false;
 
-  myId = -1;
+  QByteArray sd = state;
+  QDataStream stream( &sd, QIODevice::ReadOnly );
+  int marker, ver;
+  stream >> marker;
+  stream >> ver;
+  if ( stream.status() != QDataStream::Ok || marker != QtxWorkstack::VersionMarker || ver != version )
+    return false;
 
-  if ( e->button() == RightButton )
-    emit contextMenuRequested( e->globalPos() );
+  return restoreState( stream );
 }
 
-void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
+void QtxWorkstack::saveState( QDataStream& stream ) const
 {
-  if ( e->reason() != QContextMenuEvent::Mouse )
-    emit contextMenuRequested( e->globalPos() );
+  mySplit->saveState( stream );
 }
 
-void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
+bool QtxWorkstack::restoreState( QDataStream& stream )
 {
-  if ( currentTab() != t->identifier() )
+  QMap<QString, QtxWorkstackChild*> map;
+  QList<QtxWorkstackArea*> areaList;
+  areas( mySplit, areaList, true );
+  for ( QList<QtxWorkstackArea*>::const_iterator it = areaList.begin(); it != areaList.end(); ++it )
   {
-    QFont fnt = p->font();
-    fnt.setUnderline( false );
-    p->setFont( fnt );
-  }
-  QTabBar::paintLabel( p, br, t, has_focus );
-}
+    QtxWorkstackArea* area = *it;
+    QList<QtxWorkstackChild*> childList = area->childList();
+    for ( QList<QtxWorkstackChild*>::iterator itr = childList.begin(); itr != childList.end(); ++itr )
+    {
+      QtxWorkstackChild* c = *itr;
+      if ( !c->widget() )
+        continue;
 
-/*!
-    Class: QtxWorkstackDrag [Internal]
-    Descr:
-*/
+      map.insert( c->widget()->objectName(), c );
 
-QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
-: QObject( 0 ),
-myWS( ws ),
-myTab( -1 ),
-myArea( 0 ),
-myPainter( 0 ),
-myChild( child )
-{
-  qApp->installEventFilter( this );
-}
+      qDebug( "QtxWorkstack::restoreState: found widget \"%s\"", (const char*)c->widget()->objectName().toUtf8() );
+    }
+  }
 
-QtxWorkstackDrag::~QtxWorkstackDrag()
-{
-  qApp->removeEventFilter( this );
+  int marker;
+  stream >> marker;
+  if ( stream.status() != QDataStream::Ok || marker != QtxWorkstack::SplitMarker )
+    return false;
 
-  endDrawRect();
-}
+  QtxWorkstackSplitter* split = new QtxWorkstackSplitter( this );
+  if ( layout() )
+    layout()->addWidget( split );
 
-bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
-{
-  switch ( e->type() )
+  bool ok = split->restoreState( stream, map );
+  if ( !ok )
+    delete split;
+  else
   {
-  case QEvent::MouseMove:
-    updateTarget( ((QMouseEvent*)e)->globalPos() );
-    break;
-  case QEvent::MouseButtonRelease:
-    drawRect();
-    endDrawRect();
-    dropWidget();
-    deleteLater();
-    break;
-  default:
-    return false;
+    mySplit->deleteLater();
+    mySplit = split;
+
+    QList<QtxWorkstackArea*> aList;
+    areas( mySplit, aList, true );
+
+    QtxWorkstackArea* a = !aList.isEmpty() ? aList.first() : 0;
+    for ( QMap<QString, QtxWorkstackChild*>::const_iterator it = map.begin(); it != map.end(); ++it )
+    {
+      QtxWorkstackChild* c = it.value();
+      if ( c->widget() )
+        c->widget()->setVisible( false );
+      if ( a )
+        a->insertChild( c );
+      else
+        c->setVisible( false );
+    }
   }
-  return true;
+
+  return ok;
 }
 
-void QtxWorkstackDrag::updateTarget( const QPoint& p )
+/*!
+  \brief Set resize mode of all splitters opaque or transparent.
+  \param opaque opaque mode
+*/
+void QtxWorkstack::setOpaqueResize( bool opaque )
 {
-  int tab = -1;
-  QtxWorkstackArea* area = detectTarget( p, tab );
-  setTarget( area, tab );
+  QList<QSplitter*> splitList;
+  splitters( mySplit, splitList, true );
+  splitList << mySplit;
+  foreach( QSplitter* split, splitList )
+    split->setOpaqueResize( opaque );
 }
 
-QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
+/*!
+  \brief Get resize mode of all splitters: opaque (\c true) or transparent (\c false).
+  \return current opaque mode
+*/
+bool QtxWorkstack::opaqueResize() const
 {
-  if ( p.isNull() )
-    return 0;
-
-  QtxWorkstackArea* area = myWS->areaAt( p );
-  if ( area )
-    tab = area->tabAt( p );
-  return area;
+  return mySplit->opaqueResize();
 }
 
-void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
+/*!
+  \brief Show/hide splitter state and area.
+  \param wid widget (and parent area) will be shown/hidden
+  \param parent_list parent splitters list
+  \param split splitter will be shown/hidden
+  \param visible splitter
+*/
+void QtxWorkstack::splitterVisible(QWidget* wid, QList<QSplitter*>& parent_list, QSplitter* split, bool visible)
 {
-  if ( !area || ( myArea == area && tab == myTab ) )
-    return;
+  QList<QSplitter*> recList;
+  splitters( split, recList, false );
+  for ( QList<QSplitter*>::iterator itr = recList.begin(); itr != recList.end(); ++itr ) {
+    parent_list.prepend( *itr );
+    splitterVisible( wid, parent_list, *itr, visible );
+  }
 
-  startDrawRect();
+  QList<QtxWorkstackArea*> areaList;
+  areas( split, areaList, false );
+  for ( QList<QtxWorkstackArea*>::const_iterator it = areaList.begin(); it != areaList.end(); ++it ) {
+    QtxWorkstackArea* area = *it;
+    bool isCurrentWidget = false;
+
+    area->showTabBar(visible);
+
+    // 1. Looking for the selected widget at the lowest level among all splitted areas.
+    QList<QtxWorkstackChild*> childList = area->childList();
+    for ( QList<QtxWorkstackChild*>::iterator itr = childList.begin(); itr != childList.end(); ++itr ) {
+      QWidget* aCurWid = (*itr)->widget();
+      if ( aCurWid == wid ) {
+        isCurrentWidget = true;
+        aCurWid->setVisible( true );
+      }
+      else
+        aCurWid->setVisible( visible );
+    }
 
-  if ( myArea )
-    drawRect();
+    // 2. Show/Hide other areas and widgets that don't contain the desired widget
+    if ( !isCurrentWidget || visible )
+      area->setVisible( visible );
 
-  myTab = tab;
-  myArea = area;
+    if ( !isCurrentWidget && !visible )
+      continue;
 
-  if ( myArea )
-    drawRect();
-}
+    // 3. Show/hide all parent widgets
+    QSplitter* pSplit = splitter( area );
+    int count = pSplit->count();
+    for ( int i = 0; i < count; i++ ) {
+      if ( pSplit->indexOf( area ) == i && !visible )
+        continue;
+      pSplit->widget(i)->setVisible( visible );
+    }
 
-void QtxWorkstackDrag::dropWidget()
-{
-  if ( myArea )
-    myArea->insertWidget( myChild->widget(), myTab );
+    // 4. Show/hide all parent splitters don't contain the selected widget
+    if ( visible )
+      pSplit->setVisible( true );
+
+    if ( isCurrentWidget && !visible ) {
+      for ( QList<QSplitter*>::iterator itr = parent_list.begin(); itr != parent_list.end() && pSplit != mySplit; ++itr ) {
+        if ( pSplit == *itr )
+          continue;
+        QList<QSplitter*> splitList;
+        splitters( *itr, splitList, false );
+        for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end(); ++iter ) {
+          if ( pSplit == (*iter) ) {
+            pSplit = *itr;
+            continue;
+          }
+          (*iter)->setVisible( false );
+        }
+      }
+    }
+  }
 }
 
-void QtxWorkstackDrag::drawRect()
+/*!
+  \brief Show/hide splitters state and area.
+  \param wid widget (and parent area) will be shown/hidden
+  \param visible splitters
+*/
+void QtxWorkstack::splittersVisible( QWidget* wid, bool visible )
 {
-  if ( !myPainter || !myArea )
-    return;
+  QList<QSplitter*> parent_list;
+  parent_list.append( mySplit );
+  splitterVisible( wid, parent_list, mySplit, visible );
+}
 
-  QRect r = myArea->floatRect();
-  int m = myPainter->pen().width();
+/*!
+  \fn void QtxWorkstack::windowActivated( QWidget* w )
+  \brief Emitted when the workstack's child widget \w is activated.
+  \param w widget being activated
+*/
 
-  r.setTop( r.top() + m + 2 );
-  r.setLeft( r.left() + m + 2 );
-  r.setRight( r.right() - m - 2 );
-  r.setBottom( r.bottom() - m - 2 );
+/*!
+  \brief Gets area containing given widget
+  \param wid widget
+  \return pointer to QtxWorkstackArea* object
+*/
+QtxWorkstackArea* QtxWorkstack::wgArea( QWidget* wid ) const
+{
+  QtxWorkstackArea* resArea = 0;
 
-  myPainter->drawRect( r );
+  QList<QtxWorkstackArea*> areaList;
+  areas( mySplit, areaList, true );
 
-  QRect tr = myArea->floatTab( myTab );
-  tr.setTop( tr.top() + m );
-  tr.setLeft( tr.left() + m );
-  tr.setRight( tr.right() - m );
-  tr.setBottom( tr.bottom() - m );
+  QList<QtxWorkstackArea*>::ConstIterator it;
+  for ( it = areaList.begin(); it != areaList.end() && !resArea; ++it )
+  {
+    if ( (*it)->contains( wid ) )
+      resArea = *it;
+  }
 
-  myPainter->drawRect( tr );
+  return resArea;
 }
 
-void QtxWorkstackDrag::endDrawRect()
+/*!
+  \brief Moves the first widget to the same area which the second widget belongs to
+  \param wid widget to be moved
+  \param wid_to widget specified the destination area
+  \param before specifies whether the first widget has to be moved before or after
+         the second widget
+  \return \c true if operation is completed successfully, \c false otherwise
+*/
+bool QtxWorkstack::move( QWidget* wid, QWidget* wid_to, const bool before )
 {
-  delete myPainter;
-  myPainter = 0;
+  if ( wid && wid_to )
+  {
+    QtxWorkstackArea* area_src = wgArea( wid );
+    QtxWorkstackArea* area_to = wgArea( wid_to );
+    if ( area_src && area_to )
+    {
+      // find index of the second widget
+      QWidgetList wgList = area_to->widgetList();
+      QWidgetList::ConstIterator it;
+      int idx = 0;
+      for ( it = wgList.begin(); it != wgList.begin(); ++it, idx++ )
+      {
+        if ( *it == wid_to )
+          break;
+      }
+
+      if ( idx < wgList.count() ) // paranoidal check
+      {
+        if ( !before )
+          idx++;
+        area_src->removeWidget( wid, true );
+        area_to->insertWidget( wid, idx );
+        wid->showMaximized();
+        return true;
+      }
+    }
+  }
+  return false;
 }
 
-void QtxWorkstackDrag::startDrawRect()
+/*!
+  \brief Group all windows in one area
+  \return \c true if operation is completed successfully, \c false otherwise
+*/
+void QtxWorkstack::stack()
 {
-  if ( myPainter )
-    return;
+  QWidgetList wgList = windowList();
+  if ( !wgList.count() )
+    return; // nothing to do
+
+  QtxWorkstackArea* area_to = 0;
+  QWidgetList::ConstIterator it;
+  for ( it = wgList.begin(); it != wgList.end(); ++it )
+  {
+    QtxWorkstackArea* area_src = 0;
+    if ( !area_to )
+    {
+      area_to = wgArea( *it );
+      area_src = area_to;
+    }
+    else
+      area_src = wgArea( *it );
 
-  int scr = QApplication::desktop()->screenNumber( (QWidget*)this );
-  QWidget* paint_on = QApplication::desktop()->screen( scr );
+    if ( area_src != area_to )
+    {
+      area_src->removeWidget( *it, true );
+      area_to->insertWidget( *it, -1 );
+      (*it)->showMaximized();
+    }
+  }
+}
 
-  myPainter = new QPainter( paint_on, true );
-  myPainter->setPen( QPen( gray, 3 ) );
-  myPainter->setRasterOp( XorROP );
+QAction* QtxWorkstack::action( const int id ) const
+{
+  return myActionsMap.contains( id ) ? myActionsMap[id] : 0;
 }