From 46c8445ebb017412aac810d13ac64592a7151e6a Mon Sep 17 00:00:00 2001 From: vsr Date: Mon, 14 May 2007 11:15:42 +0000 Subject: [PATCH] Porting to Qt4 --- src/Qtx/QtxWorkstack.cxx | 4107 +++++++++++++++++++++----------------- src/Qtx/QtxWorkstack.h | 149 +- 2 files changed, 2300 insertions(+), 1956 deletions(-) diff --git a/src/Qtx/QtxWorkstack.cxx b/src/Qtx/QtxWorkstack.cxx index 3cf22f413..7c6f234d9 100644 --- a/src/Qtx/QtxWorkstack.cxx +++ b/src/Qtx/QtxWorkstack.cxx @@ -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 @@ -21,2594 +21,2949 @@ #include "QtxWorkstack.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "QtxAction.h" -#define DARK_COLOR_LIGHT 250 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 lst; - areas( mySplit, lst, true ); + QApplication::instance()->removeEventFilter( this ); - QWidgetList widList; - for ( QPtrListIterator 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 areaList; - areas( s, areaList ); + QRect r = myArea->floatRect(); + int m = 2; - QPtrList 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 SplitType 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 allAreas; - areas(mySplit, allAreas, true); + QVBoxLayout* base = new QVBoxLayout( this ); + base->setMargin( frameWidth() ); - QPtrListIterator 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 areaList; - areas(s, areaList); + myBar = new QtxWorkstackTabBar( top ); + tl->addWidget( myBar, 1 ); - QPtrList 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 allAreas; - areas( mySplit, allAreas, true ); - for ( QPtrListIterator 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( sender() ); + if ( !bar ) return; - // find area of the widgets - QtxWorkstackArea *area1 = NULL, *area2 = NULL; - QPtrList allAreas; - areas(mySplit, allAreas, true); - QPtrListIterator 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 # - 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 # - *its = new_this; - ++its; - // set size to all items after an item # - 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 allAreas; - areas(mySplit, allAreas, true); - for ( QPtrListIterator 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 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 SetRelativePosition 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 areaList; - areas((QSplitter*)it.current(), areaList, true); - for (QPtrListIterator 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 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 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 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( (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 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& 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& 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 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 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 lst; - areas( mySplit, lst, true ); - for ( QPtrListIterator 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 recList; - splitters( split, recList, false ); - for ( QPtrListIterator itr( recList ); itr.current(); ++itr ) - updateState( itr.current() ); - - QPtrList splitList; - splitters( split, splitList, false ); - - QPtrList areaList; - areas( split, areaList, false ); - - bool vis = false; - for ( QPtrListIterator 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 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 sizes = split->sizes(); - QString sizesStr; - for ( QValueList::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( 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 >& 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 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 splitList; - QPtrList areaList; - splitters( mySplit, splitList, false ); - areas( mySplit, areaList, false ); - for ( QPtrListIterator iter( splitList ); iter.current(); ++iter ) - delete iter.current(); - for ( QPtrListIterator it( areaList ); it.current(); ++it ) - if ( it.current()->isEmpty() ) - delete it.current(); - - // restore splitter recursively - QMap< QSplitter*, QValueList > 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 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 >::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 lst; + areas( mySplit, lst, true ); - updateActiveState(); + QWidgetList widList; + for ( QList::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 areaList; + areas( s, areaList ); - updateState(); + QList 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( 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 allAreas; + areas(mySplit, allAreas, true); + + + for ( QList::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 areaList; + areas( s, areaList ); + + QList 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 allAreas; + areas( mySplit, allAreas, true ); + for ( QList::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 # + 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 # + *its = new_this; + ++its; + // set size to all items after an item # + 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 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 allAreas; + areas( mySplit, allAreas, true ); + for ( QList::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 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( *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 areaList; + areas( (QSplitter*)(*it), areaList, true ); + for ( QList::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 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 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 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( (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 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& 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& 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 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 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 lst; + areas( mySplit, lst, true ); + for ( QList::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 recList; + splitters( split, recList, false ); + for ( QList::iterator itr = recList.begin(); itr != recList.end(); ++itr ) + updateState( *itr ); + + QList splitList; + splitters( split, splitList, false ); + + QList areaList; + areas( split, areaList, false ); + + bool vis = false; + for ( QList::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::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 sizes = split->sizes(); + for ( QList::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 "= ...". + 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 l = qFindChildren( parent->topLevelWidget(), aName ); + if ( !l.isEmpty() ) + view = ::qobject_cast( 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 >& 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 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 splitList; + QList areaList; + splitters( mySplit, splitList, false ); + areas( mySplit, areaList, false ); + for ( QList::iterator iter = splitList.begin(); iter != splitList.end(); ++iter ) + delete *iter; - QRect r = myArea->floatRect(); - int m = myPainter->pen().width(); + for ( QList::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 > 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::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 >::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 ); -} diff --git a/src/Qtx/QtxWorkstack.h b/src/Qtx/QtxWorkstack.h index c31479cc6..ad5cf59e4 100644 --- a/src/Qtx/QtxWorkstack.h +++ b/src/Qtx/QtxWorkstack.h @@ -24,17 +24,16 @@ #include "Qtx.h" -#include -#include -#include -#include +#include +#include +#include +#include +#include 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&, const bool = false ) const; - void areas( QSplitter*, QPtrList&, const bool = false ) const; + void splitters( QSplitter*, QList&, const bool = false ) const; + void areas( QSplitter*, QList&, 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 >& ); + void setSplitter( QSplitter*, const QString&, QMap< QSplitter*, QList >& ); 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 myActionsMap; //!< The map of the actions. Allows to get the QAction object by the key. + QMap 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 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 -- 2.39.2