]> SALOME platform Git repositories - modules/gui.git/commitdiff
Salome HOME
Porting to Qt4
authorvsr <vsr@opencascade.com>
Mon, 14 May 2007 11:15:42 +0000 (11:15 +0000)
committervsr <vsr@opencascade.com>
Mon, 14 May 2007 11:15:42 +0000 (11:15 +0000)
src/Qtx/QtxWorkstack.cxx
src/Qtx/QtxWorkstack.h

index 3cf22f41384375b6e1237c40fd8c6f83745555cd..7c6f234d98a105bdafc104c5bd3fa4f894c03971 100644 (file)
@@ -1,17 +1,17 @@
 // Copyright (C) 2005  OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
-// 
+//
 // 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 
+// License as published by the Free Software Foundation; either
 // version 2.1 of the License.
-// 
-// 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 
+//
+// 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 
+// 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
 
 #include "QtxWorkstack.h"
 
-#include <qstyle.h>
-#include <qimage.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 <qinputdialog.h>
-#include <qevent.h>
-#include <qregexp.h>
+#include "QtxAction.h"
 
-#define DARK_COLOR_LIGHT      250
+#include <QRegExp>
+#include <QMenu>
+#include <QFocusEvent>
+#include <QMouseEvent>
+#include <QStyle>
+#include <QLayout>
+#include <QSplitter>
+#include <QRubberBand>
+#include <QPushButton>
+#include <QApplication>
+#include <QInputDialog>
+#include <QStackedWidget>
+
+#define DARK_COLOR_LIGHT 250
 
 /*!
-  Constructor
+  \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 ),
-myWorkWin( 0 ),
-myWorkArea( 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 ) );
-  myActionsMap.insert( Rename,          new QAction( tr( "Rename" ),      0, this ) );
+public:
+  WidgetEvent( Type t, QWidget* w = 0 ) : QEvent( t ), myWidget( 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() ) );
-  connect( myActionsMap[Rename], SIGNAL( activated() ), this, SLOT( onRename() ) );
+  QWidget* widget() const { return myWidget; }
 
-  QVBoxLayout* base = new QVBoxLayout( this );
-  mySplit = new QSplitter( this );
-  mySplit->setChildrenCollapsible( false );
-  base->addWidget( mySplit );
-}
+private:
+  QWidget* myWidget;   // event receiver widget
+};
 
 /*!
-  Destructor
+  \class QtxWorkstackDrag
+  \internal
+  \brief Workstack drag object
 */
-QtxWorkstack::~QtxWorkstack()
+
+class QtxWorkstackDrag : public QObject
+{
+  Q_OBJECT
+
+public:
+  QtxWorkstackDrag( QtxWorkstack*, QtxWorkstackChild* );
+  virtual ~QtxWorkstackDrag();
+
+  virtual bool        eventFilter( QObject*, QEvent* );
+
+private:
+  void                dropWidget();
+
+  void                updateTarget( const QPoint& );
+  QtxWorkstackArea*   detectTarget( const QPoint&, int& ) const;
+  void                setTarget( QtxWorkstackArea*, const int );
+
+  void                drawRect();
+  void                endDrawRect();
+  void                startDrawRect();
+
+private:
+  QtxWorkstack*       myWS;          //!< parent workstack
+  QtxWorkstackChild*  myChild;       //!< workstack child widget container
+
+  int                 myTab;         //!< workarea tab page index
+  QtxWorkstackArea*   myArea;        //!< workarea
+  QRubberBand*        myTabRect;     //!< tab bar rubber band
+  QRubberBand*        myAreaRect;    //!< workarea rubber band
+};
+
+/*!
+  \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 );
 }
 
 /*!
-  \return list of all widgets in all areas
+  \brief Destructor.
 */
-QWidgetList QtxWorkstack::windowList() const
+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;
 }
 
 /*!
-  \return list of all widgets in active area
+  \brief Detect and set dropping target widget.
+  \param p current dragging position
 */
-QWidgetList QtxWorkstack::splitWindowList() const
+void QtxWorkstackDrag::updateTarget( const QPoint& p )
 {
-  return myArea ? myArea->widgetList() : QWidgetList();
+  int tab = -1;
+  QtxWorkstackArea* area = detectTarget( p, tab );
+  setTarget( area, tab );
 }
 
 /*!
-  \return active widget
+  \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
 */
-QWidget* QtxWorkstack::activeWindow() const
+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;
 }
 
 /*!
-  Splits widgets
-  \param o - orientation (Qt::Orientation)
+  \brief Set dropping target.
+  \param area new target workarea
+  \param tab target workarea's tab page index
 */
-void QtxWorkstack::split( const int o )
+void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
 {
-  QtxWorkstackArea* area = myWorkArea;
-  if ( !area )
-    area = activeArea();
-  if ( !area )
+  if ( !area || ( myArea == area && tab == myTab ) )
     return;
 
-  if ( area->widgetList().count() < 2 )
-    return;
+  startDrawRect();
 
-  QWidget* curWid = area->activeWidget();
-  if ( !curWid )
+  if ( myArea )
+    drawRect();
+
+  myTab = tab;
+  myArea = area;
+
+  if ( myArea )
+    drawRect();
+}
+
+/*!
+  \brief Called when drop operation is finished.
+  
+  Inserts dropped widget to the target workarea.
+*/
+void QtxWorkstackDrag::dropWidget()
+{
+  if ( myArea )
+    myArea->insertWidget( myChild->widget(), myTab );
+}
+
+/*!
+  \brief Draw floating rectangle.
+*/
+void QtxWorkstackDrag::drawRect()
+{
+  if ( !myArea )
     return;
 
-  QSplitter* s = splitter( area );
-  QPtrList<QtxWorkstackArea> areaList;
-  areas( s, areaList );
+  QRect r = myArea->floatRect();
+  int m = 2;
 
-  QPtrList<QSplitter> splitList;
-  splitters( s, splitList );
+  r.setTop( r.top() + m + 2 );
+  r.setLeft( r.left() + m + 2 );
+  r.setRight( r.right() - m - 2 );
+  r.setBottom( r.bottom() - m - 2 );
 
-  QSplitter* trg = 0;
-  if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
-    trg = s;
+  if ( myAreaRect )
+  {
+    myAreaRect->setGeometry( r );
+    myAreaRect->setVisible( r.isValid() );
+  }
 
-  if ( !trg )
-    trg = wrapSplitter( area );
+  QRect tr = myArea->floatTab( myTab );
 
-  if ( !trg )
-    return;
+  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() );
+  }
+}
 
-  trg->setOrientation( (Orientation)o );
+/*!
+  \brief Delete rubber band on the end on the dragging operation.
+*/
+void QtxWorkstackDrag::endDrawRect()
+{
+  delete myAreaRect;
+  myAreaRect = 0;
 
-  QtxWorkstackArea* newArea = createArea( 0 );
-  insertWidget( newArea, trg, area );
+  delete myTabRect;
+  myTabRect = 0;
+}
 
-  area->removeWidget( curWid );
-  newArea->insertWidget( curWid );
+/*!
+  \brief Create rubber band to be drawn on the dragging operation.
+*/
+void QtxWorkstackDrag::startDrawRect()
+{
+  if ( !myTabRect )
+    myTabRect = new QRubberBand( QRubberBand::Rectangle );
 
-  distributeSpace( trg );
+  myTabRect->hide();
 
-  curWid->show();
-  curWid->setFocus();
+  if ( !myAreaRect )
+    myAreaRect = new QRubberBand( QRubberBand::Rectangle );
+
+  myAreaRect->hide();
 }
 
 /*!
- \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
+  \class QtxWorkstackArea
+  \internal
+  \brief Workstack widget workarea.
+*/
+
+/*!
+  \brief Constructor.
+  \param parent parent widget
 */
-void QtxWorkstack::Split (QWidget* wid, const Qt::Orientation o, const SplitType type)
+QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
+: QFrame( parent )
 {
-  if (!wid) return;
+  setFrameStyle( QFrame::Panel | QFrame::Sunken );
 
-  // find area of the given widget
-  QtxWorkstackArea* area = NULL;
-  QPtrList<QtxWorkstackArea> allAreas;
-  areas(mySplit, allAreas, true);
+  QVBoxLayout* base = new QVBoxLayout( this );
+  base->setMargin( frameWidth() );
 
-  QPtrListIterator<QtxWorkstackArea> it (allAreas);
-  for (; it.current() && !area; ++it) {
-    if (it.current()->contains(wid))
-      area = it.current();
-  }
-  if (!area) return;
+  QWidget* top = new QWidget( this );
+  base->addWidget( top );
 
-  QWidgetList wids = area->widgetList();
-  if (wids.count() < 2)
-    return;
+  QHBoxLayout* tl = new QHBoxLayout( top );
+  tl->setMargin( 0 );
 
-  QSplitter* s = splitter(area);
-  QPtrList<QtxWorkstackArea> areaList;
-  areas(s, areaList);
+  myBar = new QtxWorkstackTabBar( top );
+  tl->addWidget( myBar, 1 );
 
-  QPtrList<QSplitter> splitList;
-  splitters(s, splitList);
+  QPushButton* close = new QPushButton( top );
+  close->setIcon( style()->standardIcon( QStyle::SP_TitleBarCloseButton ) );
+  close->setAutoDefault( true );
+  close->setFlat( true );
+  myClose = close;
+  tl->addWidget( myClose );
 
-  QSplitter* trg = 0;
-  if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
-    trg = s;
+  myStack = new QStackedWidget( this );
 
-  if (!trg) trg = wrapSplitter(area);
-  if (!trg) return;
+  base->addWidget( myStack, 1 );
 
-  trg->setOrientation(o);
+  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 ) ) );
 
-  QtxWorkstackArea* newArea = createArea(0);
-  insertWidget(newArea, trg, area);
+  updateState();
 
-  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;
-  }
+  updateActiveState();
 
-  distributeSpace(trg);
+  QApplication::instance()->installEventFilter( this );
 }
 
 /*!
- \brief Put given widget on top of its workarea
- \param wid - widget, belonging to this workstack
+  \brief Destructor.
 */
-/*
-void QtxWorkstack::OnTop (QWidget* wid)
+QtxWorkstackArea::~QtxWorkstackArea()
+{
+  QApplication::instance()->removeEventFilter( this );
+}
+
+/*!
+  \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 ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !res; ++it )
+    res = it.value().vis;
+  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)
+*/
+QWidget* QtxWorkstackArea::insertWidget( QWidget* wid, const int idx, Qt::WindowFlags f )
 {
   if ( !wid )
-    return;
+    return 0;
 
-  // 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 )
+  int pos = myList.indexOf( wid );
+  if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
+    return 0;
+
+  myList.removeAll( wid );
+  pos = idx < 0 ? myList.count() : idx;
+  myList.insert( qMin( pos, (int)myList.count() ), wid );
+  if ( !myInfo.contains( wid ) )
   {
-    if ( it.current()->contains( wid ) )
-      area = it.current();
+    QtxWorkstackChild* child = new QtxWorkstackChild( wid, myStack, f );
+    myChild.insert( wid, child );
+    myInfo.insert( wid, WidgetInfo() );
+    myInfo[wid].id = generateId();
+    myInfo[wid].vis = wid->isVisibleTo( wid->parentWidget() );
+
+    connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
+    connect( wid, SIGNAL( destroyed() ), this, SLOT( onWidgetDestroyed() ) );
+    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* ) ) );
   }
 
-  if ( area )
-    area->setActiveWidget( wid );
+  updateState();
+
+  setWidgetActive( wid );
+  wid->setFocus();
+
+  return myChild[wid];
 }
-*/
 
 /*!
- \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 Create and show popup menu for the area.
+  \param p mouse pointer position at which popup menu should be shown
 */
-void QtxWorkstack::Attract ( QWidget* wid1, QWidget* wid2, const bool all )
+void QtxWorkstackArea::onContextMenuRequested( QPoint p )
 {
-  if ( !wid1 || !wid2 )
+  const QtxWorkstackTabBar* bar = ::qobject_cast<const QtxWorkstackTabBar*>( sender() );
+  if ( !bar )
     return;
 
-  // 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;
+  QWidget* wid = 0;
+  int idx = tabAt( p );
+  if ( idx != -1 )
+    wid = widget( myBar->tabId( idx ) );
 
-  QWidget* curWid = area1->activeWidget();
-  if (!curWid) return;
+  emit contextMenuRequested( wid, p );
+}
 
-  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);
-
-    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);
-    }
-  }
-
-  area1->setActiveWidget( curWid );
-}
+/*!
+  \brief Called when area's child widget is destroyed.
 
-static void setSizes (QIntList& szList, const int item_ind,
-                      const int new_near, const int new_this, const int new_farr)
+  Removes child widget from the area.
+*/
+void QtxWorkstackArea::onWidgetDestroyed()
 {
-  // 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;
-  }
+  if ( sender() )
+    removeWidget( (QWidget*)sender(), 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 Remove widget from workarea.
+  \param wid widget to be removed
+  \param del if \c true the widget should be also deleted
 */
-void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
+void QtxWorkstackArea::removeWidget( QWidget* wid, const bool del )
 {
-  if ( position < 0.0 || 1.0 < position)
-    return;
-
-  if ( !wid )
+  if ( !myList.contains( 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 ( myBar->indexOf( widgetId( wid ) ) != -1 )
+    myBar->removeTab( myBar->indexOf( widgetId( wid ) ) );
 
-  if ( !area )
-    return;
+  myStack->removeWidget( child( wid ) );
 
-  QSplitter* split = splitter( area );
-  if ( !split )
-    return;
+  myList.removeAll( wid );
+  myInfo.remove( wid );
+  myChild.remove( wid );
 
-  // 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)
+  if ( del )
   {
-    if (ito.current() == area)
-      isFound = true;
+    delete child( wid );
+    if ( myList.isEmpty() )
+      delete this;
+    else
+      updateState();
   }
-  if (!isFound || item_ind == 0)
-    return;
-
-  QIntList szList = split->sizes();
-  int splitter_size = (split->orientation() == Horizontal ?
-                       split->width() : split->height());
-  int nb = szList.count();
+  else
+    updateState();
+}
 
-  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);
+/*!
+  \brief Get all visible child widgets.
+  \return list of visible child widgets
+*/
+QWidgetList QtxWorkstackArea::widgetList() const
+{
+  QWidgetList lst;
+  for ( QWidgetList::const_iterator it = myList.begin(); it != myList.end(); ++it )
+  {
+    if ( widgetVisibility( *it ) )
+      lst.append( *it );
+  }
+  return lst;
 }
 
 /*!
-* \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].
+  \brief Get active child widget.
+  \return active widget
 */
-void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
-                                        const double position )
+QWidget* QtxWorkstackArea::activeWidget() const
 {
-  if ( position < 0.0 || 1.0 < position )
-    return;
+  return widget( myBar->tabId( myBar->currentIndex() ) );
+}
 
-  if ( !wid )
-    return;
+/*!
+  \brief Set active widget.
+  \param wid widget to be made active
+*/
+void QtxWorkstackArea::setActiveWidget( QWidget* wid )
+{
+  myBar->setCurrentIndex( myBar->indexOf( widgetId( wid ) ) );
+}
 
-  int splitter_size = o == Horizontal ? mySplit->width() : mySplit->height();
-  int need_pos = int( position * splitter_size );
-  int splitter_pos = 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 myList.contains( wid );
+}
 
-  if ( setPosition( wid, mySplit, o, need_pos, splitter_pos ) != 0 )
+/*!
+  \brief Show/hide workarea.
+  \param on new visibility state
+*/
+void QtxWorkstackArea::setVisible( bool on )
+{
+  QMap<QWidget*, bool> map;
+  for ( QWidgetList::iterator it = myList.begin(); it != myList.end(); ++it )
   {
-    // impossible to set required position
+    map.insert( *it, isBlocked( *it ) );
+    setBlocked( *it, true );
   }
+
+  QFrame::setVisible( on );
+
+  for ( QWidgetList::iterator itr = myList.begin(); itr != myList.end(); ++itr )
+    setBlocked( *itr, map.contains( *itr ) ? map[*itr] : false );
 }
 
 /*!
-* \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 Check if workarea is active.
+  \return \c true if area is active
 */
-void QtxWorkstack::setAccel( const int id, const int accel )
+bool QtxWorkstackArea::isActive() const
 {
-  if ( !myActionsMap.contains( id ) )
-    return;
+  QtxWorkstack* ws = workstack();
+  if ( !ws )
+    return false;
 
-  myActionsMap[id]->setAccel( accel );
+  return ws->activeArea() == this;
 }
 
 /*!
-* \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 Update active tab bar state (active/inactive).
 */
-int QtxWorkstack::accel( const int id ) const
+void QtxWorkstackArea::updateActiveState()
 {
-  int res = 0;
-  if ( myActionsMap.contains( id ) )
-    res = myActionsMap[id]->accel();
-  return res;
+  myBar->setActive( isActive() );
 }
 
-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 Get parent workstack
+  \return workstack owning this workarea
+*/
+QtxWorkstack* QtxWorkstackArea::workstack() const
 {
-  if (item_ind == 0) { // cannot move in this splitter
-    return (need_pos - splitter_pos);
+  QtxWorkstack* ws = 0;
+  QWidget* wid = parentWidget();
+  while ( wid && !ws )
+  {
+    if ( wid->inherits( "QtxWorkstack" ) )
+      ws = (QtxWorkstack*)wid;
+    wid = wid->parentWidget();
   }
+  return ws;
+}
 
-  int delta = 0;
-  int new_prev = 0;
-  int new_this = szList[item_ind];
-  int new_next = 0;
-
-  bool isToCheck = false;
-
-  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;
+/*!
+  \brief Custom event filter.
 
-  } 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);
+  Process events from child widgets.
 
-  } 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;
+  \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 ( 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();
       }
-      // 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);
+      if ( ok )
+        QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
     }
-    delta = 0;
   }
+  return false;
+}
 
-  setSizes (szList, item_ind, new_prev, new_this, new_next);
-  return delta;
+/*!
+  \brief Get rectangle to be drawn when highlighting drop area.
+  \return area drop rectangle
+*/
+QRect QtxWorkstackArea::floatRect() const
+{
+  QRect r = myStack->geometry();
+  return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
 }
 
 /*!
-* \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 rectangle to be drawn when highlighting drop area on tab bar.
+  \param idx tab index
+  \return tab bar drop rectrangle
 */
-int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
-                               const int need_pos, const int splitter_pos )
+QRect QtxWorkstackArea::floatTab( const int idx ) const
 {
-  if ( !wid || !split )
-    return need_pos - splitter_pos;
+  QRect r = myBar->tabRect( idx );
+  return QRect( myBar->mapToGlobal( r.topLeft() ), r.size() );
+}
 
-  // 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 )
+/*!
+  \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
+{
+  int idx = -1;
+  QPoint p = myBar->mapFromGlobal( pnt );
+  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;
+}
+
+/*!
+  \brief Event handler for custom events.
+  \param e custom event
+*/
+void QtxWorkstackArea::customEvent( QEvent* e )
+{
+  WidgetEvent* we = (WidgetEvent*)e;
+
+  switch ( we->type() )
   {
-    for (QObjectListIt it (*objs); it.current() && !isFound; ++it)
+  case ActivateWidget:
+    emit activated( activeWidget() );
+    break;
+  case FocusWidget:
+    if ( activeWidget() )
     {
-      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 ( !activeWidget()->focusWidget() )
+        activeWidget()->setFocus();
+      else
+      {
+        if ( activeWidget()->focusWidget()->hasFocus() )
         {
-          if (ita.current()->contains(wid)) {
-            item_ind = cur_ind;
-            isFound = true;
-            sub_split = (QSplitter*)it.current();
-          }
-        }
-        cur_ind++;
+          QFocusEvent in( QEvent::FocusIn );
+               QApplication::sendEvent( this, &in );
+             }
+        else
+          activeWidget()->focusWidget()->setFocus();
       }
     }
+    break;
+  case RemoveWidget:
+    removeWidget( we->widget() );
+    break;
+  default:
+    break;
   }
-  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
-
-      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;
 }
 
 /*!
-  Redistributes space among widgets equally
+  \brief Customize focus in event handler.
+  \param e focus in event
 */
-void QtxWorkstack::distributeSpace( QSplitter* split ) const
+void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
 {
-  if ( !split )
-    return;
+  QFrame::focusInEvent( e );
 
-  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 );
+  emit activated( activeWidget() );
 }
 
 /*!
-  Splits widgets vertically
+  \brief Customize mouse press event handler.
+  \param e mouse press event
 */
-void QtxWorkstack::splitVertical()
+void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
 {
-  split( Qt::Horizontal );
+  QFrame::mousePressEvent( e );
+
+  emit activated( activeWidget() );
 }
 
 /*!
-  Splits widgets horizontally
+  \brief Called when user presses "Close" button.
 */
-void QtxWorkstack::splitHorizontal()
+void QtxWorkstackArea::onClose()
 {
-  split( Qt::Vertical );
+  QWidget* wid = activeWidget();
+  if ( wid )
+    wid->close();
 }
 
 /*!
-  SLOT: called if action "Rename" is activated, changes caption of widget
+  \brief Called when user selects any tab page.
+  \param idx tab page index (not used)
 */
-void QtxWorkstack::onRename()
+void QtxWorkstackArea::onCurrentChanged( int /*idx*/ )
 {
-  if ( !myWorkWin )
-    return;
+  updateCurrent();
 
-  bool ok = false;
-  QString newName = QInputDialog::getText( tr( "Rename" ), tr( "Enter new name:" ), QLineEdit::Normal,
-                                           myWorkWin->caption(), &ok, topLevelWidget() );
-  if ( ok && !newName.isEmpty() )
-    myWorkWin->setCaption( newName );
+  emit activated( activeWidget() );
 }
 
 /*!
-  Wraps area into new splitter
-  \return new splitter
+  \brief Called when user starts tab page dragging.
 */
-QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
+void QtxWorkstackArea::onDragActiveTab()
 {
-  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 );
+  QtxWorkstackChild* c = child( activeWidget() );
+  if ( !c )
+    return;
 
-  return wrap;
+  new QtxWorkstackDrag( workstack(), c );
 }
 
 /*!
-  Reparenst and adds widget
-  \param wid - widget
-  \param pWid - parent widget
-  \param after - after widget
+  \brief Called when area's child widget container is destroyed.
+  \param obj widget container being destroyed
 */
-void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
+void QtxWorkstackArea::onChildDestroyed( QObject* obj )
 {
-  if ( !wid || !pWid )
-    return;
+  QtxWorkstackChild* child = (QtxWorkstackChild*)obj;
+  myStack->removeWidget( child );
 
-  QWidgetList moveList;
-  const QObjectList* lst = pWid->children();
-  if ( lst )
+  QWidget* wid = 0;
+  for ( ChildMap::ConstIterator it = myChild.begin(); it != myChild.end() && !wid; ++it )
   {
-    bool found = false;
-    for ( QObjectListIt it( *lst ); it.current(); ++it )
-    {
-      if ( found && ( it.current()->inherits( "QSplitter" ) ||
-                      it.current()->inherits( "QtxWorkstackArea" ) ) )
-        moveList.append( (QWidget*)it.current() );
-      if ( it.current() == after )
-        found = true;
-    }
+    if ( it.value() == child )
+      wid = it.key();
   }
 
-  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 );
-  }
+  myChild.remove( wid );
 
-  wid->reparent( pWid, QPoint( 0, 0 ), true );
+  QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)RemoveWidget, wid ) );
+}
 
-  for ( QWidgetListIt itr( moveList ); itr.current(); ++itr )
-    itr.current()->reparent( pWid, QPoint( 0, 0 ), map.contains( itr.current() ) ? map[itr.current()] : false );
+/*!
+  \brief Called when child widget container is shown.
+  \param c child widget container being shown
+*/
+void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
+{
+  setWidgetShown( c->widget(), true );
 }
 
 /*!
-* \brief Closes the active window.
+  \brief Called when child widget container is hidden.
+  \param c child widget container being hidden
 */
-void QtxWorkstack::onCloseWindow()
+void QtxWorkstackArea::onChildHidden( QtxWorkstackChild* c )
 {
-  if ( myWorkWin )
-    myWorkWin->close();
-  else if( activeWindow() )
-    activeWindow()->close();
+  setWidgetShown( c->widget(), false );
 }
 
 /*!
-  SLOT: called on area is destroyed
-  Sets focus to neighbour area
+  \brief Called when child widget container is activated.
+  \param c child widget container being activated
 */
-void QtxWorkstack::onDestroyed( QObject* obj )
+void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
 {
-  QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
+  setWidgetActive( c->widget() );
+}
 
-  if ( area == myArea )
-    myArea = 0;
+/*!
+  \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() );
+}
 
-  if ( !myArea )
+/*!
+  \brief Update current child widget container.
+
+  Raises widget when active tab page is changed.
+*/
+void QtxWorkstackArea::updateCurrent()
+{
+  QMap<QWidget*, bool> map;
+  for ( QWidgetList::iterator it = myList.begin(); it != myList.end(); ++it )
   {
-    QtxWorkstackArea* cur = neighbourArea( area );
-    if ( cur )
-      cur->setFocus();
+    map.insert( *it, isBlocked( *it ) );
+    setBlocked( *it, true );
   }
 
-  QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
+  QWidget* cur = child( widget( myBar->tabId( myBar->currentIndex() ) ) );
+  if ( cur )
+    myStack->setCurrentWidget( cur );
+
+  for ( QWidgetList::iterator itr = myList.begin(); itr != myList.end(); ++itr )
+    setBlocked( *itr, map.contains( *itr ) ? map[*itr] : false );
 }
 
 /*!
-  SLOT: called on window activating
+  \brief Update tab bar.
+  \param wid tab page widget
 */
-void QtxWorkstack::onWindowActivated( QWidget* wid )
+void QtxWorkstackArea::updateTab( QWidget* wid )
 {
-  const QObject* obj = sender();
-  if ( !obj->inherits( "QtxWorkstackArea" ) )
+  int idx = myBar->indexOf( widgetId( wid ) );
+  if ( idx < 0 )
     return;
 
-  setActiveArea( (QtxWorkstackArea*)obj );
+  myBar->setTabIcon( idx, wid->windowIcon() );
+  myBar->setTabText( idx, wid->windowTitle() );
 }
 
 /*!
-  SLOT: called on window deactivating
+  \brief Get child widget by specified identifier.
+  \param id widget ID
+  \return widget or 0, if identifier in invalid
 */
-void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
+QWidget* QtxWorkstackArea::widget( const int id ) const
 {
-  if ( myArea != area )
-    return;
+  QWidget* wid = 0;
+  for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !wid; ++it )
+  {
+    if ( it.value().id == id )
+      wid = it.key();
+  }
+  return wid;
+}
 
-  QPtrList<QtxWorkstackArea> lst;
-  areas( mySplit, lst, true );
+/*!
+  \brief Get child widget identifier.
+  \param wid widget
+  \return widget ID or -1 if widget is not found
+*/
+int QtxWorkstackArea::widgetId( QWidget* wid ) const
+{
+  int id = -1;
+  if ( myInfo.contains( wid ) )
+    id = myInfo[wid].id;
+  return id;
+}
 
-  int idx = lst.find( area );
-  if ( idx == -1 )
+/*!
+  \brief Get child widget's visibility.
+  \param wid widget
+  \return \c true if widget is visible
+*/
+bool QtxWorkstackArea::widgetVisibility( QWidget* wid ) const
+{
+  bool res = false;
+  if ( myInfo.contains( wid ) )
+    res = myInfo[wid].vis;
+  return res;
+}
+
+/*!
+  \brief Set active child widget.
+  \param wid widget to be set active
+*/
+void QtxWorkstackArea::setWidgetActive( QWidget* wid )
+{
+  int id = widgetId( wid );
+  if ( id < 0 )
     return;
 
-  myWin = 0;
-  myArea = 0;
+  myBar->setCurrentIndex( myBar->indexOf( id ) );
+}
 
-  QtxWorkstackArea* newArea = neighbourArea( area );
-  if ( newArea && newArea->activeWidget() )
-    newArea->activeWidget()->setFocus();
+/*!
+  \brief Show/hide child widget.
+  \param wid widget
+  \param on new visibility state
+*/
+void QtxWorkstackArea::setWidgetShown( QWidget* wid, const bool on )
+{
+  if ( isBlocked( wid ) || !myInfo.contains( wid ) || myInfo[wid].vis == on )
+    return;
 
-  QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
+  myInfo[wid].vis = on;
+  updateState();
 }
 
 /*!
-  Creates and shows popup menu for area
-  \param w - area
-  \param p - popup position
+  \brief Update internal state.
 */
-void QtxWorkstack::onContextMenuRequested( QWidget* w, QPoint p )
+void QtxWorkstackArea::updateState()
 {
-  QtxWorkstackArea* anArea = ::qt_cast<QtxWorkstackArea*>( (QObject*)sender() );
-  if ( !anArea )
-    anArea = activeArea();
+  bool updBar = myBar->updatesEnabled();
+  bool updStk = myStack->updatesEnabled();
+  myBar->setUpdatesEnabled( false );
+  myStack->setUpdatesEnabled( false );
 
-  if ( !anArea )
-    return;
+  bool block = myBar->signalsBlocked();
+  myBar->blockSignals( true );
 
-  QWidgetList lst = anArea->widgetList();
-  if ( lst.isEmpty() )
-    return;
+  QWidget* prev = activeWidget();
 
-  myWorkWin = w;
-  myWorkArea = anArea;
+  int idx = 0;
+  for ( QWidgetList::iterator it = myList.begin(); it != myList.end(); ++it )
+  {
+    QWidget* wid = *it;
+    int id = widgetId( wid );
 
-  QPopupMenu* pm = new QPopupMenu();
-  
-  if ( lst.count() > 1 )
+    if ( id < 0 )
+      continue;
+
+    bool vis = widgetVisibility( wid );
+
+    int cIdx = myBar->indexOf( id );
+    if ( cIdx != -1 && ( !vis || myBar->indexOf( id ) != idx ) )
+      myBar->removeTab( cIdx );
+
+    if ( myBar->indexOf( id ) == -1 && vis )
+      myBar->setTabId( myBar->insertTab( idx, wid->windowTitle() ), id );
+
+    updateTab( wid );
+
+    bool block = isBlocked( wid );
+    setBlocked( wid, true );
+
+    QtxWorkstackChild* cont = child( wid );
+
+    if ( !vis )
+      myStack->removeWidget( cont );
+    else if ( myStack->indexOf( cont ) < 0 )
+      myStack->addWidget( cont );
+
+    if ( vis )
+      idx++;
+
+    setBlocked( wid, block );
+  }
+
+  int curId = widgetId( prev );
+  if ( myBar->indexOf( curId ) < 0 )
   {
-    myActionsMap[SplitVertical]->addTo( pm );
-    myActionsMap[SplitHorizontal]->addTo( pm );
-    pm->insertSeparator();
+    QWidget* wid = 0;
+    int pos = myList.indexOf( 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 ( w )
+  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 );
+
+  if ( isEmpty() )
+  {
+    hide();
+    emit deactivated( this );
+  }
+  else
   {
-    myActionsMap[Close]->addTo( pm );
-    myActionsMap[Rename]->addTo( pm );
+    show();
+    if ( prev != activeWidget() )
+      emit activated( activeWidget() );
   }
+}
 
-  Qtx::simplifySeparators( pm );
+/*!
+  \brief Generate unique widget identifier.
+  \return first non shared widget ID
+*/
+int QtxWorkstackArea::generateId() const
+{
+  QMap<int, int> map;
 
-  if ( pm->count() )
-    pm->exec( p );
+  for ( WidgetInfoMap::const_iterator it = myInfo.begin(); it != myInfo.end(); ++it )
+    map.insert( it.value().id, 0 );
 
-  delete pm;
+  int id = 0;
+  while ( map.contains( id ) )
+    id++;
 
-  myWorkWin = 0;
-  myWorkArea = 0;
+  return id;
 }
 
 /*!
-  Custom child event handler, inserts widget to active or current area
+  \brief Check if the child wiget is blocked.
+  \param wid widget
+  \return \c true if the widget is blocked
 */
-void QtxWorkstack::childEvent( QChildEvent* e )
+bool QtxWorkstackArea::isBlocked( QWidget* wid ) const
 {
-  if ( e->inserted() && e->child()->isWidgetType() )
-  {
-         QWidget* w = (QWidget*)e->child();
-         if ( w && w != mySplit )
-    {
-      targetArea()->insertWidget( w );
-      return;
-    }
-  }
-  QWidget::childEvent( e );
+  return myBlock.contains( wid );
 }
 
 /*!
-  Handler of custom events
+  \brief Block widget.
+  \param wid widget
+  \param on new blocked state
 */
-void QtxWorkstack::customEvent( QCustomEvent* e )
+void QtxWorkstackArea::setBlocked( QWidget* wid, const bool on )
 {
-  updateState();
+  if ( on )
+    myBlock.insert( wid, 0 );
+  else
+    myBlock.remove( wid );
 }
 
 /*!
-  \return splitter corresponding to area
-  \param area
+  \brief Get child widget container.
+  \param wid child widget
+  \return child widget container corresponding to the \a wid
 */
-QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
+QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
 {
-  if ( !area )
-    return 0;
+  QtxWorkstackChild* res = 0;
+  if ( myChild.contains( wid ) )
+    res = myChild[wid];
+  return res;
+}
 
-  QSplitter* split = 0;
+/*!
+  \fn void QtxWorkstackArea::activated( QWidget* w )
+  \brief Emitted when child widget is activated.
+  \param w child widget being activated
+*/
 
-  QWidget* wid = area->parentWidget();
-  if ( wid && wid->inherits( "QSplitter" ) )
-    split = (QSplitter*)wid;
+/*!
+  \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
+*/
 
-  return split;
-}
+/*!
+  \fn void QtxWorkstackArea::deactivated( QtxWorkstackArea* wa )
+  \brief Emitted when workarea is deactivated.
+  \param wa workarea being deactivated
+*/
 
 /*!
-  Fills list with children splitters
-  \param split - parent splitter
-  \param splitList - list to be filled with
-  \param rec - recursive search of children
+  \class QtxWorkstackChild
+  \internal
+  \brief Workarea child widget container.
 */
-void QtxWorkstack::splitters( QSplitter* split, QPtrList<QSplitter>& splitList, const bool rec ) const
+
+/*!
+  \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 ),
+  myWidget( wid )
 {
-  if ( !split )
-    return;
+  myWidget->setParent( this, f );
+  myWidget->installEventFilter( this );
+  QVBoxLayout* base = new QVBoxLayout( this );
+  base->addWidget( myWidget );
 
-  const QObjectList* objs = split->children();
-  if ( objs )
-  {
-    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() );
-    }
-  }
+  connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
 }
 
 /*!
-  Fills list with children areas
-  \param split - parent splitter
-  \param areaList - list to be filled with
-  \param rec - recursive search of children
+  \brief Destructor.
 */
-void QtxWorkstack::areas( QSplitter* split, QPtrList<QtxWorkstackArea>& areaList, const bool rec ) const
+QtxWorkstackChild::~QtxWorkstackChild()
 {
-  if ( !split )
+  QApplication::instance()->removeEventFilter( this );
+
+  if ( !widget() )
     return;
 
-  const QObjectList* objs = split->children();
-  if ( objs )
-  {
-    for ( QObjectListIt it( *objs ); it.current(); ++it )
-    {
-      if ( it.current()->inherits( "QtxWorkstackArea" ) )
-        areaList.append( (QtxWorkstackArea*)it.current() );
-      else if ( rec && it.current()->inherits( "QSplitter" ) )
-        areas( (QSplitter*)it.current(), areaList, rec );
-    }
-  }
+  disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
+
+  widget()->hide();
+  widget()->removeEventFilter( this );
+
+  widget()->setParent( 0 );
 }
 
 /*!
-  \return active area
+  \brief Get child widget.
+  \return child widget
 */
-QtxWorkstackArea* QtxWorkstack::activeArea() const
+QWidget* QtxWorkstackChild::widget() const
 {
-  return myArea;
+  return myWidget;
 }
 
 /*!
-  \return active area or current area or create new area of there is no one
+  \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)
 */
-QtxWorkstackArea* QtxWorkstack::targetArea()
+bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
 {
-  QtxWorkstackArea* area = activeArea();
-  if ( !area )
-    area = currentArea();
-  if ( !area )
+  if ( o->isWidgetType() )
   {
-    QPtrList<QtxWorkstackArea> lst;
-    areas( mySplit, lst );
-    if ( !lst.isEmpty() )
-      area = lst.first();
+    if ( e->type() == QEvent::WindowTitleChange || e->type() == QEvent::WindowIconChange )
+      emit captionChanged( this );
+
+    if ( !e->spontaneous() && ( e->type() == QEvent::Show || e->type() == QEvent::ShowToParent ) )
+      emit shown( this );
+
+    if ( !e->spontaneous() && ( e->type() == QEvent::Hide || e->type() == QEvent::HideToParent ) )
+      emit hidden( this );
+
+    if ( e->type() == QEvent::FocusIn )
+      emit activated( this );
   }
+  return QWidget::eventFilter( o, e );
+}
 
-  if ( !area )
-    area = createArea( mySplit );
+/*!
+  \brief Called when child widget is destroyed.
+  \param obj child widget being destroyed
+*/
+void QtxWorkstackChild::onDestroyed( QObject* obj )
+{
+  if ( obj != widget() )
+    return;
 
-  return area;
+  myWidget = 0;
+  deleteLater();
 }
 
 /*!
-  \return current area (that has focus)
+  \brief Customize child event handler.
+  \param e child event
 */
-QtxWorkstackArea* QtxWorkstack::currentArea() const
+void QtxWorkstackChild::childEvent( QChildEvent* e )
 {
-  QtxWorkstackArea* area = 0;
-  QWidget* wid = focusWidget();
-  while ( wid && !area )
+  if ( e->removed() && e->child() == widget() )
   {
-    if ( wid->inherits( "QtxWorkstackArea" ) )
-      area = (QtxWorkstackArea*)wid;
-    wid = wid->parentWidget();
+    myWidget = 0;
+    deleteLater();
   }
-
-  return area;
+  QWidget::childEvent( e );
 }
 
 /*!
-  Creates new area
+  \fn void QtxWorkstackChild::shown( QtxWorkstackChild* w )
+  \brief Emitted when child widget is shown.
+  \param w child widget container
 */
-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;
-}
 
 /*!
-  Sets area as active
-  \param area
+  \fn void QtxWorkstackChild::hidden( QtxWorkstackChild* w )
+  \brief Emitted when child widget is hidden.
+  \param w child widget container
 */
-void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
-{
-  QWidget* oldCur = myWin;
 
-  QtxWorkstackArea* oldArea = myArea;
+/*!
+  \fn void QtxWorkstackChild::activated( QtxWorkstackChild* w )
+  \brief Emitted when child widget is activated.
+  \param w child widget container
+*/
 
-  myArea = area;
+/*!
+  \fn void QtxWorkstackChild::captionChanged( QtxWorkstackChild* w )
+  \brief Emitted when child widget's title is changed.
+  \param w child widget container
+*/
 
-  if ( myArea != oldArea )
-  {
-    if ( oldArea )
-      oldArea->updateActiveState();
-    if ( myArea )
-      myArea->updateActiveState();
-  }
+/*!
+  \class QtxWorkstackTabBar
+  \internal
+  \brief Workstack tab bar widget
+*/
 
-  if ( myArea )
-    myWin = myArea->activeWidget();
+/*!
+  \brief Constructor.
+  \param parent parent widget
+*/
+QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
+: QTabBar( parent ),
+  myId( -1 )
+{
+  setDrawBase( true );
+  setElideMode( Qt::ElideNone );
 
-  if ( myWin && oldCur != myWin )
-    emit windowActivated( myWin );
+  connect( this, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
 }
 
 /*!
-  \return neighbour area
-  \param area - area to search neighbour
+  \brief Destructor.
 */
-QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
+QtxWorkstackTabBar::~QtxWorkstackTabBar()
 {
-  QPtrList<QtxWorkstackArea> lst;
-  areas( mySplit, lst, true );
-  int pos = lst.find( area );
-  if ( pos < 0 )
-    return 0;
-
-  QtxWorkstackArea* na = 0;
-  for ( int i = pos - 1; i >= 0 && !na; i-- )
-  {
-    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 );
-  }
-  return na;
 }
 
 /*!
-  \return area covering point
-  \param p - point
+  \brief Get tab page identifier.
+  \param index tab page index
+  \return tab page ID or -1 if \a index is out of range
 */
-QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
+int QtxWorkstackTabBar::tabId( const int index ) const
 {
-  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;
+  QVariant v = tabData( index );
+  if ( !v.canConvert( QVariant::Int ) )
+    return -1;
+  return v.toInt();
 }
 
 /*!
-  Update
+  \brief Set tab page identifier.
+  \param index tab page index
+  \param id tab page ID
 */
-void QtxWorkstack::updateState()
+void QtxWorkstackTabBar::setTabId( const int index, const int id )
 {
-  updateState( mySplit );
+  setTabData( index, id );
 }
 
 /*!
-  Update splitters
+  \brief Get tab page index by specified identifier.
+  \param id tab page ID
+  \return tab page index or -1 if not found
 */
-void QtxWorkstack::updateState( QSplitter* split )
+int QtxWorkstackTabBar::indexOf( const int id ) const
 {
-  QPtrList<QSplitter> recList;
-  splitters( split, recList, false );
-  for ( QPtrListIterator<QSplitter> itr( recList ); itr.current(); ++itr )
-    updateState( itr.current() );
-
-  QPtrList<QSplitter> splitList;
-  splitters( split, splitList, false );
-
-  QPtrList<QtxWorkstackArea> areaList;
-  areas( split, areaList, false );
-
-  bool vis = false;
-  for ( QPtrListIterator<QtxWorkstackArea> it( areaList ); it.current(); ++it )
+  int index = -1;
+  for ( int i = 0; i < (int)count() && index < 0; i++ )
   {
-    if ( it.current()->isEmpty() )
-      it.current()->hide();
-    else
-    {
-      it.current()->show();
-      vis = true;
-    }
+    if ( tabId( i ) == id )
+      index = i;
   }
-
-  if ( split == mySplit )
-    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();
+  return index;
 }
 
 /*!
-  Gets splitter info for debug
-  \param split - splitter
-  \param info - string to be filled with info
+  \brief Check if the tab bar is active.
+  \return \c true if tab bar is active
 */
-void QtxWorkstack::splitterInfo( QSplitter* split, QString& info ) const
+bool QtxWorkstackTabBar::isActive() const
 {
-  if ( !split )
-    return;
-
-  const QObjectList* objs = split->children();
-  if ( objs )
-  {
-    // make up a sizes string: integer values are separated by ':' char
-    QValueList<int> sizes = split->sizes();
-    QString sizesStr;
-    for ( QValueList<int>::Iterator sIt = sizes.begin(); sIt != sizes.end(); ++sIt ) {
-      if ( *sIt > 1 ) // size 1 pixel usually means empty Workstack area, which will NOT be re-created,
-       sizesStr += QString( ":%1" ).arg( *sIt );  // so we don't need to store its size
-    }
-    if ( !sizesStr.isEmpty() ) // cut the first ':'
-      sizesStr = sizesStr.right( sizesStr.length()-1 );    
-
-    // count all QSplitter-s and QtxWorkstackArea-s
-    //    int nChilds( 0 );
-    //    QObjectListIt it( *objs );
-    //    for ( ; it.current(); ++it )
-    //    {
-    //      if ( it.current()->inherits( "QSplitter" ) ||
-    //           it.current()->inherits( "QtxWorkstackArea" ) )
-    // nChilds++;
-    //    }
-      
-    info += QString( "(splitter orientation=%1 sizes=%3 " ).arg( split->orientation() ).arg( sizesStr );
-    
-    for ( QObjectListIt it( *objs ); it.current(); ++it )
-    {
-      if ( it.current()->inherits( "QSplitter" ) )
-       splitterInfo( (QSplitter*)it.current(), info );
-      else if ( it.current()->inherits( "QtxWorkstackArea" ) ) {
-       QtxWorkstackArea* area = (QtxWorkstackArea*)it.current();
-       if ( area->isEmpty() )
-         continue;
-       info += QString( "(views active='%1'" ).arg( area->activeWidget()->name() );
-       QWidgetList views = area->widgetList();
-       for ( QWidgetListIt wIt( views ); wIt.current(); ++wIt )
-         info += QString( " '%1'" ).arg( wIt.current()->name() );
-       info += ')';
-      }
-    }
-  }
-  info += ')';
+  return myActive;
 }
 
-
-//Cuts starting '(' symbol and ending '(' symbol
-void cutBrackets( QString& parameters )
+/*!
+  \brief Set tab bar active/inactive.
+  \param on new active state
+*/
+void QtxWorkstackTabBar::setActive( const bool on )
 {
-  QChar c1 = parameters[0];
-  QChar c2 = parameters[int(parameters.length()-1)];
-  if ( !parameters.isEmpty() && c1 == '(' && c2 == ')' )
-    parameters = parameters.mid( 1, parameters.length()-2 );
+  if ( myActive == on )
+    return;
+
+  myActive = on;
+  updateActiveState();
 }
 
-/*
-  for strings like "(splitter orientation=0 children=2 sizes=332:478" returns values of
-  parameters.  For example, getValue( example, "children" ) returns "2"
-  getValue( example, "sizes" ) returns "332:478"
+/*!
+  \brief Update tab bar according to the 'active' state.
 */
-QString getValue( const QString& str, const QString& valName )
+void QtxWorkstackTabBar::updateActiveState()
 {
-  int i = str.find( valName );
-  if ( i != -1 ) {
-    int equal_i = str.find( '=', i );
-    if ( equal_i != -1 ) {
-      int space_i = str.find( ' ', ++equal_i );
-      if ( space_i != -1 )
-       return str.mid( equal_i, space_i-equal_i );
-    }
-  }
-  return QString( "" );
+  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 );
 }
 
-/*
-  checks format of splitter parameters string
+/*!
+  \brief Called when current tab page is changed.
+  \param idx tab page index (not used)
 */
-bool checkFormat( const QString& parameters )
+void QtxWorkstackTabBar::onCurrentChanged( int /*index*/ )
 {
-  QString params( parameters );
-  // 1. begins and ends with brackets
-  QChar c1 = params[0];
-  QChar c2 = params[int(params.length()-1)];
-  bool ok = ( c1 == '(' && c2 == ')' );
-  if ( !ok ) return ok;
-  ::cutBrackets( params );
-  // 2. has splitter word
-  ok = ( params.left( 8 ) == "splitter" );
-  if ( !ok ) return ok;
-  // 3. has children?  = '(' is found
-  int i = params.find( '(' );
-  ok = i != -1;
-  if ( !ok ) return ok;
-  params = params.left( i ); // cut all children, they will be checked later
-  // 4. has orientation word and correct value
-  ::getValue( params, "orientation" ).toInt( &ok );
-  if ( !ok ) return ok;
-  // 5. has sizes word and values
-  ok = ! ::getValue( params, "sizes" ).isEmpty();
-  if ( !ok ) return ok;
-  // 6. check children -> number of '(' == number of ')' in original string
-  ok = ( parameters.contains( '(' ) == parameters.contains( ')' ) );
-  return ok;
+  updateActiveState();
 }
 
-/*
-  Returns children of splitter in a list.  Children are separated by '(' and ')' symbols
+/*!
+  \brief Customize mouse move event handler.
+  \param e mouse event
 */
-QStringList getChildren( const QString& str )
+void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
 {
-  QStringList lst;
-  if ( !str.startsWith( "(" ) )
-    return lst;
-  
-  int i = 1,
-  nOpen = 1, // count brackets: '(' increments nOpen, ')' decrements
-  start = 0;
-  while ( i < (int)str.length() )
+  if ( myId != -1 && !tabRect( indexOf( myId ) ).contains( e->pos() ) )
   {
-    if ( str[i] == '(' )
-    {
-      nOpen++;
-      if ( nOpen == 1 )
-       start = i;
-    }
-    else if ( str[i] == ')' )
-    {
-      nOpen--;
-      if ( nOpen == 0 ) 
-       lst.append( str.mid( start, i-start+1 ) );
-    }
-    i++;
+    myId = -1;
+    emit dragActiveTab();
   }
 
-  return lst;
+  QTabBar::mouseMoveEvent( e );
 }
 
-// for a string like "views active='AnotherView' 'GLView' 'AnotherView'"
-// getViewName( example, 0 ) returns "GLView", 
-// getViewName( example, 1 ) -> "AnotherView", etc.
-QString getViewName( const QString& str, int i )
+/*!
+  \brief Customize mouse press event handler.
+  \param e mouse event
+*/
+void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
 {
-  QRegExp exp( "\\s'\\w+'" );
-  int start = 0; // start index of view name in the string
-  int num = 0 ; // index of found match
-  while ( ( start = exp.search( str, start ) ) != -1 && num < i ) {
-    start += exp.matchedLength();
-    num ++;
-  }
-  if ( start != -1 )      // +2 and -3 avoid starting space and starting and ending ' symbols
-    return str.mid( start+2, exp.matchedLength()-3 );
-
-  return QString( "" );
-}
+  QTabBar::mousePressEvent( e );
 
-// returns widget with given name
-QWidget* getView( const QWidget* parent, const QString& aName )
-{
-  QWidget* view = 0;
-  QObjectList *l = parent->topLevelWidget()->queryList( "QWidget", aName, false, true );
-  if ( !l->isEmpty() )
-    view = ::qt_cast<QWidget*>( l->first() );
-  delete l;
-  return view;
+  if ( e->button() == Qt::LeftButton )
+    myId = tabId( currentIndex() );
 }
 
 /*!
-  Installs a splitter described by given parameters string
+  \brief Customize mouse release event handler.
+  \param e mouse event
 */
-void QtxWorkstack::setSplitter( QSplitter* splitter, const QString& parameters, QMap< QSplitter*, QValueList<int> >& sMap )
+void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
 {
-  if ( !::checkFormat( parameters ) ) {
-    printf( "\nInvalid format of workstack parameters.  Positions of viewers can not be restored.\n" );
-    return;
-  }
-
-  QString params( parameters );
-  ::cutBrackets( params );
-
-  // get splitter sizes and store it in the map for future setting
-  QValueList<int> sizes;
-  QStringList sizesLst = QStringList::split( ':', ::getValue( params, "sizes" ) );
-  QStringList::Iterator it;
-  for ( it = sizesLst.begin(); it != sizesLst.end(); ++it )
-    sizes.append( (*it).toInt() );
-  sMap[ splitter ] = sizes;
+  QTabBar::mouseReleaseEvent( e );
 
-  // set orientation of splitter
-  int orient = ::getValue( params, "orientation" ).toInt();
-  splitter->setOrientation( (Qt::Orientation)orient );
+  myId = -1;
 
-  // get children
-  QString options = params.left( params.find( '(' ) );
-  QString childrenStr = params.right( params.length()-options.length() );
-  QStringList children = ::getChildren( childrenStr );
+  if ( e->button() == Qt::RightButton )
+    emit contextMenuRequested( e->globalPos() );
+}
 
-  // debug output..
-  //  printf (" splitter orient=%d, sizes_count=%d, children=%d\n", orient, sizes.count(), children.count() ); 
-  //  for ( QStringList::Iterator tit = children.begin(); tit != children.end(); ++tit ) 
-  //    printf ("   |-> child = [%s]\n", (*tit).latin1() );
+/*!
+  \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() );
+}
 
-  for ( QStringList::Iterator it = children.begin(); it != children.end(); ++it ) {
-    if ( (*it).startsWith( "(splitter" ) ) {
-      QSplitter* newSplitter = new QSplitter( splitter );
-      setSplitter( newSplitter, *it, sMap );
-    }
-    else if ( (*it).startsWith( "(views" ) ) {
-      QtxWorkstackArea* newArea = createArea( splitter );
-      QString activeViewName = ::getValue( *it, "active" );
-      QWidget* activeView( 0 );
-      activeViewName = activeViewName.mid( 1, activeViewName.length()-2 ); // chop off ' symbols
-      int i = 0;
-      QString viewName = ::getViewName( *it, i );
-      while ( !viewName.isEmpty() ) {
-       if ( QWidget* view = ::getView( splitter, viewName ) ) {
-         newArea->insertWidget( view );
-         if ( activeViewName == view->name() )
-           activeView = view;
-       }
-       viewName = ::getViewName( *it, ++i );
-      }
-      if ( activeView )
-       newArea->setActiveWidget( activeView );
-    }
+/*
+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 );
 }
+*/
 
 /*!
-  Restore workstack's configuration stored in 'parameters' string
+  \fn void QtxWorkstackTabBar::dragActiveTab()
+  \brief Emitted when dragging operation is started.
 */
-QtxWorkstack& QtxWorkstack::operator<<( const QString& parameters )
-{
-  // clear the main splitter - remove all child splitters and empty areas from it
-  QPtrList<QSplitter> splitList;
-  QPtrList<QtxWorkstackArea> areaList;
-  splitters( mySplit, splitList, false );
-  areas( mySplit, areaList, false );
-  for ( QPtrListIterator<QSplitter> iter( splitList ); iter.current(); ++iter )
-    delete iter.current();
-  for ( QPtrListIterator<QtxWorkstackArea> it( areaList ); it.current(); ++it ) 
-    if ( it.current()->isEmpty() )
-      delete it.current();
-
-  // restore splitter recursively
-  QMap< QSplitter*, QValueList<int> > sMap;
-  setSplitter( mySplit, parameters, sMap );  
 
-  // now mySplit may contains empty area (where all views were located before restoring)
-  // in order setSize to work correctly we have to exclude this area
-  areaList.clear();
-  areas( mySplit, areaList, false );
-  for ( QPtrListIterator<QtxWorkstackArea> delIt( areaList ); delIt.current(); ++delIt ) 
-    if ( delIt.current()->isEmpty() )
-      delete delIt.current();
+/*!
+  \fn void QtxWorkstackTabBar::contextMenuRequested( QPoint p )
+  \brief Emitted when context popup menu is requested.
+  \param p point popup menu to be shown at
+*/
 
-  qApp->processEvents();
+/*!
+  \class QtxWorkstack
+  \brief Workstack widget.
 
-  // restore splitters' sizes (map of sizes is filled in setSplitters)
-  for ( QMap< QSplitter*, QValueList<int> >::Iterator itm = sMap.begin(); itm != sMap.end(); ++itm )
-    itm.key()->setSizes( itm.data() );
+  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.
 
-  return (*this);
-}
+  This widget can be used as workspace of the application main window, 
+  for example, as kind of implementation of multi-document interface.
+*/
 
 /*!
-  Example of string produced by operator>> :
-  "(splitter orientation=0 sizes=186:624 (views active='OCCViewer_0_0' 'OCCViewer_0_0')
-/ (views active='VTKViewer_0_0' 'VTKViewer_0_0'))"
+  \brief Constructor.
+  \param parent parent widget
 */
-QtxWorkstack& QtxWorkstack::operator>>( QString& outParameters )
+QtxWorkstack::QtxWorkstack( QWidget* parent )
+: QWidget( parent ),
+  myWin( 0 ),
+  myArea( 0 ),
+  myWorkWin( 0 ),
+  myWorkArea( 0 )
 {
-  splitterInfo( mySplit, outParameters );
-  return (*this);
-}
+  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() ) );
 
-/*!
-  Constructor
-*/
-QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
-: QWidget( parent )
-{
   QVBoxLayout* base = new QVBoxLayout( this );
+  base->setMargin( 0 );
 
-  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 );
+  mySplit = new QSplitter( this );
+  mySplit->setChildrenCollapsible( false );
+  base->addWidget( mySplit );
+}
 
-  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, SLOT( onContextMenuRequested( QPoint ) ) );
+/*!
+  \brief Destructor.
+*/
+QtxWorkstack::~QtxWorkstack()
+{
+}
 
-  updateState();
+/*!
+  \brief Get all child widgets in all workareas.
+  \return list of widgets in all workareas
+*/
+QWidgetList QtxWorkstack::windowList() const
+{
+  QList<QtxWorkstackArea*> lst;
+  areas( mySplit, lst, true );
 
-  updateActiveState();
+  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 );
+  }
 
-  qApp->installEventFilter( this );
+  return widList;
 }
 
 /*!
-  Destructor
+  \brief Get all child widgets in the active workarea.
+  \return list of widgets in active workarea
 */
-QtxWorkstackArea::~QtxWorkstackArea()
+QWidgetList QtxWorkstack::splitWindowList() const
 {
-  qApp->removeEventFilter( this );
+  return myArea ? myArea->widgetList() : QWidgetList();
 }
 
 /*!
-  \return true if area is empty
+  \brief Get active widget.
+  \return active widget
 */
-bool QtxWorkstackArea::isEmpty() const
+QWidget* QtxWorkstack::activeWindow() const
 {
-  bool res = false;
-  for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !res; ++it )
-    res = it.data().vis;
-  return !res;
+  return myWin;
 }
 
 /*!
-  Adds widget to area
-  \param wid - widget
-  \param idx - index
+  \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 QtxWorkstackArea::insertWidget( QWidget* wid, const int idx )
+void QtxWorkstack::split( const int o )
 {
-  if ( !wid )
+  QtxWorkstackArea* area = myWorkArea;
+  if ( !area )
+    area = activeArea();
+  if ( !area )
     return;
 
-  int pos = myList.find( wid );
-  if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
+  if ( area->widgetList().count() < 2 )
     return;
 
-  myList.removeRef( wid );
-  pos = idx < 0 ? myList.count() : idx;
-  myList.insert( QMIN( pos, (int)myList.count() ), wid );
-  if ( !myInfo.contains( wid ) )
-  {
-    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() );
+  QWidget* curWid = area->activeWidget();
+  if ( !curWid )
+    return;
 
-    connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
-    connect( wid, SIGNAL( destroyed() ), this, SLOT( onWidgetDestroyed() ) );
-    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* ) ) );
-  }
+  QSplitter* s = splitter( area );
+  QList<QtxWorkstackArea*> areaList;
+  areas( s, areaList );
 
-  updateState();
+  QList<QSplitter*> splitList;
+  splitters( s, splitList );
 
-  setWidgetActive( wid );
-  wid->setFocus();
-}
+  QSplitter* trg = 0;
+  if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
+    trg = s;
 
-/*!
-  Creates and shows popup menu for area
-  \param p - popup position
-*/
-void QtxWorkstackArea::onContextMenuRequested( QPoint p )
-{
-  const QtxWorkstackTabBar* bar = ::qt_cast<const QtxWorkstackTabBar*>( sender() );
-  if ( !bar )
+  if ( !trg )
+    trg = wrapSplitter( area );
+
+  if ( !trg )
     return;
 
-  QWidget* wid = 0;
-  QTab* tab = myBar->tabAt( tabAt( p ) );
-  if ( tab )
-    wid = widget( tab->identifier() );
+  trg->setOrientation( (Qt::Orientation)o );
 
-  emit contextMenuRequested( wid, p );
+  QtxWorkstackArea* newArea = createArea( 0 );
+  trg->insertWidget( trg->indexOf( area ) + 1, newArea );
+
+  area->removeWidget( curWid );
+  newArea->insertWidget( curWid );
+
+  distributeSpace( trg );
+
+  curWid->show();
+  curWid->setFocus();
 }
 
 /*!
-  SLOT: called when widget added to area is destroyed, removes widget from area
+  \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 QtxWorkstackArea::onWidgetDestroyed()
+void QtxWorkstack::Split( QWidget* wid, const Qt::Orientation o, const SplitType type )
 {
-  if ( sender() )
-    removeWidget( (QWidget*)sender(), false );
+  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;
+
+  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 );
+      }
+    }
+    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 );
+      }
+    }
+    break;
+  case SplitMove:
+    area->removeWidget( wid );
+    newArea->insertWidget( wid );
+    break;
+  }
+
+  distributeSpace( trg );
 }
 
 /*!
-  Removes widget from area
-  \param wid - widget
-  \param del - auto deleting
+ \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 QtxWorkstackArea::removeWidget( QWidget* wid, const bool del )
+void QtxWorkstack::Attract( QWidget* wid1, QWidget* wid2, const bool all )
 {
-  if ( !myList.contains( wid ) )
+  if ( !wid1 || !wid2 )
     return;
 
-  if ( myBar->tab( widgetId( wid ) ) )
-    myBar->removeTab( myBar->tab( widgetId( wid ) ) );
-  myStack->removeWidget( child( wid ) );
+  // 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;
 
-  myList.remove( wid );
-  myInfo.remove( wid );
-  myChild.remove( wid );
+    if ( (*it)->contains( wid2 ) )
+      area2 = *it;
+  }
+
+  if ( !area1 || !area2 )
+    return;
 
-  if( del )
+  QWidget* curWid = area1->activeWidget();
+  if ( !curWid )
+    return;
+
+  if ( area1 == area2 )
   {
-    delete child( wid );
-    if( myList.isEmpty() )
-      delete this;
+    if ( all )
+    {
+      // Set wid1 at first position, wid2 at second
+      area1->insertWidget( wid1 );
+      area1->insertWidget( wid2, 1 );
+    }
     else
-      updateState();
+    {
+      // 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 );
+    }
   }
   else
-    updateState();
-}
-
-/*!
-  \return list of visible widgets
-*/
-QWidgetList QtxWorkstackArea::widgetList() const
-{
-  QWidgetList lst;
-  for ( QWidgetListIt it( myList ); it.current(); ++it )
   {
-    if ( widgetVisibility( it.current() ) )
-      lst.append( it.current() );
+    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 );
+      }
+    }
+    else
+    {
+      // Set wid2 right after wid1
+      area2->removeWidget( wid2 );
+      area1->insertWidget( wid2, wid1_ind + 1 );
+    }
   }
-  return lst;
-}
 
-/*!
-  \return active widget
-*/
-QWidget* QtxWorkstackArea::activeWidget() const
-{
-  return widget( myBar->currentTab() );
+  area1->setActiveWidget( curWid );
 }
 
 /*!
-  Sets widget as active
-  \param wid - widget
+  \brief Calculate sizes of the splitter widget for the workarea.
+  \internal
 */
-void QtxWorkstackArea::setActiveWidget( QWidget* wid )
+static void setSizes (QIntList& szList, const int item_ind,
+                      const int new_near, const int new_this, const int new_farr)
 {
-  myBar->setCurrentTab( widgetId( wid ) );
+  // 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;
+  }
 }
 
 /*!
-  \return true if area contains widget
-  \param wid - widget
-*/
-bool QtxWorkstackArea::contains( QWidget* wid ) const
-{
-  return myList.contains( wid );
-}
+  \brief Set position of the widget relatively to its parent splitter.
 
-/*!
-  Shows area
+  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 QtxWorkstackArea::show()
+void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
 {
-  QMap<QWidget*, bool> map;
-  for ( QWidgetListIt it( myList ); it.current(); ++it )
+  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 )
   {
-    map.insert( it.current(), isBlocked( it.current() ) );
-    setBlocked( it.current(), true );
+    if ( *ito == area )
+      isFound = true;
   }
 
-  QWidget::show();
+  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();
 
-  for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
-    setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
+  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 );
 }
 
 /*!
-  Hides area
+  \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 QtxWorkstackArea::hide()
+void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
+                                        const double position )
 {
-  QMap<QWidget*, bool> map;
-  for ( QWidgetListIt it( myList ); it.current(); ++it )
+  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 )
   {
-    map.insert( it.current(), isBlocked( it.current() ) );
-    setBlocked( it.current(), true );
+    // impossible to set required position
   }
+}
 
-  QWidget::hide();
+/*!
+  \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;
 
-  for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
-    setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
+  myActionsMap[id]->setShortcut( accel );
 }
 
 /*!
-  \return true if area is active
+  \brief Get the action's accelerator key-combination.
+  \param id action ID
+  \return action accelerator
 */
-bool QtxWorkstackArea::isActive() const
+int QtxWorkstack::accel( const int id ) const
 {
-  QtxWorkstack* ws = workstack();
-  if ( !ws )
-    return false;
+  int res = 0;
+  if ( myActionsMap.contains( id ) )
+    res = myActionsMap[id]->shortcut();
+  return res;
+}
 
-  return ws->activeArea() == this;
+/*!
+  \brief Check if the action is enabled.
+  \param id action ID
+  \return \c true if action is enabled
+*/
+bool QtxWorkstack::isActionEnabled( const int id ) const
+{
+  bool res = false;
+  if ( myActionsMap.contains( id ) )
+    res = myActionsMap[id]->isEnabled();
+  return res;
 }
 
 /*!
-  Update active state of tab bar
+  \brief Enable/disable action.
+  \param id action ID
+  \param on if \c true, enable the action, else disable it
 */
-void QtxWorkstackArea::updateActiveState()
+void QtxWorkstack::setActionEnabled( const int id, const bool on )
 {
-  myBar->setActive( isActive() );
+  if ( myActionsMap.contains( id ) )
+    myActionsMap[id]->setEnabled( on );
 }
 
 /*!
-  \return corresponding workstack
+  \brief Calculate sizes of the splitter widget for the workarea.
+  \internal
 */
-QtxWorkstack* QtxWorkstackArea::workstack() const
+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)
 {
-  QtxWorkstack* ws = 0;
-  QWidget* wid = parentWidget();
-  while ( wid && !ws )
-  {
-    if ( wid->inherits( "QtxWorkstack" ) )
-      ws = (QtxWorkstack*)wid;
-    wid = wid->parentWidget();
+  if (item_ind == 0) { // cannot move in this splitter
+    return (need_pos - splitter_pos);
   }
-  return ws;
+
+  int delta = 0;
+  int new_prev = 0;
+  int new_this = szList[item_ind];
+  int new_next = 0;
+
+  bool isToCheck = false;
+
+  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
+      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;
+  }
+
+  setSizes (szList, item_ind, new_prev, new_this, new_next);
+  return delta;
 }
 
 /*!
-  Custom event filter
+  \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
 */
-bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
+int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
+                               const int need_pos, const int splitter_pos )
 {
-  if ( o->isWidgetType() )
+  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 )
   {
-    QWidget* wid = (QWidget*)o;
-    if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
+    QtxWorkstackArea* area = ::qobject_cast<QtxWorkstackArea*>( *it );
+    if ( area )
     {
-      bool ok = false;
-      while ( !ok && wid && wid != myClose )
+      if ( area->contains( wid ) )
       {
-        ok = wid == this;
-        wid = wid->parentWidget();
+        item_ind = cur_ind;
+        isBottom = true;
+        isFound = true;
       }
-      if ( ok )
-        QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
+      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++;
     }
   }
-  return false;
-}
 
-/*!
-  \return rectangle of area in order to draw drop rectangle on area
-*/
-QRect QtxWorkstackArea::floatRect() const
-{
-  QRect r = myStack->geometry();
-  return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
-}
+  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;
 
-/*!
-  \return rectangle of tab in order to draw drop rectangle on tab
-  \param idx - tab index
-*/
-QRect QtxWorkstackArea::floatTab( const int idx ) const
-{
-  return myBar->tabRect( idx );
-}
+        // this can be if corresponding workarea is first in sub-splitter
+        // or sub-splitter has another orientation
 
-/*!
-  \return tab covering point 
-  \param p - point
-*/
-int QtxWorkstackArea::tabAt( const QPoint& p ) const
-{
-  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;
-}
+        // Resize ones again to reach precize position <->
+        int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
 
-/*!
-  Event handler for custom events
-*/
-void QtxWorkstackArea::customEvent( QCustomEvent* e )
-{
-  switch ( e->type() )
-  {
-  case ActivateWidget:
-    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();
+        // 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;
       }
     }
-    break;
-  case RemoveWidget:
-    removeWidget( (QWidget*)e->data() );
-    break;
+  } else {
+    return setPosition(wid, sub_split, o, need_pos, splitter_pos);
   }
-}
-
-/*!
-  Custom focus in event handler
-*/
-void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
-{
-  QWidget::focusInEvent( e );
 
-  emit activated( activeWidget() );
+  return 0;
 }
 
 /*!
-  Custom mouse press event handler
+  \brief Redistribute space among widgets equally.
+  \param split splitter
 */
-void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
+void QtxWorkstack::distributeSpace( QSplitter* split ) const
 {
-  QWidget::mousePressEvent( e );
+  if ( !split )
+    return;
 
-  emit activated( activeWidget() );
+  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 );
 }
 
 /*!
-  SLOT: called if button close is pressed
+  \brief Split widgets vertically.
 */
-void QtxWorkstackArea::onClose()
+void QtxWorkstack::splitVertical()
 {
-  QWidget* wid = activeWidget();
-  if ( wid )
-    wid->close();
+  split( Qt::Horizontal );
 }
 
 /*!
-  SLOT: called if tab page is selected
+  \brief Split widgets horizontally.
 */
-void QtxWorkstackArea::onSelected( int id )
+void QtxWorkstack::splitHorizontal()
 {
-  updateCurrent();
-
-  emit activated( activeWidget() );
+  split( Qt::Vertical );
 }
 
 /*!
-  SLOT: called if active tab page is dragged
+  \brief Called when user activates "Rename" menu item.
+
+  Changes widget title.
 */
-void QtxWorkstackArea::onDragActiveTab()
+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 );
 }
 
 /*!
-  SLOT: called on child is destroyed, removes from area
+  \brief Wrap area into the new splitter.
+  \param workarea
+  \return new splitter
 */
-void QtxWorkstackArea::onChildDestroyed( QObject* obj )
+QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
 {
-  QtxWorkstackChild* child = (QtxWorkstackChild*)obj;
-  myStack->removeWidget( child );
-
-  QWidget* wid = 0;
-  for ( ChildMap::ConstIterator it = myChild.begin(); it != myChild.end() && !wid; ++it )
-  {
-    if ( it.data() == child )
-      wid = it.key();
-  }
-
-  myChild.remove( wid );
-
-  QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)RemoveWidget, wid ) );
-}
+  if ( !area )
+    return 0;
 
-/*!
-  SLOT: called on child is shown
-*/
-void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
-{
-  setWidgetShown( c->widget(), true );
-}
+  QSplitter* pSplit = splitter( area );
+  if ( !pSplit )
+    return 0;
 
-/*!
-  SLOT: called on child is hidden
-*/
-void QtxWorkstackArea::onChildHided( QtxWorkstackChild* c )
-{
-  setWidgetShown( c->widget(), false );
-}
+  bool upd = pSplit->updatesEnabled();
+  pSplit->setUpdatesEnabled( false );
 
-/*!
-  SLOT: called on child is activated
-*/
-void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
-{
-  setWidgetActive( c->widget() );
-}
+  QIntList szList = pSplit->sizes();
 
-/*!
-  SLOT: called on child caption is changed
-*/
-void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
-{
-  updateTab( c->widget() );
-}
+  QSplitter* wrap = new QSplitter( 0 );
+  wrap->setChildrenCollapsible( false );
+  pSplit->insertWidget( pSplit->indexOf( area ) + 1, wrap );
+  wrap->setVisible( true );
+  wrap->addWidget( area );
 
-/*!
-  Raises widget when active tab is changed
-*/
-void QtxWorkstackArea::updateCurrent()
-{
-  QMap<QWidget*, bool> map;
-  for ( QWidgetListIt it( myList ); it.current(); ++it )
-  {
-    map.insert( it.current(), isBlocked( it.current() ) );
-    setBlocked( it.current(), true );
-  }
+  pSplit->setSizes( szList );
 
-  myStack->raiseWidget( myBar->currentTab() );
+  pSplit->setUpdatesEnabled( upd );
 
-  for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
-    setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
+  return wrap;
 }
 
 /*!
-  Updates tab
-  \param wid - tab widget
+  \brief Reparent and add widget.
+  \param wid widget
+  \param pWid parent widget
+  \param after widget after which \a wid should be added
 */
-void QtxWorkstackArea::updateTab( QWidget* wid )
+void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
 {
-  QTab* tab = myBar->tab( widgetId( wid ) );
-  if ( !tab )
+  if ( !wid || !pWid )
     return;
 
-  QIconSet icoSet;
-  if ( wid->icon() )
+  QWidgetList moveList;
+  const QObjectList& lst = pWid->children();
+  bool found = false;
+  for ( QObjectList::const_iterator it = lst.begin(); it != lst.end(); ++it )
   {
-    QPixmap pix = *wid->icon();
-    pix.convertFromImage( pix.convertToImage().smoothScale( pix.width(), 16, QImage::ScaleMin ) );
-    icoSet = QIconSet( pix );
+    if ( found && ( (*it)->inherits( "QSplitter" ) ||
+                    (*it)->inherits( "QtxWorkstackArea" ) ) )
+      moveList.append( (QWidget*)(*it) );
+    if ( *it == after )
+      found = true;
   }
 
-  tab->setIconSet( icoSet );
-  tab->setText( wid->caption() );
-}
-
-/*!
-  \return widget
-  \param id - widget id
-*/
-QWidget* QtxWorkstackArea::widget( const int id ) const
-{
-  QWidget* wid = 0;
-  for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !wid; ++it )
+  QMap<QWidget*, bool> map;
+  for ( QWidgetList::iterator it = moveList.begin(); it != moveList.end(); ++it )
   {
-    if ( it.data().id == id )
-      wid = it.key();
+    map.insert( *it, (*it)->isVisibleTo( (*it)->parentWidget() ) );
+    (*it)->setParent( 0 );
+    (*it)->hide();
   }
-  return wid;
-}
 
-/*!
-  \return widget id
-  \param wid - widget
-*/
-int QtxWorkstackArea::widgetId( QWidget* wid ) const
-{
-  int id = -1;
-  if ( myInfo.contains( wid ) )
-    id = myInfo[wid].id;
-  return id;
-}
+  wid->setParent( pWid );
 
-/*!
-  \return true if widget is visible
-  \param wid - widget
-*/
-bool QtxWorkstackArea::widgetVisibility( QWidget* wid ) const
-{
-  bool res = false;
-  if ( myInfo.contains( wid ) )
-    res = myInfo[wid].vis;
-  return res;
+  for ( QWidgetList::iterator itr = moveList.begin(); itr != moveList.end(); ++itr )
+  {
+    (*itr)->setParent( pWid );
+    (*itr)->setShown( map.contains( *itr ) ? map[*itr] : false );
+  }
 }
 
 /*!
-  Sets widget as active
-  \param wid - widget
+  \brief Close active window.
 */
-void QtxWorkstackArea::setWidgetActive( QWidget* wid )
+void QtxWorkstack::onCloseWindow()
 {
-  int id = widgetId( wid );
-  if ( id < 0 )
-    return;
-
-  myBar->setCurrentTab( id );
+  if ( myWorkWin )
+    myWorkWin->close();
+  else if( activeWindow() )
+    activeWindow()->close();
 }
 
 /*!
-  Shows/hides widget
-  \param wid - widget
-  \param on - new shown state
-*/
-void QtxWorkstackArea::setWidgetShown( QWidget* wid, const bool on )
-{
-  if ( isBlocked( wid ) || !myInfo.contains( wid ) || myInfo[wid].vis == on )
-    return;
+  \brief Called when workarea is destroyed.
 
-  myInfo[wid].vis = on;
-  updateState();
-}
+  Set input focus to the neighbour area.
 
-/*!
-  Update
+  \param obj workarea being destroyed
 */
-void QtxWorkstackArea::updateState()
+void QtxWorkstack::onDestroyed( QObject* obj )
 {
-  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 );
-
-    if ( id < 0 )
-      continue;
-
-    bool vis = widgetVisibility( wid );
+  QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
 
-    if ( myBar->tab( id ) && ( !vis || myBar->indexOf( id ) != idx ) )
-      myBar->removeTab( myBar->tab( id ) );
+  if ( area == myArea )
+    myArea = 0;
 
-    if ( !myBar->tab( id ) && vis )
-    {
-      QTab* tab = new QTab( wid->caption() );
-      myBar->insertTab( tab, idx );
-      tab->setIdentifier( id );
-    }
+  if ( !myArea )
+  {
+    QtxWorkstackArea* cur = neighbourArea( area );
+    if ( cur )
+      cur->setFocus();
+  }
 
-    updateTab( wid );
+  QApplication::postEvent( this, new QEvent( QEvent::User ) );
+}
 
-    bool block = isBlocked( wid );
-    setBlocked( wid, true );
+/*!
+  \brief Called on window activating.
+  \param area workarea being activated (not used)
+*/
+void QtxWorkstack::onWindowActivated( QWidget* /*area*/ )
+{
+  const QObject* obj = sender();
+  if ( !obj->inherits( "QtxWorkstackArea" ) )
+    return;
 
-    QtxWorkstackChild* cont = child( wid );
+  setActiveArea( (QtxWorkstackArea*)obj );
+}
 
-    if ( !vis )
-      myStack->removeWidget( cont );
-    else if ( !myStack->widget( id ) )
-      myStack->addWidget( cont, id );
+/*!
+  \brief Called on window deactivating.
+  \param area workarea being deactivated
+*/
+void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
+{
+  if ( myArea != area )
+    return;
 
-    if ( vis )
-      idx++;
+  QList<QtxWorkstackArea*> lst;
+  areas( mySplit, lst, true );
 
-    setBlocked( wid, block );
-  }
+  int idx = lst.indexOf( area );
+  if ( idx == -1 )
+    return;
 
-  int curId = widgetId( prev );
-  if ( !myBar->tab( curId ) )
-  {
-    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 );
-    }
+  myWin = 0;
+  myArea = 0;
 
-    for ( int j = pos + 1; j < (int)myList.count() && !wid; j++ )
-    {
-      if ( widgetVisibility( myList.at( j ) ) )
-        wid = myList.at( j );
-    }
+  QtxWorkstackArea* newArea = neighbourArea( area );
+  if ( newArea && newArea->activeWidget() )
+    newArea->activeWidget()->setFocus();
 
-    if ( wid )
-      curId = widgetId( wid );
-  }
+  QApplication::postEvent( this, new QEvent( QEvent::User ) );
+}
 
-  myBar->setCurrentTab( curId );
+/*!
+  \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();
 
-  myBar->blockSignals( block );
+  if ( !anArea )
+    return;
 
-  updateCurrent();
+  QWidgetList lst = anArea->widgetList();
+  if ( lst.isEmpty() )
+    return;
 
-  myBar->setUpdatesEnabled( updBar );
-  myStack->setUpdatesEnabled( updStk );
-  if ( updBar )
-    myBar->update();
-  if ( updStk )
-    myStack->update();
+  myWorkWin = w;
+  myWorkArea = anArea;
 
-  QResizeEvent re( myBar->size(), myBar->size() );
-  QApplication::sendEvent( myBar, &re );
+  QMenu* pm = new QMenu();
 
-  if ( isEmpty() )
+  if ( lst.count() > 1 )
   {
-    hide();
-    emit deactivated( this );
+    if ( myActionsMap[SplitVertical]->isEnabled() )
+      pm->addAction( myActionsMap[SplitVertical] );
+    if ( myActionsMap[SplitHorizontal]->isEnabled() )
+      pm->addAction( myActionsMap[SplitHorizontal] );
+    pm->addSeparator();
   }
-  else
+
+  if ( w )
   {
-    show();
-    if ( prev != activeWidget() )
-      emit activated( activeWidget() );
+    if ( myActionsMap[Close]->isEnabled() )
+      pm->addAction( myActionsMap[Close] );
+    if ( myActionsMap[Rename]->isEnabled() )
+      pm->addAction( myActionsMap[Rename] );
   }
-}
 
-/*!
-  \return first unshared widget id
-*/
-int QtxWorkstackArea::generateId() const
-{
-  QMap<int, int> map;
+  Qtx::simplifySeparators( pm );
 
-  for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end(); ++it )
-    map.insert( it.data().id, 0 );
+  if ( !pm->actions().isEmpty() )
+    pm->exec( p );
 
-  int id = 0;
-  while ( map.contains( id ) )
-    id++;
+  delete pm;
 
-  return id;
+  myWorkWin = 0;
+  myWorkArea = 0;
 }
 
 /*!
-  \return true if widget is blocked
-  \param wid - widget
+  \brief Add child widget.
+  \param w widget
+  \param f widget flags
+  \return child widget container
 */
-bool QtxWorkstackArea::isBlocked( QWidget* wid ) const
+QWidget* QtxWorkstack::addWindow( QWidget* w, Qt::WindowFlags f )
 {
-  return myBlock.contains( wid );
+  if ( !w )
+    return 0;
+
+  return targetArea()->insertWidget( w, -1, f );
 }
 
 /*!
-  Blocks widget
-  \param wid - widget
-  \param on - new blocked state
+  \brief Handle custom events.
+  \param e custom event (not used)
 */
-void QtxWorkstackArea::setBlocked( QWidget* wid, const bool on )
+void QtxWorkstack::customEvent( QEvent* /*e*/ )
 {
-  if ( on )
-    myBlock.insert( wid, 0 );
-  else
-    myBlock.remove( wid );
+  updateState();
 }
 
 /*!
-  \return child corresponding to widget
-  \param wid - widget
+  \brief Get splitter corresponding to the workarea.
+  \param workarea
+  \return splitter corresponding to the workarea
 */
-QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
+QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
 {
-  QtxWorkstackChild* res = 0;
-  if ( myChild.contains( wid ) )
-    res = myChild[wid];
-  return res;
+  if ( !area )
+    return 0;
+
+  QSplitter* split = 0;
+
+  QWidget* wid = area->parentWidget();
+  if ( wid && wid->inherits( "QSplitter" ) )
+    split = (QSplitter*)wid;
+
+  return split;
 }
 
 /*!
-  Constructor
+  \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
 */
-QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent )
-: QHBox( parent ),
-myWidget( wid )
+void QtxWorkstack::splitters( QSplitter* split, QList<QSplitter*>& splitList, const bool rec ) const
 {
-  myWidget->reparent( this, QPoint( 0, 0 ), myWidget->isVisibleTo( myWidget->parentWidget() ) );
-  myWidget->installEventFilter( this );
+  if ( !split )
+    return;
 
-  connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
+  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 );
+  }
 }
 
 /*!
-  Destructor
+  \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
 */
-QtxWorkstackChild::~QtxWorkstackChild()
+void QtxWorkstack::areas( QSplitter* split, QList<QtxWorkstackArea*>& areaList, const bool rec ) const
 {
-  qApp->removeEventFilter( this );
-
-  if ( !widget() )
+  if ( !split )
     return;
 
-  widget()->removeEventFilter( this );
-  widget()->reparent( 0, QPoint( 0, 0 ), false );
-  disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
+  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 );
+  }
 }
 
 /*!
-  \return corresponding widget
+  \brief Get active workarea.
+  \return active workarea
 */
-QWidget* QtxWorkstackChild::widget() const
+QtxWorkstackArea* QtxWorkstack::activeArea() const
 {
-  return myWidget;
+  return myArea;
 }
 
 /*!
-  Custom event filter
+  \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.
+
+  \return workarea
 */
-bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
+QtxWorkstackArea* QtxWorkstack::targetArea()
 {
-  if ( o->isWidgetType() )
+  QtxWorkstackArea* area = activeArea();
+  if ( !area )
+    area = currentArea();
+  if ( !area )
   {
-    if ( e->type() == QEvent::CaptionChange || e->type() == QEvent::IconChange )
-      emit captionChanged( this );
-
-    if ( !e->spontaneous() && ( e->type() == QEvent::Show || e->type() == QEvent::ShowToParent ) )
-      emit shown( this );
+    QList<QtxWorkstackArea*> lst;
+    areas( mySplit, lst );
+    if ( !lst.isEmpty() )
+      area = lst.first();
+  }
 
-    if ( !e->spontaneous() && ( e->type() == QEvent::Hide || e->type() == QEvent::HideToParent ) )
-      emit hided( this );
+  if ( !area )
+    area = createArea( mySplit );
 
-    if ( e->type() == QEvent::FocusIn )
-      emit activated( this );
-  }
-  return QHBox::eventFilter( o, e );
+  return area;
 }
 
 /*!
-  SLOT: called on object is destroyed
-*/
-void QtxWorkstackChild::onDestroyed( QObject* obj )
-{
-  if ( obj != widget() )
-    return;
+  \brief Get current workarea.
 
-  myWidget = 0;
-  deleteLater();
-}
+  Current workarea is that one which has input focus.
 
-/*!
-  Custom child event handler
+  \return current area
 */
-void QtxWorkstackChild::childEvent( QChildEvent* e )
+QtxWorkstackArea* QtxWorkstack::currentArea() const
 {
-  if ( e->type() == QEvent::ChildRemoved && e->child() == widget() )
+  QtxWorkstackArea* area = 0;
+  QWidget* wid = focusWidget();
+  while ( wid && !area )
   {
-    myWidget = 0;
-    deleteLater();
+    if ( wid->inherits( "QtxWorkstackArea" ) )
+      area = (QtxWorkstackArea*)wid;
+    wid = wid->parentWidget();
   }
-  QHBox::childEvent( e );
+
+  return area;
 }
 
 /*!
-  Constructor
+  \brief Create new workarea.
+  \param parent parent widget
+  \return created workarea
 */
-QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
-: QTabBar( parent ),
-myId( -1 )
+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;
 }
 
 /*!
-  Destructor
+  \brief Set active workarea.
+  \param workarea
 */
-QtxWorkstackTabBar::~QtxWorkstackTabBar()
+void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
 {
+  QWidget* oldCur = myWin;
+
+  QtxWorkstackArea* oldArea = myArea;
+
+  myArea = area;
+
+  if ( myArea != oldArea )
+  {
+    if ( oldArea )
+      oldArea->updateActiveState();
+    if ( myArea )
+      myArea->updateActiveState();
+  }
+
+  if ( myArea )
+    myWin = myArea->activeWidget();
+
+  if ( myWin && oldCur != myWin )
+    emit windowActivated( myWin );
 }
 
 /*!
-  Sets tab bar as active or inactive
-  \param on - new active state
+  \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)
 */
-void QtxWorkstackTabBar::setActive( const bool on )
+QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
 {
-  QFont aFont = font();
-  aFont.setUnderline( on );
-  QColorGroup aColGrp;
-  QPalette aPal = palette();
-  if ( !on ) {
-    aPal.setColor( QColorGroup::HighlightedText, aColGrp.foreground() );
-    aPal.setColor( QColorGroup::Highlight, colorGroup().dark().light( DARK_COLOR_LIGHT ) );
-    setPalette( aPal );
+  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-- )
+  {
+    if ( !lst.at( i )->isEmpty() )
+      na = lst.at( i );
   }
-  else {
-    aPal.setColor( QColorGroup::HighlightedText, aColGrp.highlightedText() );
-    aPal.setColor( QColorGroup::Highlight, aColGrp.highlight() );
-    unsetPalette();
+
+  for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
+  {
+    if ( !lst.at( j )->isEmpty() )
+        na = lst.at( j );
   }
-  setFont( aFont );
+  return na;
+}
 
-  update();
+/*!
+  \brief Get workarea covering point.
+  \return workarea
+  \param p point
+*/
+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;
 }
 
 /*!
-  \return tab rectangle
-  \param idx - tab index
+  \brief Update internal state.
 */
-QRect QtxWorkstackTabBar::tabRect( const int idx ) const
+void QtxWorkstack::updateState()
 {
-  QRect r;
-  QTab* t = tabAt( idx );
-  if ( t )
-  {
-    r = t->rect();
-    r.setLeft( QMAX( r.left(), 0 ) );
-
-    int x1 = tabAt( 0 )->rect().left();
-    int x2 = tabAt( count() - 1 )->rect().right();
-
-    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
-
-    int limit = width() - bw;
-    r.setRight( QMIN( r.right(), limit ) );
-
-    r = QRect( mapToGlobal( r.topLeft() ), r.size() );
-  }
-  return r;
+  updateState( mySplit );
 }
 
 /*!
-  Custom mouse move event handler
+  \brief Update splitter state.
+  \param split splitter to be updated
 */
-void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
+void QtxWorkstack::updateState( QSplitter* split )
 {
-  if ( myId != -1 && !tab( myId )->rect().contains( e->pos() ) )
+  QList<QSplitter*> recList;
+  splitters( split, recList, false );
+  for ( QList<QSplitter*>::iterator itr = recList.begin(); itr != recList.end(); ++itr )
+    updateState( *itr );
+
+  QList<QSplitter*> splitList;
+  splitters( split, splitList, false );
+
+  QList<QtxWorkstackArea*> areaList;
+  areas( split, areaList, false );
+
+  bool vis = false;
+  for ( QList<QtxWorkstackArea*>::iterator it = areaList.begin(); it != areaList.end(); ++it )
   {
-    myId = -1;
-    emit dragActiveTab();
+    if ( (*it)->isEmpty() )
+      (*it)->hide();
+    else
+    {
+      (*it)->show();
+      vis = true;
+    }
   }
 
-  QTabBar::mouseMoveEvent( e );
-}
+  if ( split == mySplit )
+    return;
 
-/*!
-  Custom mouse press event handler
-*/
-void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
-{
-  QTabBar::mousePressEvent( e );
+  for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end() && !vis; ++iter )
+    vis = (*iter)->isVisibleTo( (*iter)->parentWidget() );
 
-  if ( e->button() == LeftButton )
-    myId = currentTab();
+  if ( areaList.isEmpty() && splitList.isEmpty() )
+    delete split;
+  else if ( vis )
+    split->show();
+  else
+    split->hide();
 }
 
 /*!
-  Custom mouse release event handler
+  \brief Get splitter info (for debug purposes)
+  \param split splitter
+  \param info string to be filled with splitter data.
 */
-void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
+void QtxWorkstack::splitterInfo( QSplitter* split, QString& info ) const
 {
-  QTabBar::mouseReleaseEvent( e );
+  if ( !split )
+    return;
 
-  myId = -1;
+  const QObjectList& objs = split->children();
 
-  if ( e->button() == RightButton )
-    emit contextMenuRequested( e->globalPos() );
+  QString sizesStr;
+  QList<int> sizes = split->sizes();
+  for ( QList<int>::iterator sIt = sizes.begin(); sIt != sizes.end(); ++sIt )
+  {
+    if ( *sIt > 1 ) // size 1 pixel usually means empty Workstack area, which will NOT be re-created,
+           sizesStr += QString( ":%1" ).arg( *sIt );  // so we don't need to store its size
+  }
+
+  if ( !sizesStr.isEmpty() ) // cut the first ':'
+    sizesStr = sizesStr.right( sizesStr.length() - 1 );
+
+  info += QString( "(splitter orientation=%1 sizes=%3 " ).arg( split->orientation() ).arg( sizesStr );
+
+  for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
+  {
+    if ( (*it)->inherits( "QSplitter" ) )
+           splitterInfo( (QSplitter*)*it, info );
+    else if ( (*it)->inherits( "QtxWorkstackArea" ) )
+    {
+           QtxWorkstackArea* area = (QtxWorkstackArea*)*it;
+           if ( area->isEmpty() )
+             continue;
+           info += QString( "(views active='%1'" ).arg( area->activeWidget()->objectName() );
+           QWidgetList views = area->widgetList();
+      for ( QWidgetList::iterator wIt = views.begin(); wIt != views.end(); ++wIt )
+             info += QString( " '%1'" ).arg( (*wIt)->objectName() );
+           info += ')';
+    }
+  }
+
+  info += ')';
+  printf( (const char*)QString( info + '\n' ).toLatin1() );
 }
 
 /*!
-  Custom context menu event handler
+  \brief Remove round brackets symbols from the string.
+  \internal
+  \param parameters string to be processed
 */
-void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
+static void cutBrackets( QString& parameters )
 {
-  if ( e->reason() != QContextMenuEvent::Mouse )
-    emit contextMenuRequested( e->globalPos() );
+  QChar c1 = parameters[0];
+  QChar c2 = parameters[int(parameters.length()-1)];
+  if ( !parameters.isEmpty() && c1 == '(' && c2 == ')' )
+    parameters = parameters.mid( 1, parameters.length()-2 );
 }
 
 /*!
-  Draws label of tab bar
+  \brief Parse string to get some parameter value.
+  \internal
+
+  String \a str can contain the parameters description of kind "<param>=<value> ...".
+  For example:
+  \code
+  QString s = "splitter orientation=0 children=2 sizes=332:478";
+  QString orient_val = getValue( s, "children" ); // orient_val contains "2"
+  QString size_val   = getValue( s, "sizes" );    // val contains "332:478"
+  \endcode
+
+  \param str string to be processed
+  \param valName parameter name
+  \return parameter value (or null QStrinhg if parameter is not found)
 */
-void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
+static QString getValue( const QString& str, const QString& valName )
 {
-  if ( currentTab() != t->identifier() )
+  int i = str.indexOf( valName );
+  if ( i != -1 )
   {
-    QFont fnt = p->font();
-    fnt.setUnderline( false );
-    p->setFont( fnt );
+    int equal_i = str.indexOf( '=', i );
+    if ( equal_i != -1 )
+    {
+      int space_i = str.indexOf( ' ', ++equal_i );
+      if ( space_i != -1 )
+             return str.mid( equal_i, space_i - equal_i );
+    }
   }
-  QTabBar::paintLabel( p, br, t, has_focus );
+  return QString();
 }
 
 /*!
-  Constructor
+  \brief Check format of splitter parameters string.
+  \internal
+  \param parameters splitter parameters description
+  \return \c true on success and \c false on error
 */
-QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
-: QObject( 0 ),
-myWS( ws ),
-myTab( -1 ),
-myArea( 0 ),
-myPainter( 0 ),
-myChild( child )
+static bool checkFormat( const QString& parameters )
 {
-  qApp->installEventFilter( this );
+  QString params( parameters );
+  // 1. begins and ends with brackets
+  QChar c1 = params[0];
+  QChar c2 = params[int(params.length()-1)];
+  bool ok = ( c1 == '(' && c2 == ')' );
+  if ( !ok ) return ok;
+  ::cutBrackets( params );
+  // 2. has splitter word
+  ok = ( params.left( 8 ) == "splitter" );
+  if ( !ok ) return ok;
+  // 3. has children?  = '(' is found
+  int i = params.indexOf( '(' );
+  ok = i != -1;
+  if ( !ok ) return ok;
+  params = params.left( i ); // cut all children, they will be checked later
+  // 4. has orientation word and correct value
+  ::getValue( params, "orientation" ).toInt( &ok );
+  if ( !ok ) return ok;
+  // 5. has sizes word and values
+  ok = ! ::getValue( params, "sizes" ).isEmpty();
+  if ( !ok ) return ok;
+  // 6. check children -> number of '(' == number of ')' in original string
+  ok = ( parameters.contains( '(' ) == parameters.contains( ')' ) );
+  return ok;
 }
 
 /*!
-  Destructor
+  \brief Get splitter's children descriptions from the string.
+  \internal
+  
+  Child widgets descriptions are separated by '(' and ')' symbols.
+
+  \param str string to be processed
+  \return child widgets descriptions
 */
-QtxWorkstackDrag::~QtxWorkstackDrag()
+static QStringList getChildren( const QString& str )
 {
-  qApp->removeEventFilter( this );
+  QStringList lst;
+  if ( !str.startsWith( "(" ) )
+    return lst;
 
-  endDrawRect();
+  int i = 1,
+  nOpen = 1, // count brackets: '(' increments nOpen, ')' decrements
+  start = 0;
+  while ( i < (int)str.length() )
+  {
+    if ( str[i] == '(' )
+    {
+      nOpen++;
+      if ( nOpen == 1 )
+             start = i;
+    }
+    else if ( str[i] == ')' )
+    {
+      nOpen--;
+      if ( nOpen == 0 )
+             lst.append( str.mid( start, i-start+1 ) );
+    }
+    i++;
+  }
+
+  return lst;
 }
 
 /*!
-  Custom event filter
+  \brief Get view name by index.
+  \internal
+
+  Example:
+  \code
+  QString s  = "views active='AnotherView' 'GLView' 'AnotherView'";
+  QString a0 = getViewName( s, 0 ); // --> a0 contains "GLView"
+  QString a1 = getViewName( s, 1 ); // --> a1 contains "AnotherView"
+  \endcode
+
+  \param str string to be processed
+  \param i index
+  \return view name
 */
-bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
+static QString getViewName( const QString& str, int i )
 {
-  switch ( e->type() )
+  QRegExp exp( "\\s'\\w+'" );
+  int start = 0; // start index of view name in the string
+  int num = 0 ; // index of found match
+  while ( ( start = exp.indexIn( str, start ) ) != -1 && num < i )
   {
-  case QEvent::MouseMove:
-    updateTarget( ((QMouseEvent*)e)->globalPos() );
-    break;
-  case QEvent::MouseButtonRelease:
-    drawRect();
-    endDrawRect();
-    dropWidget();
-    deleteLater();
-    break;
-  default:
-    return false;
+    start += exp.matchedLength();
+    num ++;
   }
-  return true;
-}
+  if ( start != -1 )      // +2 and -3 avoid starting space and starting and ending ' symbols
+    return str.mid( start + 2, exp.matchedLength() - 3 );
 
-/*!
-  Updates internal field with widget-target for dropping
-  \param p - current point of dragging
-*/
-void QtxWorkstackDrag::updateTarget( const QPoint& p )
-{
-  int tab = -1;
-  QtxWorkstackArea* area = detectTarget( p, tab );
-  setTarget( area, tab );
+  return QString();
 }
 
 /*!
-  \return target area for dropping by point
-  \param p - current point of dragging
-  \param tab - index of tab to dropping
+  \brief Get child widget with specified name.
+  \internal
+  \param parent parent widget
+  \param aName child widget name
+  \return child widget or 0 if not found
 */
-QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
+static QWidget* getView( const QWidget* parent, const QString& aName )
 {
-  if ( p.isNull() )
-    return 0;
-
-  QtxWorkstackArea* area = myWS->areaAt( p );
-  if ( area )
-    tab = area->tabAt( p );
-  return area;
+  QWidget* view = 0;
+  QList<QWidget*> l = qFindChildren<QWidget*>( parent->topLevelWidget(), aName );
+  if ( !l.isEmpty() )
+    view = ::qobject_cast<QWidget*>( l.first() );
+  return view;
 }
 
 /*!
-  Changes target area for dropping
-  \param area - new target area
-  \param tab - tab index
+  \brief Setup splitter according to the specified parameters string.
+  \param splitter splitter to be set up
+  \param parameters splitter parameters description
+  \param sMap map containing resulting child splitters sizes
 */
-void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
+void QtxWorkstack::setSplitter( QSplitter* splitter, const QString& parameters, QMap<QSplitter*, QList<int> >& sMap )
 {
-  if ( !area || ( myArea == area && tab == myTab ) )
+  printf( QString( parameters + '\n' ).toLatin1() );
+  if ( !::checkFormat( parameters ) ) {
+    printf( "\nInvalid format of workstack parameters.  Positions of viewers can not be restored.\n" );
     return;
+  }
 
-  startDrawRect();
+  QString params( parameters );
+  ::cutBrackets( params );
 
-  if ( myArea )
-    drawRect();
+  // get splitter sizes and store it in the map for future setting
+  QList<int> sizes;
+  QStringList sizesLst = ::getValue( params, "sizes" ).split( ':', QString::SkipEmptyParts );
+  QStringList::Iterator it;
+  for ( it = sizesLst.begin(); it != sizesLst.end(); ++it )
+    sizes.append( (*it).toInt() );
+  sMap[ splitter ] = sizes;
 
-  myTab = tab;
-  myArea = area;
+  // set orientation of splitter
+  int orient = ::getValue( params, "orientation" ).toInt();
+  splitter->setOrientation( (Qt::Orientation)orient );
 
-  if ( myArea )
-    drawRect();
-}
+  // get children
+  QString options = params.left( params.indexOf( '(' ) );
+  QString childrenStr = params.right( params.length()-options.length() );
+  QStringList children = ::getChildren( childrenStr );
 
-/*!
-  Called on widget drop, inserts dropped widget to area
-*/
-void QtxWorkstackDrag::dropWidget()
-{
-  if ( myArea )
-    myArea->insertWidget( myChild->widget(), myTab );
+  // debug output..
+  //  printf (" splitter orient=%d, sizes_count=%d, children=%d\n", orient, sizes.count(), children.count() );
+  //  for ( QStringList::Iterator tit = children.begin(); tit != children.end(); ++tit )
+  //    printf ("   |-> child = [%s]\n", (*tit).latin1() );
+
+  for ( it = children.begin(); it != children.end(); ++it )
+  {
+    if ( (*it).startsWith( "(splitter" ) )
+    {
+      QSplitter* newSplitter = new QSplitter( splitter );
+      setSplitter( newSplitter, *it, sMap );
+    }
+    else if ( (*it).startsWith( "(views" ) )
+    {
+      QtxWorkstackArea* newArea = createArea( splitter );
+      QString activeViewName = ::getValue( *it, "active" );
+      QWidget* activeView = 0;
+      activeViewName = activeViewName.mid( 1, activeViewName.length()-2 ); // chop off ' symbols
+      int i = 0;
+      QString viewName = ::getViewName( *it, i );
+      while ( !viewName.isEmpty() )
+      {
+             if ( QWidget* view = ::getView( splitter, viewName ) )
+        {
+               newArea->insertWidget( view );
+               if ( activeViewName == view->objectName() )
+                 activeView = view;
+             }
+             viewName = ::getViewName( *it, ++i );
+      }
+      if ( activeView )
+       newArea->setActiveWidget( activeView );
+    }
+  }
 }
 
 /*!
-  Draws float rect
+  \brief Restore workstack configuration from the state description string.
+  \param parameters workstack state description
+  \return reference to this workstack
 */
-void QtxWorkstackDrag::drawRect()
+QtxWorkstack& QtxWorkstack::operator<<( const QString& parameters )
 {
-  if ( !myPainter || !myArea )
-    return;
+  // clear the main splitter - remove all child splitters and empty areas from it
+  QList<QSplitter*> splitList;
+  QList<QtxWorkstackArea*> areaList;
+  splitters( mySplit, splitList, false );
+  areas( mySplit, areaList, false );
+  for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end(); ++iter )
+    delete *iter;
 
-  QRect r = myArea->floatRect();
-  int m = myPainter->pen().width();
+  for ( QList<QtxWorkstackArea*>::iterator it = areaList.begin(); it != areaList.end(); ++it )
+  {
+    if ( (*it)->isEmpty() )
+      delete *it;
+  }
 
-  r.setTop( r.top() + m + 2 );
-  r.setLeft( r.left() + m + 2 );
-  r.setRight( r.right() - m - 2 );
-  r.setBottom( r.bottom() - m - 2 );
+  // restore splitter recursively
+  QMap< QSplitter*, QList<int> > sMap;
+  setSplitter( mySplit, parameters, sMap );
 
-  myPainter->drawRect( r );
+  // now mySplit may contains empty area (where all views were located before restoring)
+  // in order setSize to work correctly we have to exclude this area
+  areaList.clear();
+  areas( mySplit, areaList, false );
+  for ( QList<QtxWorkstackArea*>::iterator delIt = areaList.begin(); delIt != areaList.end(); ++delIt )
+  {
+    if ( (*delIt)->isEmpty() )
+      delete *delIt;
+  }
 
-  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 );
+  QApplication::instance()->processEvents();
+
+  // restore splitters' sizes (map of sizes is filled in setSplitters)
+  for ( QMap< QSplitter*, QList<int> >::iterator itm = sMap.begin(); itm != sMap.end(); ++itm )
+    itm.key()->setSizes( itm.value() );
 
-  myPainter->drawRect( tr );
+  return (*this);
 }
 
 /*!
-  Deletes internal painter
+  \brief Dump workstack configuration to the state description string.
+  \param parameters resulting workstack state description
+  \return reference to this workstack
 */
-void QtxWorkstackDrag::endDrawRect()
+QtxWorkstack& QtxWorkstack::operator>>( QString& outParameters )
 {
-  delete myPainter;
-  myPainter = 0;
+  splitterInfo( mySplit, outParameters );
+  return (*this);
 }
 
 /*!
-  Initialize internal painter
+  \fn void QtxWorkstack::windowActivated( QWidget* w )
+  \brief Emitted when the workstack's child widget \w is activated.
+  \param w widget being activated
 */
-void QtxWorkstackDrag::startDrawRect()
-{
-  if ( myPainter )
-    return;
-
-  int scr = QApplication::desktop()->screenNumber( (QWidget*)this );
-  QWidget* paint_on = QApplication::desktop()->screen( scr );
-
-  myPainter = new QPainter( paint_on, true );
-  myPainter->setPen( QPen( gray, 3 ) );
-  myPainter->setRasterOp( XorROP );
-}
index c31479cc6b8055ac5c916bfbb1da51e1fa2b01fb..ad5cf59e4d126cde47f606341e766dbf7a2ce62e 100644 (file)
 
 #include "Qtx.h"
 
-#include <qhbox.h>
-#include <qwidget.h>
-#include <qtabbar.h>
-#include <qwidgetlist.h>
+#include <QWidget>
+#include <QFrame>
+#include <QTabBar>
+#include <QEvent>
+#include <QMap>
 
 class QAction;
-class QTabBar;
-class QPainter;
 class QSplitter;
 class QPushButton;
-class QWidgetStack;
+class QStackedWidget;
 
 class QtxWorkstackArea;
 class QtxWorkstackDrag;
@@ -50,13 +49,19 @@ class QTX_EXPORT QtxWorkstack : public QWidget
   Q_OBJECT
 
 public:
-  enum { SplitVertical, SplitHorizontal, Close, Rename };
+  //! Workstack actions (context menu items)
+  enum { SplitVertical,          //!< "Split vertically" menu item
+        SplitHorizontal,        //!< "Split horizontally" menu item
+        Close,                  //!< "Close" menu item
+        Rename                  //!< "Rename" menu item
+  };
     
+  //! Workstack splitting type
   enum SplitType
   {
-    SPLIT_STAY, //!< given widget stays in its workarea, others are moved into a new one
-    SPLIT_AT,   //!< widgets before a given widget stays in they workarea, others are moved into a new one
-    SPLIT_MOVE  //!< given widget is moved into a new workarea, others stay in an old one
+    SplitStay,  //!< selected widget stays in current workarea, others widgets are moved into a new workarea
+    SplitAt,    //!< all widgets before selected widget stay in current workarea, other widgess are moved into a new workarea
+    SplitMove   //!< selected widget is moved into a new workarea, all other widgets stay in an old workarea
   };
 
 public:
@@ -71,10 +76,12 @@ public:
   int                 accel( const int ) const;
   void                setAccel( const int, const int );
 
+  bool                isActionEnabled( const int ) const;
+  void                setActionEnabled( const int, const bool );
+
   void                split( const int );
 
-  // STV: Useless function. wid->setFocus() should be used instead.
-  // void OnTop( QWidget* wid);
+  QWidget*            addWindow( QWidget*, Qt::WindowFlags = 0 );
 
   void Split( QWidget* wid, const Qt::Orientation o, const SplitType type );
   void Attract( QWidget* wid1, QWidget* wid2, const bool all );
@@ -101,13 +108,12 @@ private slots:
   void                onDeactivated( QtxWorkstackArea* );
 
 protected:
-  virtual void        childEvent( QChildEvent* );
-  virtual void        customEvent( QCustomEvent* );
+  virtual void        customEvent( QEvent* );
 
 private:
   QSplitter*          splitter( QtxWorkstackArea* ) const;
-  void                splitters( QSplitter*, QPtrList<QSplitter>&, const bool = false ) const;
-  void                areas( QSplitter*, QPtrList<QtxWorkstackArea>&, const bool = false ) const;
+  void                splitters( QSplitter*, QList<QSplitter*>&, const bool = false ) const;
+  void                areas( QSplitter*, QList<QtxWorkstackArea*>&, const bool = false ) const;
 
   QSplitter*          wrapSplitter( QtxWorkstackArea* );
   void                insertWidget( QWidget*, QWidget*, QWidget* );
@@ -131,32 +137,34 @@ private:
                                                           const int need_pos, const int splitter_pos );
   
   void                splitterInfo( QSplitter*, QString& ) const;
-  void                setSplitter( QSplitter*, const QString&, QMap< QSplitter*,QValueList<int> >& );
+  void                setSplitter( QSplitter*, const QString&, QMap< QSplitter*, QList<int> >& );
   
 private:
-  QWidget*            myWin;
-  QtxWorkstackArea*   myArea;
-  QSplitter*          mySplit;
-  QWidget*            myWorkWin;
-  QtxWorkstackArea*   myWorkArea;
+  QWidget*            myWin;        //!< active widget
+  QtxWorkstackArea*   myArea;       //!< active workarea
+  QSplitter*          mySplit;      //!< tol-level splitter
+  QWidget*            myWorkWin;    //!< widget where popup menu is invoked (used internally)
+  QtxWorkstackArea*   myWorkArea;   //!< workarea where popup menu is invoked (used internally)
 
-  QMap<int, QAction*> myActionsMap; //!< The map of the actions. Allows to get the QAction object by the key.
+  QMap<int, QAction*> myActionsMap; //!< actions map
 
   friend class QtxWorkstackArea;
   friend class QtxWorkstackDrag;
 };
 
-class QtxWorkstackArea : public QWidget
+class QtxWorkstackArea : public QFrame
 {
   Q_OBJECT
 
+  class WidgetEvent;
+
 public:
   QtxWorkstackArea( QWidget* );
   virtual ~QtxWorkstackArea();
 
   bool                isEmpty() const;
 
-  void                insertWidget( QWidget*, const int = -1 );
+  QWidget*            insertWidget( QWidget*, const int = -1, Qt::WindowFlags = 0 );
   void                removeWidget( QWidget*, const bool = true );
 
   QWidget*            activeWidget() const;
@@ -184,18 +192,17 @@ signals:
   void                deactivated( QtxWorkstackArea* );
 
 public slots:
-  virtual void        show();
-  virtual void        hide();
+  virtual void        setVisible( bool );
 
 private slots:
   void                onClose();
-  void                onSelected( int );
+  void                onCurrentChanged( int );
 
   void                onWidgetDestroyed();
 
   void                onChildDestroyed( QObject* );
   void                onChildShown( QtxWorkstackChild* );
-  void                onChildHided( QtxWorkstackChild* );
+  void                onChildHidden( QtxWorkstackChild* );
   void                onChildActivated( QtxWorkstackChild* );
   void                onChildCaptionChanged( QtxWorkstackChild* );
 
@@ -203,12 +210,16 @@ private slots:
   void                onContextMenuRequested( QPoint );
 
 protected:
-  virtual void        customEvent( QCustomEvent* );
+  virtual void        customEvent( QEvent* );
   virtual void        focusInEvent( QFocusEvent* );
   virtual void        mousePressEvent( QMouseEvent* );
 
 private:
-  enum { ActivateWidget = QEvent::User, FocusWidget, RemoveWidget };
+  //! Custom events
+  enum { ActivateWidget = QEvent::User,   //!< activate widget event
+        FocusWidget,                     //!< focus receiving widget event
+        RemoveWidget                     //!< widget removing event
+  };
 
 private:
   void                updateState();
@@ -241,22 +252,22 @@ private:
   typedef QMap<QWidget*, WidgetInfo>         WidgetInfoMap;
 
 private:
-  QtxWorkstackTabBar* myBar;
-  QPushButton*        myClose;
-  QWidgetStack*       myStack;
-
-  QWidgetList         myList;
-  WidgetInfoMap       myInfo;
-  ChildMap            myChild;
-  BlockMap            myBlock;
+  QtxWorkstackTabBar* myBar;     //!< workarea tab bar header
+  QPushButton*        myClose;   //!< close button
+  QStackedWidget*     myStack;   //!< widget stack
+
+  QWidgetList         myList;    //!< child widgets list
+  WidgetInfoMap       myInfo;    //!< widgets states mp
+  ChildMap            myChild;   //!< child widget containers map
+  BlockMap            myBlock;   //!< blocked widgets
 };
 
-class QtxWorkstackChild : public QHBox
+class QtxWorkstackChild : public QWidget
 {
   Q_OBJECT
 
 public:
-  QtxWorkstackChild( QWidget*, QWidget* = 0 );
+  QtxWorkstackChild( QWidget*, QWidget* = 0, Qt::WindowFlags = 0 );
   virtual ~QtxWorkstackChild();
 
   QWidget*            widget() const;
@@ -265,7 +276,7 @@ public:
 
 signals:
   void                shown( QtxWorkstackChild* );
-  void                hided( QtxWorkstackChild* );
+  void                hidden( QtxWorkstackChild* );
   void                activated( QtxWorkstackChild* );
   void                captionChanged( QtxWorkstackChild* );
 
@@ -276,7 +287,7 @@ protected:
   virtual void        childEvent( QChildEvent* );
 
 private:
-  QWidget*            myWidget;
+  QWidget*            myWidget;   //!< child widget
 };
 
 class QtxWorkstackTabBar : public QTabBar
@@ -287,59 +298,37 @@ public:
   QtxWorkstackTabBar( QWidget* = 0 );
   virtual ~QtxWorkstackTabBar();
 
-  QRect               tabRect( const int ) const;
-
+  bool                isActive() const;
   void                setActive( const bool );
 
+  int                 tabId( const int ) const;
+  int                 indexOf( const int ) const;
+  void                setTabId( const int, const int );
+
+  void                updateActiveState();
+
 signals:
   void                dragActiveTab();
   void                contextMenuRequested( QPoint );
 
+private slots:
+  void                onCurrentChanged( int );
+
 protected:
   virtual void        mouseMoveEvent( QMouseEvent* );
   virtual void        mousePressEvent( QMouseEvent* );
   virtual void        mouseReleaseEvent( QMouseEvent* );
   virtual void        contextMenuEvent( QContextMenuEvent* );
 
-  virtual void        paintLabel( QPainter*, const QRect&, QTab*, bool ) const;
+//  virtual void        paintLabel( QPainter*, const QRect&, QTab*, bool ) const;
 
 private:
-  int                 myId;
-};
-
-class QtxWorkstackDrag : public QObject
-{
-  Q_OBJECT
-
-public:
-  QtxWorkstackDrag( QtxWorkstack*, QtxWorkstackChild* );
-  virtual ~QtxWorkstackDrag();
-
-  virtual bool        eventFilter( QObject*, QEvent* );
-
-private:
-  void                dropWidget();
-
-  void                updateTarget( const QPoint& );
-  QtxWorkstackArea*   detectTarget( const QPoint&, int& ) const;
-  void                setTarget( QtxWorkstackArea*, const int );
-
-  void                drawRect();
-  void                endDrawRect();
-  void                startDrawRect();
-
-private:
-  QtxWorkstack*       myWS;
-  QtxWorkstackChild*  myChild;
-
-  int                 myTab;
-  QtxWorkstackArea*   myArea;
-  QPainter*           myPainter;
-  
+  int                 myId;         //!< current tab page index
+  bool                myActive;     //!< "active" status
 };
 
 #ifdef WIN32
 #pragma warning( default:4251 )
 #endif
 
-#endif
+#endif   // QTXWORKSTACK_H