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