// Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
-//
+//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
-// License as published by the Free Software Foundation; either
+// License as published by the Free Software Foundation; either
// version 2.1 of the License.
-//
-// This library is distributed in the hope that it will be useful
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+//
+// This library is distributed in the hope that it will be useful
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
#include "QtxWorkstack.h"
-#include <qstyle.h>
-#include <qimage.h>
-#include <qaction.h>
-#include <qlayout.h>
-#include <qpixmap.h>
-#include <qiconset.h>
-#include <qpainter.h>
-#include <qsplitter.h>
-#include <qpopupmenu.h>
-#include <qobjectlist.h>
-#include <qpushbutton.h>
-#include <qwidgetstack.h>
-#include <qapplication.h>
-#include <qinputdialog.h>
-#include <qevent.h>
-#include <qregexp.h>
+#include "QtxAction.h"
-#define DARK_COLOR_LIGHT 250
+#include <QRegExp>
+#include <QMenu>
+#include <QFocusEvent>
+#include <QMouseEvent>
+#include <QStyle>
+#include <QLayout>
+#include <QSplitter>
+#include <QRubberBand>
+#include <QPushButton>
+#include <QApplication>
+#include <QInputDialog>
+#include <QStackedWidget>
+
+#define DARK_COLOR_LIGHT 250
/*!
- Constructor
+ \class QtxWorkstackArea::WidgetEvent
+ \internal
+ \brief Internal class used to forward child widgets events to the workarea
*/
-QtxWorkstack::QtxWorkstack( QWidget* parent )
-: QWidget( parent ),
-myWin( 0 ),
-myArea( 0 ),
-myWorkWin( 0 ),
-myWorkArea( 0 )
+
+class QtxWorkstackArea::WidgetEvent : public QEvent
{
- myActionsMap.insert( SplitVertical, new QAction( tr( "Split vertically" ), 0, this ) );
- myActionsMap.insert( SplitHorizontal, new QAction( tr( "Split horizontally" ), 0, this ) );
- myActionsMap.insert( Close, new QAction( tr( "Close" ), 0, this ) );
- myActionsMap.insert( Rename, new QAction( tr( "Rename" ), 0, this ) );
+public:
+ WidgetEvent( Type t, QWidget* w = 0 ) : QEvent( t ), myWidget( w ) {};
- connect( myActionsMap[SplitVertical], SIGNAL( activated() ), this, SLOT( splitVertical() ) );
- connect( myActionsMap[SplitHorizontal], SIGNAL( activated() ), this, SLOT( splitHorizontal() ) );
- connect( myActionsMap[Close], SIGNAL( activated() ), this, SLOT( onCloseWindow() ) );
- connect( myActionsMap[Rename], SIGNAL( activated() ), this, SLOT( onRename() ) );
+ QWidget* widget() const { return myWidget; }
- QVBoxLayout* base = new QVBoxLayout( this );
- mySplit = new QSplitter( this );
- mySplit->setChildrenCollapsible( false );
- base->addWidget( mySplit );
-}
+private:
+ QWidget* myWidget; // event receiver widget
+};
/*!
- Destructor
+ \class QtxWorkstackDrag
+ \internal
+ \brief Workstack drag object
*/
-QtxWorkstack::~QtxWorkstack()
+
+class QtxWorkstackDrag : public QObject
+{
+ Q_OBJECT
+
+public:
+ QtxWorkstackDrag( QtxWorkstack*, QtxWorkstackChild* );
+ virtual ~QtxWorkstackDrag();
+
+ virtual bool eventFilter( QObject*, QEvent* );
+
+private:
+ void dropWidget();
+
+ void updateTarget( const QPoint& );
+ QtxWorkstackArea* detectTarget( const QPoint&, int& ) const;
+ void setTarget( QtxWorkstackArea*, const int );
+
+ void drawRect();
+ void endDrawRect();
+ void startDrawRect();
+
+private:
+ QtxWorkstack* myWS; //!< parent workstack
+ QtxWorkstackChild* myChild; //!< workstack child widget container
+
+ int myTab; //!< workarea tab page index
+ QtxWorkstackArea* myArea; //!< workarea
+ QRubberBand* myTabRect; //!< tab bar rubber band
+ QRubberBand* myAreaRect; //!< workarea rubber band
+};
+
+/*!
+ \brief Constructor.
+ \param ws parent workstack
+ \param child child widget container
+*/
+QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
+: QObject( 0 ),
+ myWS( ws ),
+ myChild( child ),
+ myTab( -1 ),
+ myArea( 0 ),
+ myTabRect( 0 ),
+ myAreaRect( 0 )
{
+ QApplication::instance()->installEventFilter( this );
}
/*!
- \return list of all widgets in all areas
+ \brief Destructor.
*/
-QWidgetList QtxWorkstack::windowList() const
+QtxWorkstackDrag::~QtxWorkstackDrag()
{
- QPtrList<QtxWorkstackArea> lst;
- areas( mySplit, lst, true );
+ QApplication::instance()->removeEventFilter( this );
- QWidgetList widList;
- for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current(); ++it )
+ endDrawRect();
+}
+
+/*!
+ \brief Custom event filter.
+ \param o event receiver widget
+ \param e event
+ \return \c true if event should be filtered (stop further processing)
+*/
+bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
+{
+ switch ( e->type() )
{
- QWidgetList wids = it.current()->widgetList();
- for ( QWidgetListIt itr( wids ); itr.current(); ++itr )
- widList.append( itr.current() );
+ case QEvent::MouseMove:
+ updateTarget( ((QMouseEvent*)e)->globalPos() );
+ break;
+ case QEvent::MouseButtonRelease:
+ drawRect();
+ endDrawRect();
+ dropWidget();
+ deleteLater();
+ break;
+ default:
+ return false;
}
-
- return widList;
+ return true;
}
/*!
- \return list of all widgets in active area
+ \brief Detect and set dropping target widget.
+ \param p current dragging position
*/
-QWidgetList QtxWorkstack::splitWindowList() const
+void QtxWorkstackDrag::updateTarget( const QPoint& p )
{
- return myArea ? myArea->widgetList() : QWidgetList();
+ int tab = -1;
+ QtxWorkstackArea* area = detectTarget( p, tab );
+ setTarget( area, tab );
}
/*!
- \return active widget
+ \brief Detect dropping target.
+ \param p current dragging position
+ \param tab resulting target tab page index
+ \return target workarea or 0 if there is no correct drop target
*/
-QWidget* QtxWorkstack::activeWindow() const
+QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
{
- return myWin;
+ if ( p.isNull() )
+ return 0;
+
+ QtxWorkstackArea* area = myWS->areaAt( p );
+ if ( area )
+ tab = area->tabAt( p );
+ return area;
}
/*!
- Splits widgets
- \param o - orientation (Qt::Orientation)
+ \brief Set dropping target.
+ \param area new target workarea
+ \param tab target workarea's tab page index
*/
-void QtxWorkstack::split( const int o )
+void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
{
- QtxWorkstackArea* area = myWorkArea;
- if ( !area )
- area = activeArea();
- if ( !area )
+ if ( !area || ( myArea == area && tab == myTab ) )
return;
- if ( area->widgetList().count() < 2 )
- return;
+ startDrawRect();
- QWidget* curWid = area->activeWidget();
- if ( !curWid )
+ if ( myArea )
+ drawRect();
+
+ myTab = tab;
+ myArea = area;
+
+ if ( myArea )
+ drawRect();
+}
+
+/*!
+ \brief Called when drop operation is finished.
+
+ Inserts dropped widget to the target workarea.
+*/
+void QtxWorkstackDrag::dropWidget()
+{
+ if ( myArea )
+ myArea->insertWidget( myChild->widget(), myTab );
+}
+
+/*!
+ \brief Draw floating rectangle.
+*/
+void QtxWorkstackDrag::drawRect()
+{
+ if ( !myArea )
return;
- QSplitter* s = splitter( area );
- QPtrList<QtxWorkstackArea> areaList;
- areas( s, areaList );
+ QRect r = myArea->floatRect();
+ int m = 2;
- QPtrList<QSplitter> splitList;
- splitters( s, splitList );
+ r.setTop( r.top() + m + 2 );
+ r.setLeft( r.left() + m + 2 );
+ r.setRight( r.right() - m - 2 );
+ r.setBottom( r.bottom() - m - 2 );
- QSplitter* trg = 0;
- if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
- trg = s;
+ if ( myAreaRect )
+ {
+ myAreaRect->setGeometry( r );
+ myAreaRect->setVisible( r.isValid() );
+ }
- if ( !trg )
- trg = wrapSplitter( area );
+ QRect tr = myArea->floatTab( myTab );
- if ( !trg )
- return;
+ tr.setTop( tr.top() + m );
+ tr.setLeft( tr.left() + m );
+ tr.setRight( tr.right() - m );
+ tr.setBottom( tr.bottom() - m );
+
+ if ( myTabRect )
+ {
+ myTabRect->setGeometry( tr );
+ myTabRect->setVisible( tr.isValid() );
+ }
+}
- trg->setOrientation( (Orientation)o );
+/*!
+ \brief Delete rubber band on the end on the dragging operation.
+*/
+void QtxWorkstackDrag::endDrawRect()
+{
+ delete myAreaRect;
+ myAreaRect = 0;
- QtxWorkstackArea* newArea = createArea( 0 );
- insertWidget( newArea, trg, area );
+ delete myTabRect;
+ myTabRect = 0;
+}
- area->removeWidget( curWid );
- newArea->insertWidget( curWid );
+/*!
+ \brief Create rubber band to be drawn on the dragging operation.
+*/
+void QtxWorkstackDrag::startDrawRect()
+{
+ if ( !myTabRect )
+ myTabRect = new QRubberBand( QRubberBand::Rectangle );
- distributeSpace( trg );
+ myTabRect->hide();
- curWid->show();
- curWid->setFocus();
+ if ( !myAreaRect )
+ myAreaRect = new QRubberBand( QRubberBand::Rectangle );
+
+ myAreaRect->hide();
}
/*!
- \brief Split workarea of the given widget on two parts.
- \param wid - widget, belonging to this workstack
- \param o - orientation of splitting (Qt::Horizontal or Qt::Vertical)
- \param type - type of splitting, see <VAR>SplitType</VAR> enumeration
+ \class QtxWorkstackArea
+ \internal
+ \brief Workstack widget workarea.
+*/
+
+/*!
+ \brief Constructor.
+ \param parent parent widget
*/
-void QtxWorkstack::Split (QWidget* wid, const Qt::Orientation o, const SplitType type)
+QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
+: QFrame( parent )
{
- if (!wid) return;
+ setFrameStyle( QFrame::Panel | QFrame::Sunken );
- // find area of the given widget
- QtxWorkstackArea* area = NULL;
- QPtrList<QtxWorkstackArea> allAreas;
- areas(mySplit, allAreas, true);
+ QVBoxLayout* base = new QVBoxLayout( this );
+ base->setMargin( frameWidth() );
- QPtrListIterator<QtxWorkstackArea> it (allAreas);
- for (; it.current() && !area; ++it) {
- if (it.current()->contains(wid))
- area = it.current();
- }
- if (!area) return;
+ QWidget* top = new QWidget( this );
+ base->addWidget( top );
- QWidgetList wids = area->widgetList();
- if (wids.count() < 2)
- return;
+ QHBoxLayout* tl = new QHBoxLayout( top );
+ tl->setMargin( 0 );
- QSplitter* s = splitter(area);
- QPtrList<QtxWorkstackArea> areaList;
- areas(s, areaList);
+ myBar = new QtxWorkstackTabBar( top );
+ tl->addWidget( myBar, 1 );
- QPtrList<QSplitter> splitList;
- splitters(s, splitList);
+ QPushButton* close = new QPushButton( top );
+ close->setIcon( style()->standardIcon( QStyle::SP_TitleBarCloseButton ) );
+ close->setAutoDefault( true );
+ close->setFlat( true );
+ myClose = close;
+ tl->addWidget( myClose );
- QSplitter* trg = 0;
- if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
- trg = s;
+ myStack = new QStackedWidget( this );
- if (!trg) trg = wrapSplitter(area);
- if (!trg) return;
+ base->addWidget( myStack, 1 );
- trg->setOrientation(o);
+ connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
+ connect( myBar, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
+ connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
+ connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
- QtxWorkstackArea* newArea = createArea(0);
- insertWidget(newArea, trg, area);
+ updateState();
- switch (type) {
- case SPLIT_STAY:
- {
- QWidgetListIt itr (wids);
- for (; itr.current(); ++itr)
- {
- QWidget* wid_i = itr.current();
- if (wid_i != wid) {
- area->removeWidget(wid_i);
- newArea->insertWidget(wid_i);
- }
- }
- }
- break;
- case SPLIT_AT:
- {
- QWidgetListIt itr (wids);
- for (; itr.current() && itr.current() != wid; ++itr) {
- }
- for (; itr.current(); ++itr) {
- area->removeWidget(itr.current());
- newArea->insertWidget(itr.current());
- }
- }
- break;
- case SPLIT_MOVE:
- area->removeWidget(wid);
- newArea->insertWidget(wid);
- break;
- }
+ updateActiveState();
- distributeSpace(trg);
+ QApplication::instance()->installEventFilter( this );
}
/*!
- \brief Put given widget on top of its workarea
- \param wid - widget, belonging to this workstack
+ \brief Destructor.
*/
-/*
-void QtxWorkstack::OnTop (QWidget* wid)
+QtxWorkstackArea::~QtxWorkstackArea()
+{
+ QApplication::instance()->removeEventFilter( this );
+}
+
+/*!
+ \brief Check if workarea contains visible widgets.
+ \return \c true if area is empty (all child widgets are removed or now shown)
+*/
+bool QtxWorkstackArea::isEmpty() const
+{
+ bool res = false;
+ for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !res; ++it )
+ res = it.value().vis;
+ return !res;
+}
+
+/*!
+ \brief Add widget to the workarea.
+ \param wid widget to be added
+ \param idx position in the area widget to be added to
+ \param f widget flags
+ \return child widget container object (or 0 if index is invalid)
+*/
+QWidget* QtxWorkstackArea::insertWidget( QWidget* wid, const int idx, Qt::WindowFlags f )
{
if ( !wid )
- return;
+ return 0;
- // find area of the given widget
- QtxWorkstackArea* area = 0;
- QPtrList<QtxWorkstackArea> allAreas;
- areas( mySplit, allAreas, true );
- for ( QPtrListIterator<QtxWorkstackArea> it( allAreas ); it.current() && !area; ++it )
+ int pos = myList.indexOf( wid );
+ if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
+ return 0;
+
+ myList.removeAll( wid );
+ pos = idx < 0 ? myList.count() : idx;
+ myList.insert( qMin( pos, (int)myList.count() ), wid );
+ if ( !myInfo.contains( wid ) )
{
- if ( it.current()->contains( wid ) )
- area = it.current();
+ QtxWorkstackChild* child = new QtxWorkstackChild( wid, myStack, f );
+ myChild.insert( wid, child );
+ myInfo.insert( wid, WidgetInfo() );
+ myInfo[wid].id = generateId();
+ myInfo[wid].vis = wid->isVisibleTo( wid->parentWidget() );
+
+ connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
+ connect( wid, SIGNAL( destroyed() ), this, SLOT( onWidgetDestroyed() ) );
+ connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
+ connect( child, SIGNAL( hidden( QtxWorkstackChild* ) ), this, SLOT( onChildHidden( QtxWorkstackChild* ) ) );
+ connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
+ connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
}
- if ( area )
- area->setActiveWidget( wid );
+ updateState();
+
+ setWidgetActive( wid );
+ wid->setFocus();
+
+ return myChild[wid];
}
-*/
/*!
- \brief Move widget(s) from source workarea into target workarea
- or just reorder widgets inside one workarea.
- \param wid1 - widget from target workarea
- \param wid2 - widget from source workarea
- \param all - if this parameter is TRUE, all widgets from source workarea will
- be moved into the target one, else only the \a wid2 will be moved
-
- Move \a wid2 in target workarea. Put it right after \a wid1.
- If value of boolean argument is TRUE, all widgets from source workarea
- will be moved together with \a wid2, source workarea will be deleted.
- If \a wid1 and \a wid2 belongs to one workarea, simple reordering will take place.
+ \brief Create and show popup menu for the area.
+ \param p mouse pointer position at which popup menu should be shown
*/
-void QtxWorkstack::Attract ( QWidget* wid1, QWidget* wid2, const bool all )
+void QtxWorkstackArea::onContextMenuRequested( QPoint p )
{
- if ( !wid1 || !wid2 )
+ const QtxWorkstackTabBar* bar = ::qobject_cast<const QtxWorkstackTabBar*>( sender() );
+ if ( !bar )
return;
- // find area of the widgets
- QtxWorkstackArea *area1 = NULL, *area2 = NULL;
- QPtrList<QtxWorkstackArea> allAreas;
- areas(mySplit, allAreas, true);
- QPtrListIterator<QtxWorkstackArea> it (allAreas);
- for (; it.current() && !(area1 && area2); ++it) {
- if (it.current()->contains(wid1))
- area1 = it.current();
- if (it.current()->contains(wid2))
- area2 = it.current();
- }
- if (!area1 || !area2) return;
+ QWidget* wid = 0;
+ int idx = tabAt( p );
+ if ( idx != -1 )
+ wid = widget( myBar->tabId( idx ) );
- QWidget* curWid = area1->activeWidget();
- if (!curWid) return;
+ emit contextMenuRequested( wid, p );
+}
- if (area1 == area2) {
- if (all) {
- // Set wid1 at first position, wid2 at second
- area1->insertWidget(wid1);
- area1->insertWidget(wid2, 1);
- } else {
- // Set wid2 right after wid1
- area1->removeWidget(wid2);
- int wid1_ind = 0;
- QWidgetList wids1 = area1->widgetList();
- QWidgetListIt itr1 (wids1);
- for (; itr1.current() && itr1.current() != wid1; ++itr1, ++wid1_ind);
- area1->insertWidget(wid2, wid1_ind + 1);
- }
- } else {
- int wid1_ind = 0;
- QWidgetList wids1 = area1->widgetList();
- QWidgetListIt itr1 (wids1);
- for (; itr1.current() && itr1.current() != wid1; ++itr1, ++wid1_ind);
-
- if (all) {
- // Set wid2 right after wid1, other widgets from area2 right after wid2
- QWidgetList wids2 = area2->widgetList();
- QWidgetListIt itr2 (wids2);
- for (int ind = wid1_ind + 1; itr2.current(); ++itr2, ++ind)
- {
- area2->removeWidget(itr2.current());
- if (itr2.current() == wid2) {
- area1->insertWidget(itr2.current(), wid1_ind + 1);
- } else {
- area1->insertWidget(itr2.current(), ind);
- }
- }
- } else {
- // Set wid2 right after wid1
- area2->removeWidget(wid2);
- area1->insertWidget(wid2, wid1_ind + 1);
- }
- }
-
- area1->setActiveWidget( curWid );
-}
+/*!
+ \brief Called when area's child widget is destroyed.
-static void setSizes (QIntList& szList, const int item_ind,
- const int new_near, const int new_this, const int new_farr)
+ Removes child widget from the area.
+*/
+void QtxWorkstackArea::onWidgetDestroyed()
{
- // set size to all items before an item # <item_ind>
- int cur_pos = 0;
- QIntList::iterator its = szList.begin();
- for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
- *its = new_near;
- }
- if (its == szList.end()) return;
- // set size to item # <item_ind>
- *its = new_this;
- ++its;
- // set size to all items after an item # <item_ind>
- for (; its != szList.end(); ++its) {
- *its = new_farr;
- }
+ if ( sender() )
+ removeWidget( (QWidget*)sender(), false );
}
/*!
-* \brief Set position of the widget relatively its splitter.
-* \param wid - widget to set position of
-* \param pos - position relatively splitter. Value in range [0..1].
-*
-* Orientation of positioning will correspond to the splitter orientation.
+ \brief Remove widget from workarea.
+ \param wid widget to be removed
+ \param del if \c true the widget should be also deleted
*/
-void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
+void QtxWorkstackArea::removeWidget( QWidget* wid, const bool del )
{
- if ( position < 0.0 || 1.0 < position)
- return;
-
- if ( !wid )
+ if ( !myList.contains( wid ) )
return;
- // find area of the given widget
- QtxWorkstackArea* area = NULL;
- QPtrList<QtxWorkstackArea> allAreas;
- areas(mySplit, allAreas, true);
- for ( QPtrListIterator<QtxWorkstackArea> it( allAreas );
- it.current() && !area;
- ++it )
- {
- if (it.current()->contains(wid))
- area = it.current();
- }
+ if ( myBar->indexOf( widgetId( wid ) ) != -1 )
+ myBar->removeTab( myBar->indexOf( widgetId( wid ) ) );
- if ( !area )
- return;
+ myStack->removeWidget( child( wid ) );
- QSplitter* split = splitter( area );
- if ( !split )
- return;
+ myList.removeAll( wid );
+ myInfo.remove( wid );
+ myChild.remove( wid );
- // find index of the area in its splitter
- int item_ind = -1;
- bool isFound = false;
- const QObjectList* was = split->children();
- for (QObjectListIt ito (*was); ito.current() && !isFound; ++ito, ++item_ind)
+ if ( del )
{
- if (ito.current() == area)
- isFound = true;
+ delete child( wid );
+ if ( myList.isEmpty() )
+ delete this;
+ else
+ updateState();
}
- if (!isFound || item_ind == 0)
- return;
-
- QIntList szList = split->sizes();
- int splitter_size = (split->orientation() == Horizontal ?
- split->width() : split->height());
- int nb = szList.count();
+ else
+ updateState();
+}
- int new_prev = int(splitter_size * position / item_ind);
- int new_next = int(splitter_size * (1.0 - position) / (nb - item_ind));
- setSizes (szList, item_ind, new_prev, new_next, new_next);
- split->setSizes(szList);
+/*!
+ \brief Get all visible child widgets.
+ \return list of visible child widgets
+*/
+QWidgetList QtxWorkstackArea::widgetList() const
+{
+ QWidgetList lst;
+ for ( QWidgetList::const_iterator it = myList.begin(); it != myList.end(); ++it )
+ {
+ if ( widgetVisibility( *it ) )
+ lst.append( *it );
+ }
+ return lst;
}
/*!
-* \brief Set position of the widget relatively the entire workstack.
-* \param wid - widget to set position of
-* \param o - orientation of positioning (Qt::Horizontal or Qt::Vertical).
-* If o = Qt::Horizontal, horizontal position of \a wid will be changed.
-* If o = Qt::Vertical, vertical position of \a wid will be changed.
-* \param pos - position relatively workstack. Value in range [0..1].
+ \brief Get active child widget.
+ \return active widget
*/
-void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
- const double position )
+QWidget* QtxWorkstackArea::activeWidget() const
{
- if ( position < 0.0 || 1.0 < position )
- return;
+ return widget( myBar->tabId( myBar->currentIndex() ) );
+}
- if ( !wid )
- return;
+/*!
+ \brief Set active widget.
+ \param wid widget to be made active
+*/
+void QtxWorkstackArea::setActiveWidget( QWidget* wid )
+{
+ myBar->setCurrentIndex( myBar->indexOf( widgetId( wid ) ) );
+}
- int splitter_size = o == Horizontal ? mySplit->width() : mySplit->height();
- int need_pos = int( position * splitter_size );
- int splitter_pos = 0;
+/*!
+ \brief Check if area owns the specified widget.
+ \param wid widget to be checked
+ \return \c true if area contains widget
+*/
+bool QtxWorkstackArea::contains( QWidget* wid ) const
+{
+ return myList.contains( wid );
+}
- if ( setPosition( wid, mySplit, o, need_pos, splitter_pos ) != 0 )
+/*!
+ \brief Show/hide workarea.
+ \param on new visibility state
+*/
+void QtxWorkstackArea::setVisible( bool on )
+{
+ QMap<QWidget*, bool> map;
+ for ( QWidgetList::iterator it = myList.begin(); it != myList.end(); ++it )
{
- // impossible to set required position
+ map.insert( *it, isBlocked( *it ) );
+ setBlocked( *it, true );
}
+
+ QFrame::setVisible( on );
+
+ for ( QWidgetList::iterator itr = myList.begin(); itr != myList.end(); ++itr )
+ setBlocked( *itr, map.contains( *itr ) ? map[*itr] : false );
}
/*!
-* \brief Sets the action's accelerator key to accel.
-* \param id - the key of the action in the actions map.
-* \param accel - action's accelerator key.
+ \brief Check if workarea is active.
+ \return \c true if area is active
*/
-void QtxWorkstack::setAccel( const int id, const int accel )
+bool QtxWorkstackArea::isActive() const
{
- if ( !myActionsMap.contains( id ) )
- return;
+ QtxWorkstack* ws = workstack();
+ if ( !ws )
+ return false;
- myActionsMap[id]->setAccel( accel );
+ return ws->activeArea() == this;
}
/*!
-* \brief Returns the action's accelerator key.
-* \param id - the key of the action in the actions map.
-* \retval int - action's accelerator key.
+ \brief Update active tab bar state (active/inactive).
*/
-int QtxWorkstack::accel( const int id ) const
+void QtxWorkstackArea::updateActiveState()
{
- int res = 0;
- if ( myActionsMap.contains( id ) )
- res = myActionsMap[id]->accel();
- return res;
+ myBar->setActive( isActive() );
}
-static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
- const int item_ind, const int item_rel_pos,
- const int need_pos, const int splitter_pos)
+/*!
+ \brief Get parent workstack
+ \return workstack owning this workarea
+*/
+QtxWorkstack* QtxWorkstackArea::workstack() const
{
- if (item_ind == 0) { // cannot move in this splitter
- return (need_pos - splitter_pos);
+ QtxWorkstack* ws = 0;
+ QWidget* wid = parentWidget();
+ while ( wid && !ws )
+ {
+ if ( wid->inherits( "QtxWorkstack" ) )
+ ws = (QtxWorkstack*)wid;
+ wid = wid->parentWidget();
}
+ return ws;
+}
- int delta = 0;
- int new_prev = 0;
- int new_this = szList[item_ind];
- int new_next = 0;
-
- bool isToCheck = false;
-
- if (need_pos < splitter_pos) {
- // Set size of all previous workareas to zero <--
- if (item_ind == nb - 1) {
- // item iz last in the splitter, it will occupy all the splitter
- new_this = splitter_size;
- } else {
- // recompute size of next items in splitter
- new_next = (splitter_size - new_this) / (nb - item_ind - 1);
- }
- delta = need_pos - splitter_pos;
+/*!
+ \brief Custom event filter.
- } else if (need_pos > (splitter_pos + splitter_size)) {
- // Set size of all next workareas to zero -->
- // recompute size of previous items in splitter
- new_this = 0;
- new_prev = (splitter_size - new_this) / item_ind;
- delta = need_pos - (splitter_pos + splitter_size - new_this);
+ Process events from child widgets.
- } else { // required position lays inside this splitter
- // Move workarea inside splitter into required position <->
- int new_item_rel_pos = need_pos - splitter_pos;
- new_prev = new_item_rel_pos / item_ind;
- if (need_pos < (splitter_pos + item_rel_pos)) {
- // Make previous workareas smaller, next - bigger
- // No problem to keep old size of the widget
- } else {
- // Make previous workareas bigger, next - smaller
- if (new_this > splitter_size - new_item_rel_pos) {
- new_this = splitter_size - new_item_rel_pos;
+ \param o event receiver widget
+ \param e event
+ \return \c true if event should be filtered (stop further processing)
+*/
+bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
+{
+ if ( o->isWidgetType() )
+ {
+ QWidget* wid = (QWidget*)o;
+ if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
+ {
+ bool ok = false;
+ while ( !ok && wid && wid != myClose )
+ {
+ ok = wid == this;
+ wid = wid->parentWidget();
}
- // jfa to do: in this case fixed size of next widgets could prevent right resizing
- isToCheck = true;
- }
- if (item_ind == nb - 1) {
- new_this = splitter_size - new_item_rel_pos;
- } else {
- new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
+ if ( ok )
+ QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
}
- delta = 0;
}
+ return false;
+}
- setSizes (szList, item_ind, new_prev, new_this, new_next);
- return delta;
+/*!
+ \brief Get rectangle to be drawn when highlighting drop area.
+ \return area drop rectangle
+*/
+QRect QtxWorkstackArea::floatRect() const
+{
+ QRect r = myStack->geometry();
+ return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
}
/*!
-* \brief Set position of given widget.
-* \param wid - widget to be moved
-* \param split - currently processed splitter (goes from more common
-* to more particular splitter in recursion calls)
-* \param o - orientation of positioning
-* \param need_pos - required position of the given widget in pixels
-* (from top/left side of workstack area)
-* \param splitter_pos - position of the splitter \a split
-* (from top/left side of workstack area)
-* \retval int - returns difference between a required and a distinguished position.
-*
-* Internal method. Recursively calls itself.
-* Is called from <VAR>SetRelativePosition</VAR> public method.
+ \brief Get rectangle to be drawn when highlighting drop area on tab bar.
+ \param idx tab index
+ \return tab bar drop rectrangle
*/
-int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
- const int need_pos, const int splitter_pos )
+QRect QtxWorkstackArea::floatTab( const int idx ) const
{
- if ( !wid || !split )
- return need_pos - splitter_pos;
+ QRect r = myBar->tabRect( idx );
+ return QRect( myBar->mapToGlobal( r.topLeft() ), r.size() );
+}
- // Find corresponding sub-splitter.
- // Find also index of appropriate item in current splitter.
- int cur_ind = 0, item_ind = 0;
- bool isBottom = false, isFound = false;
- QSplitter* sub_split = NULL;
- const QObjectList* objs = split->children();
- if ( objs )
+/*!
+ \brief Get tab index by point.
+ \param p point
+ \return tab covering point or -1 if there is no tab covering point
+*/
+int QtxWorkstackArea::tabAt( const QPoint& pnt ) const
+{
+ int idx = -1;
+ QPoint p = myBar->mapFromGlobal( pnt );
+ for ( int i = 0; i < myBar->count() && idx == -1; i++ )
+ {
+ QRect r = myBar->tabRect( i );
+ if ( r.isValid() && r.contains( p ) )
+ idx = i;
+ }
+ return idx;
+}
+
+/*!
+ \brief Event handler for custom events.
+ \param e custom event
+*/
+void QtxWorkstackArea::customEvent( QEvent* e )
+{
+ WidgetEvent* we = (WidgetEvent*)e;
+
+ switch ( we->type() )
{
- for (QObjectListIt it (*objs); it.current() && !isFound; ++it)
+ case ActivateWidget:
+ emit activated( activeWidget() );
+ break;
+ case FocusWidget:
+ if ( activeWidget() )
{
- if (it.current()->inherits( "QtxWorkstackArea")) {
- if (((QtxWorkstackArea*)it.current())->contains(wid)) {
- item_ind = cur_ind;
- isBottom = true;
- isFound = true;
- }
- cur_ind++;
- } else if (it.current()->inherits("QSplitter")) {
- QPtrList<QtxWorkstackArea> areaList;
- areas((QSplitter*)it.current(), areaList, true);
- for (QPtrListIterator<QtxWorkstackArea> ita (areaList);
- ita.current() && !isFound;
- ++ita)
+ if ( !activeWidget()->focusWidget() )
+ activeWidget()->setFocus();
+ else
+ {
+ if ( activeWidget()->focusWidget()->hasFocus() )
{
- if (ita.current()->contains(wid)) {
- item_ind = cur_ind;
- isFound = true;
- sub_split = (QSplitter*)it.current();
- }
- }
- cur_ind++;
+ QFocusEvent in( QEvent::FocusIn );
+ QApplication::sendEvent( this, &in );
+ }
+ else
+ activeWidget()->focusWidget()->setFocus();
}
}
+ break;
+ case RemoveWidget:
+ removeWidget( we->widget() );
+ break;
+ default:
+ break;
}
- if (!isFound)
- return (need_pos - splitter_pos);
-
- if (split->orientation() == o) {
- // Find coordinates of near and far sides of the appropriate item relatively current splitter
- int splitter_size = (o == Horizontal ? split->width() : split->height());
- QIntList szList = split->sizes();
- int nb = szList.count();
- int item_rel_pos = 0; // position of near side of item relatively this splitter
- for (int i = 0; i < item_ind; i++) {
- item_rel_pos += szList[i];
- }
- int item_size = szList[item_ind]; // size of item
- int item_pos = splitter_pos + item_rel_pos;
-
- // Resize splitter items to complete the conditions
- if (isBottom) {
- // I. Bottom of splitters stack reached
-
- int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
- split->setSizes(szList);
- // Recompute delta, as some windows can reject given size
- int new_item_rel_pos = 0;
- QIntList szList1 = split->sizes();
- for (int i = 0; i < item_ind; i++) {
- new_item_rel_pos += szList1[i];
- }
- delta = need_pos - (splitter_pos + new_item_rel_pos);
- return delta;
-
- } else {
- // II. Bottom of splitters stack is not yet reached
-
- if (item_ind == 0) { // cannot move in this splitter
- // Process in sub-splitter
- return setPosition(wid, sub_split, o, need_pos, splitter_pos);
- }
-
- int new_prev = 0;
- int new_this = szList[item_ind];
- int new_next = 0;
-
- if (need_pos < splitter_pos) {
- // Set size of all previous workareas to zero <--
- if (item_ind == nb - 1) {
- new_this = splitter_size;
- } else {
- new_next = (splitter_size - new_this) / (nb - item_ind - 1);
- }
- setSizes (szList, item_ind, new_prev, new_this, new_next);
- split->setSizes(szList);
- // Recompute splitter_pos, as some windows can reject given size
- int new_item_rel_pos = 0;
- QIntList szList1 = split->sizes();
- for (int i = 0; i < item_ind; i++) {
- new_item_rel_pos += szList1[i];
- }
- // Process in sub-splitter
- return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
- } else if (need_pos > (splitter_pos + splitter_size)) {
- // Set size of all next workareas to zero -->
- new_prev = (splitter_size - new_this) / item_ind;
- setSizes (szList, item_ind, new_prev, new_this, new_next);
- split->setSizes(szList);
- // Recompute splitter_pos, as some windows can reject given size
- int new_item_rel_pos = 0;
- QIntList szList1 = split->sizes();
- for (int i = 0; i < item_ind; i++) {
- new_item_rel_pos += szList1[i];
- }
- // Process in sub-splitter
- return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
- } else {
- // Set appropriate size of all previous/next items <->
- int new_item_rel_pos = item_rel_pos;
- if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
- // Move item inside splitter into required position <->
- int new_this = szList[item_ind];
- int new_next = 0;
- new_item_rel_pos = need_pos - splitter_pos;
- if ((item_pos + item_size) < need_pos) {
- //new_item_rel_pos = need_pos - (item_pos + item_size);
- new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
- }
- int new_prev = new_item_rel_pos / item_ind;
- if (need_pos < (splitter_pos + item_rel_pos)) {
- // Make previous workareas smaller, next - bigger
- // No problem to keep old size of the widget
- } else {
- // Make previous workareas bigger, next - smaller
- if (new_this > splitter_size - new_item_rel_pos) {
- new_this = splitter_size - new_item_rel_pos;
- }
- }
- if (item_ind == nb - 1) {
- new_this = splitter_size - new_item_rel_pos;
- } else {
- new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
- }
- setSizes (szList, item_ind, new_prev, new_this, new_next);
- split->setSizes(szList);
- // Recompute new_item_rel_pos, as some windows can reject given size
- new_item_rel_pos = 0;
- QIntList szList1 = split->sizes();
- for (int i = 0; i < item_ind; i++) {
- new_item_rel_pos += szList1[i];
- }
- } else {
- // Do nothing
- }
- // Process in sub-splitter
- int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
- if (add_pos == 0)
- return 0;
-
- // this can be if corresponding workarea is first in sub-splitter
- // or sub-splitter has another orientation
-
- // Resize ones again to reach precize position <->
- int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
-
- // Move workarea inside splitter into required position <->
- int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
- new_item_rel_pos, need_pos_1, splitter_pos);
- split->setSizes(szList);
- // Recompute new_item_rel_pos, as some windows can reject given size
- new_item_rel_pos = 0;
- QIntList szList1 = split->sizes();
- for (int i = 0; i < item_ind; i++) {
- new_item_rel_pos += szList1[i];
- }
- delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
- return delta_1;
- }
- }
- } else {
- return setPosition(wid, sub_split, o, need_pos, splitter_pos);
- }
-
- return 0;
}
/*!
- Redistributes space among widgets equally
+ \brief Customize focus in event handler.
+ \param e focus in event
*/
-void QtxWorkstack::distributeSpace( QSplitter* split ) const
+void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
{
- if ( !split )
- return;
+ QFrame::focusInEvent( e );
- QIntList szList = split->sizes();
- int size = ( split->orientation() == Horizontal ?
- split->width() : split->height() ) / szList.count();
- for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
- *it = size;
- split->setSizes( szList );
+ emit activated( activeWidget() );
}
/*!
- Splits widgets vertically
+ \brief Customize mouse press event handler.
+ \param e mouse press event
*/
-void QtxWorkstack::splitVertical()
+void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
{
- split( Qt::Horizontal );
+ QFrame::mousePressEvent( e );
+
+ emit activated( activeWidget() );
}
/*!
- Splits widgets horizontally
+ \brief Called when user presses "Close" button.
*/
-void QtxWorkstack::splitHorizontal()
+void QtxWorkstackArea::onClose()
{
- split( Qt::Vertical );
+ QWidget* wid = activeWidget();
+ if ( wid )
+ wid->close();
}
/*!
- SLOT: called if action "Rename" is activated, changes caption of widget
+ \brief Called when user selects any tab page.
+ \param idx tab page index (not used)
*/
-void QtxWorkstack::onRename()
+void QtxWorkstackArea::onCurrentChanged( int /*idx*/ )
{
- if ( !myWorkWin )
- return;
+ updateCurrent();
- bool ok = false;
- QString newName = QInputDialog::getText( tr( "Rename" ), tr( "Enter new name:" ), QLineEdit::Normal,
- myWorkWin->caption(), &ok, topLevelWidget() );
- if ( ok && !newName.isEmpty() )
- myWorkWin->setCaption( newName );
+ emit activated( activeWidget() );
}
/*!
- Wraps area into new splitter
- \return new splitter
+ \brief Called when user starts tab page dragging.
*/
-QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
+void QtxWorkstackArea::onDragActiveTab()
{
- if ( !area )
- return 0;
-
- QSplitter* pSplit = splitter( area );
- if ( !pSplit )
- return 0;
-
- bool upd = pSplit->isUpdatesEnabled();
- pSplit->setUpdatesEnabled( false );
-
- QIntList szList = pSplit->sizes();
-
- QSplitter* wrap = new QSplitter( 0 );
-#if defined QT_VERSION && QT_VERSION >= 0x30200
- wrap->setChildrenCollapsible( false );
-#endif
- insertWidget( wrap, pSplit, area );
- area->reparent( wrap, QPoint( 0, 0 ), true );
-
- pSplit->setSizes( szList );
-
- pSplit->setUpdatesEnabled( upd );
+ QtxWorkstackChild* c = child( activeWidget() );
+ if ( !c )
+ return;
- return wrap;
+ new QtxWorkstackDrag( workstack(), c );
}
/*!
- Reparenst and adds widget
- \param wid - widget
- \param pWid - parent widget
- \param after - after widget
+ \brief Called when area's child widget container is destroyed.
+ \param obj widget container being destroyed
*/
-void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
+void QtxWorkstackArea::onChildDestroyed( QObject* obj )
{
- if ( !wid || !pWid )
- return;
+ QtxWorkstackChild* child = (QtxWorkstackChild*)obj;
+ myStack->removeWidget( child );
- QWidgetList moveList;
- const QObjectList* lst = pWid->children();
- if ( lst )
+ QWidget* wid = 0;
+ for ( ChildMap::ConstIterator it = myChild.begin(); it != myChild.end() && !wid; ++it )
{
- bool found = false;
- for ( QObjectListIt it( *lst ); it.current(); ++it )
- {
- if ( found && ( it.current()->inherits( "QSplitter" ) ||
- it.current()->inherits( "QtxWorkstackArea" ) ) )
- moveList.append( (QWidget*)it.current() );
- if ( it.current() == after )
- found = true;
- }
+ if ( it.value() == child )
+ wid = it.key();
}
- QMap<QWidget*, bool> map;
- for ( QWidgetListIt it( moveList ); it.current(); ++it )
- {
- map.insert( it.current(), it.current()->isVisibleTo( it.current()->parentWidget() ) );
- it.current()->reparent( 0, QPoint( 0, 0 ), false );
- }
+ myChild.remove( wid );
- wid->reparent( pWid, QPoint( 0, 0 ), true );
+ QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)RemoveWidget, wid ) );
+}
- for ( QWidgetListIt itr( moveList ); itr.current(); ++itr )
- itr.current()->reparent( pWid, QPoint( 0, 0 ), map.contains( itr.current() ) ? map[itr.current()] : false );
+/*!
+ \brief Called when child widget container is shown.
+ \param c child widget container being shown
+*/
+void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
+{
+ setWidgetShown( c->widget(), true );
}
/*!
-* \brief Closes the active window.
+ \brief Called when child widget container is hidden.
+ \param c child widget container being hidden
*/
-void QtxWorkstack::onCloseWindow()
+void QtxWorkstackArea::onChildHidden( QtxWorkstackChild* c )
{
- if ( myWorkWin )
- myWorkWin->close();
- else if( activeWindow() )
- activeWindow()->close();
+ setWidgetShown( c->widget(), false );
}
/*!
- SLOT: called on area is destroyed
- Sets focus to neighbour area
+ \brief Called when child widget container is activated.
+ \param c child widget container being activated
*/
-void QtxWorkstack::onDestroyed( QObject* obj )
+void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
{
- QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
+ setWidgetActive( c->widget() );
+}
- if ( area == myArea )
- myArea = 0;
+/*!
+ \brief Called when child widget container's title is changed.
+ \param c child widget container which title is changed
+*/
+void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
+{
+ updateTab( c->widget() );
+}
- if ( !myArea )
+/*!
+ \brief Update current child widget container.
+
+ Raises widget when active tab page is changed.
+*/
+void QtxWorkstackArea::updateCurrent()
+{
+ QMap<QWidget*, bool> map;
+ for ( QWidgetList::iterator it = myList.begin(); it != myList.end(); ++it )
{
- QtxWorkstackArea* cur = neighbourArea( area );
- if ( cur )
- cur->setFocus();
+ map.insert( *it, isBlocked( *it ) );
+ setBlocked( *it, true );
}
- QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
+ QWidget* cur = child( widget( myBar->tabId( myBar->currentIndex() ) ) );
+ if ( cur )
+ myStack->setCurrentWidget( cur );
+
+ for ( QWidgetList::iterator itr = myList.begin(); itr != myList.end(); ++itr )
+ setBlocked( *itr, map.contains( *itr ) ? map[*itr] : false );
}
/*!
- SLOT: called on window activating
+ \brief Update tab bar.
+ \param wid tab page widget
*/
-void QtxWorkstack::onWindowActivated( QWidget* wid )
+void QtxWorkstackArea::updateTab( QWidget* wid )
{
- const QObject* obj = sender();
- if ( !obj->inherits( "QtxWorkstackArea" ) )
+ int idx = myBar->indexOf( widgetId( wid ) );
+ if ( idx < 0 )
return;
- setActiveArea( (QtxWorkstackArea*)obj );
+ myBar->setTabIcon( idx, wid->windowIcon() );
+ myBar->setTabText( idx, wid->windowTitle() );
}
/*!
- SLOT: called on window deactivating
+ \brief Get child widget by specified identifier.
+ \param id widget ID
+ \return widget or 0, if identifier in invalid
*/
-void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
+QWidget* QtxWorkstackArea::widget( const int id ) const
{
- if ( myArea != area )
- return;
+ QWidget* wid = 0;
+ for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !wid; ++it )
+ {
+ if ( it.value().id == id )
+ wid = it.key();
+ }
+ return wid;
+}
- QPtrList<QtxWorkstackArea> lst;
- areas( mySplit, lst, true );
+/*!
+ \brief Get child widget identifier.
+ \param wid widget
+ \return widget ID or -1 if widget is not found
+*/
+int QtxWorkstackArea::widgetId( QWidget* wid ) const
+{
+ int id = -1;
+ if ( myInfo.contains( wid ) )
+ id = myInfo[wid].id;
+ return id;
+}
- int idx = lst.find( area );
- if ( idx == -1 )
+/*!
+ \brief Get child widget's visibility.
+ \param wid widget
+ \return \c true if widget is visible
+*/
+bool QtxWorkstackArea::widgetVisibility( QWidget* wid ) const
+{
+ bool res = false;
+ if ( myInfo.contains( wid ) )
+ res = myInfo[wid].vis;
+ return res;
+}
+
+/*!
+ \brief Set active child widget.
+ \param wid widget to be set active
+*/
+void QtxWorkstackArea::setWidgetActive( QWidget* wid )
+{
+ int id = widgetId( wid );
+ if ( id < 0 )
return;
- myWin = 0;
- myArea = 0;
+ myBar->setCurrentIndex( myBar->indexOf( id ) );
+}
- QtxWorkstackArea* newArea = neighbourArea( area );
- if ( newArea && newArea->activeWidget() )
- newArea->activeWidget()->setFocus();
+/*!
+ \brief Show/hide child widget.
+ \param wid widget
+ \param on new visibility state
+*/
+void QtxWorkstackArea::setWidgetShown( QWidget* wid, const bool on )
+{
+ if ( isBlocked( wid ) || !myInfo.contains( wid ) || myInfo[wid].vis == on )
+ return;
- QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
+ myInfo[wid].vis = on;
+ updateState();
}
/*!
- Creates and shows popup menu for area
- \param w - area
- \param p - popup position
+ \brief Update internal state.
*/
-void QtxWorkstack::onContextMenuRequested( QWidget* w, QPoint p )
+void QtxWorkstackArea::updateState()
{
- QtxWorkstackArea* anArea = ::qt_cast<QtxWorkstackArea*>( (QObject*)sender() );
- if ( !anArea )
- anArea = activeArea();
+ bool updBar = myBar->updatesEnabled();
+ bool updStk = myStack->updatesEnabled();
+ myBar->setUpdatesEnabled( false );
+ myStack->setUpdatesEnabled( false );
- if ( !anArea )
- return;
+ bool block = myBar->signalsBlocked();
+ myBar->blockSignals( true );
- QWidgetList lst = anArea->widgetList();
- if ( lst.isEmpty() )
- return;
+ QWidget* prev = activeWidget();
- myWorkWin = w;
- myWorkArea = anArea;
+ int idx = 0;
+ for ( QWidgetList::iterator it = myList.begin(); it != myList.end(); ++it )
+ {
+ QWidget* wid = *it;
+ int id = widgetId( wid );
- QPopupMenu* pm = new QPopupMenu();
-
- if ( lst.count() > 1 )
+ if ( id < 0 )
+ continue;
+
+ bool vis = widgetVisibility( wid );
+
+ int cIdx = myBar->indexOf( id );
+ if ( cIdx != -1 && ( !vis || myBar->indexOf( id ) != idx ) )
+ myBar->removeTab( cIdx );
+
+ if ( myBar->indexOf( id ) == -1 && vis )
+ myBar->setTabId( myBar->insertTab( idx, wid->windowTitle() ), id );
+
+ updateTab( wid );
+
+ bool block = isBlocked( wid );
+ setBlocked( wid, true );
+
+ QtxWorkstackChild* cont = child( wid );
+
+ if ( !vis )
+ myStack->removeWidget( cont );
+ else if ( myStack->indexOf( cont ) < 0 )
+ myStack->addWidget( cont );
+
+ if ( vis )
+ idx++;
+
+ setBlocked( wid, block );
+ }
+
+ int curId = widgetId( prev );
+ if ( myBar->indexOf( curId ) < 0 )
{
- myActionsMap[SplitVertical]->addTo( pm );
- myActionsMap[SplitHorizontal]->addTo( pm );
- pm->insertSeparator();
+ QWidget* wid = 0;
+ int pos = myList.indexOf( prev );
+ for ( int i = pos - 1; i >= 0 && !wid; i-- )
+ {
+ if ( widgetVisibility( myList.at( i ) ) )
+ wid = myList.at( i );
+ }
+
+ for ( int j = pos + 1; j < (int)myList.count() && !wid; j++ )
+ {
+ if ( widgetVisibility( myList.at( j ) ) )
+ wid = myList.at( j );
+ }
+
+ if ( wid )
+ curId = widgetId( wid );
}
- if ( w )
+ myBar->setCurrentIndex( myBar->indexOf( curId ) );
+
+ myBar->blockSignals( block );
+
+ updateCurrent();
+
+ myBar->updateActiveState();
+
+ myBar->setUpdatesEnabled( updBar );
+ myStack->setUpdatesEnabled( updStk );
+ if ( updBar )
+ myBar->update();
+ if ( updStk )
+ myStack->update();
+
+ QResizeEvent re( myBar->size(), myBar->size() );
+ QApplication::sendEvent( myBar, &re );
+
+ if ( isEmpty() )
+ {
+ hide();
+ emit deactivated( this );
+ }
+ else
{
- myActionsMap[Close]->addTo( pm );
- myActionsMap[Rename]->addTo( pm );
+ show();
+ if ( prev != activeWidget() )
+ emit activated( activeWidget() );
}
+}
- Qtx::simplifySeparators( pm );
+/*!
+ \brief Generate unique widget identifier.
+ \return first non shared widget ID
+*/
+int QtxWorkstackArea::generateId() const
+{
+ QMap<int, int> map;
- if ( pm->count() )
- pm->exec( p );
+ for ( WidgetInfoMap::const_iterator it = myInfo.begin(); it != myInfo.end(); ++it )
+ map.insert( it.value().id, 0 );
- delete pm;
+ int id = 0;
+ while ( map.contains( id ) )
+ id++;
- myWorkWin = 0;
- myWorkArea = 0;
+ return id;
}
/*!
- Custom child event handler, inserts widget to active or current area
+ \brief Check if the child wiget is blocked.
+ \param wid widget
+ \return \c true if the widget is blocked
*/
-void QtxWorkstack::childEvent( QChildEvent* e )
+bool QtxWorkstackArea::isBlocked( QWidget* wid ) const
{
- if ( e->inserted() && e->child()->isWidgetType() )
- {
- QWidget* w = (QWidget*)e->child();
- if ( w && w != mySplit )
- {
- targetArea()->insertWidget( w );
- return;
- }
- }
- QWidget::childEvent( e );
+ return myBlock.contains( wid );
}
/*!
- Handler of custom events
+ \brief Block widget.
+ \param wid widget
+ \param on new blocked state
*/
-void QtxWorkstack::customEvent( QCustomEvent* e )
+void QtxWorkstackArea::setBlocked( QWidget* wid, const bool on )
{
- updateState();
+ if ( on )
+ myBlock.insert( wid, 0 );
+ else
+ myBlock.remove( wid );
}
/*!
- \return splitter corresponding to area
- \param area
+ \brief Get child widget container.
+ \param wid child widget
+ \return child widget container corresponding to the \a wid
*/
-QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
+QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
{
- if ( !area )
- return 0;
+ QtxWorkstackChild* res = 0;
+ if ( myChild.contains( wid ) )
+ res = myChild[wid];
+ return res;
+}
- QSplitter* split = 0;
+/*!
+ \fn void QtxWorkstackArea::activated( QWidget* w )
+ \brief Emitted when child widget is activated.
+ \param w child widget being activated
+*/
- QWidget* wid = area->parentWidget();
- if ( wid && wid->inherits( "QSplitter" ) )
- split = (QSplitter*)wid;
+/*!
+ \fn void QtxWorkstackArea::contextMenuRequested( QWidget* w, QPoint p )
+ \brief Emitted when context popup menu is requested.
+ \param w child widget popup menu requested for
+ \param p point popup menu to be shown at
+*/
- return split;
-}
+/*!
+ \fn void QtxWorkstackArea::deactivated( QtxWorkstackArea* wa )
+ \brief Emitted when workarea is deactivated.
+ \param wa workarea being deactivated
+*/
/*!
- Fills list with children splitters
- \param split - parent splitter
- \param splitList - list to be filled with
- \param rec - recursive search of children
+ \class QtxWorkstackChild
+ \internal
+ \brief Workarea child widget container.
*/
-void QtxWorkstack::splitters( QSplitter* split, QPtrList<QSplitter>& splitList, const bool rec ) const
+
+/*!
+ \brief Constructor.
+ \param wid child widget
+ \param parent parent widget
+ \param f widget flags
+*/
+QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent, Qt::WindowFlags f )
+: QWidget( parent ),
+ myWidget( wid )
{
- if ( !split )
- return;
+ myWidget->setParent( this, f );
+ myWidget->installEventFilter( this );
+ QVBoxLayout* base = new QVBoxLayout( this );
+ base->addWidget( myWidget );
- const QObjectList* objs = split->children();
- if ( objs )
- {
- for ( QObjectListIt it( *objs ); it.current(); ++it )
- {
- if ( rec )
- splitters( (QSplitter*)it.current(), splitList, rec );
- if ( it.current()->inherits( "QSplitter" ) )
- splitList.append( (QSplitter*)it.current() );
- }
- }
+ connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
}
/*!
- Fills list with children areas
- \param split - parent splitter
- \param areaList - list to be filled with
- \param rec - recursive search of children
+ \brief Destructor.
*/
-void QtxWorkstack::areas( QSplitter* split, QPtrList<QtxWorkstackArea>& areaList, const bool rec ) const
+QtxWorkstackChild::~QtxWorkstackChild()
{
- if ( !split )
+ QApplication::instance()->removeEventFilter( this );
+
+ if ( !widget() )
return;
- const QObjectList* objs = split->children();
- if ( objs )
- {
- for ( QObjectListIt it( *objs ); it.current(); ++it )
- {
- if ( it.current()->inherits( "QtxWorkstackArea" ) )
- areaList.append( (QtxWorkstackArea*)it.current() );
- else if ( rec && it.current()->inherits( "QSplitter" ) )
- areas( (QSplitter*)it.current(), areaList, rec );
- }
- }
+ disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
+
+ widget()->hide();
+ widget()->removeEventFilter( this );
+
+ widget()->setParent( 0 );
}
/*!
- \return active area
+ \brief Get child widget.
+ \return child widget
*/
-QtxWorkstackArea* QtxWorkstack::activeArea() const
+QWidget* QtxWorkstackChild::widget() const
{
- return myArea;
+ return myWidget;
}
/*!
- \return active area or current area or create new area of there is no one
+ \brief Custom event filter.
+
+ Process events from child widgets.
+
+ \param o event receiver widget
+ \param e event
+ \return \c true if event should be filtered (stop further processing)
*/
-QtxWorkstackArea* QtxWorkstack::targetArea()
+bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
{
- QtxWorkstackArea* area = activeArea();
- if ( !area )
- area = currentArea();
- if ( !area )
+ if ( o->isWidgetType() )
{
- QPtrList<QtxWorkstackArea> lst;
- areas( mySplit, lst );
- if ( !lst.isEmpty() )
- area = lst.first();
+ if ( e->type() == QEvent::WindowTitleChange || e->type() == QEvent::WindowIconChange )
+ emit captionChanged( this );
+
+ if ( !e->spontaneous() && ( e->type() == QEvent::Show || e->type() == QEvent::ShowToParent ) )
+ emit shown( this );
+
+ if ( !e->spontaneous() && ( e->type() == QEvent::Hide || e->type() == QEvent::HideToParent ) )
+ emit hidden( this );
+
+ if ( e->type() == QEvent::FocusIn )
+ emit activated( this );
}
+ return QWidget::eventFilter( o, e );
+}
- if ( !area )
- area = createArea( mySplit );
+/*!
+ \brief Called when child widget is destroyed.
+ \param obj child widget being destroyed
+*/
+void QtxWorkstackChild::onDestroyed( QObject* obj )
+{
+ if ( obj != widget() )
+ return;
- return area;
+ myWidget = 0;
+ deleteLater();
}
/*!
- \return current area (that has focus)
+ \brief Customize child event handler.
+ \param e child event
*/
-QtxWorkstackArea* QtxWorkstack::currentArea() const
+void QtxWorkstackChild::childEvent( QChildEvent* e )
{
- QtxWorkstackArea* area = 0;
- QWidget* wid = focusWidget();
- while ( wid && !area )
+ if ( e->removed() && e->child() == widget() )
{
- if ( wid->inherits( "QtxWorkstackArea" ) )
- area = (QtxWorkstackArea*)wid;
- wid = wid->parentWidget();
+ myWidget = 0;
+ deleteLater();
}
-
- return area;
+ QWidget::childEvent( e );
}
/*!
- Creates new area
+ \fn void QtxWorkstackChild::shown( QtxWorkstackChild* w )
+ \brief Emitted when child widget is shown.
+ \param w child widget container
*/
-QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
-{
- QtxWorkstackArea* area = new QtxWorkstackArea( parent );
-
- connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
- connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
- connect( area, SIGNAL( contextMenuRequested( QWidget*, QPoint ) ),
- this, SLOT( onContextMenuRequested( QWidget*, QPoint ) ) );
- connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
-
- return area;
-}
/*!
- Sets area as active
- \param area
+ \fn void QtxWorkstackChild::hidden( QtxWorkstackChild* w )
+ \brief Emitted when child widget is hidden.
+ \param w child widget container
*/
-void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
-{
- QWidget* oldCur = myWin;
- QtxWorkstackArea* oldArea = myArea;
+/*!
+ \fn void QtxWorkstackChild::activated( QtxWorkstackChild* w )
+ \brief Emitted when child widget is activated.
+ \param w child widget container
+*/
- myArea = area;
+/*!
+ \fn void QtxWorkstackChild::captionChanged( QtxWorkstackChild* w )
+ \brief Emitted when child widget's title is changed.
+ \param w child widget container
+*/
- if ( myArea != oldArea )
- {
- if ( oldArea )
- oldArea->updateActiveState();
- if ( myArea )
- myArea->updateActiveState();
- }
+/*!
+ \class QtxWorkstackTabBar
+ \internal
+ \brief Workstack tab bar widget
+*/
- if ( myArea )
- myWin = myArea->activeWidget();
+/*!
+ \brief Constructor.
+ \param parent parent widget
+*/
+QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
+: QTabBar( parent ),
+ myId( -1 )
+{
+ setDrawBase( true );
+ setElideMode( Qt::ElideNone );
- if ( myWin && oldCur != myWin )
- emit windowActivated( myWin );
+ connect( this, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
}
/*!
- \return neighbour area
- \param area - area to search neighbour
+ \brief Destructor.
*/
-QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
+QtxWorkstackTabBar::~QtxWorkstackTabBar()
{
- QPtrList<QtxWorkstackArea> lst;
- areas( mySplit, lst, true );
- int pos = lst.find( area );
- if ( pos < 0 )
- return 0;
-
- QtxWorkstackArea* na = 0;
- for ( int i = pos - 1; i >= 0 && !na; i-- )
- {
- if ( !lst.at( i )->isEmpty() )
- na = lst.at( i );
- }
-
- for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
- {
- if ( !lst.at( j )->isEmpty() )
- na = lst.at( j );
- }
- return na;
}
/*!
- \return area covering point
- \param p - point
+ \brief Get tab page identifier.
+ \param index tab page index
+ \return tab page ID or -1 if \a index is out of range
*/
-QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
+int QtxWorkstackTabBar::tabId( const int index ) const
{
- QtxWorkstackArea* area = 0;
- QPtrList<QtxWorkstackArea> lst;
- areas( mySplit, lst, true );
- for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current() && !area; ++it )
- {
- QtxWorkstackArea* cur = it.current();
- QRect r = cur->geometry();
- if ( cur->parentWidget() )
- r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
- if ( r.contains( p ) )
- area = cur;
- }
- return area;
+ QVariant v = tabData( index );
+ if ( !v.canConvert( QVariant::Int ) )
+ return -1;
+ return v.toInt();
}
/*!
- Update
+ \brief Set tab page identifier.
+ \param index tab page index
+ \param id tab page ID
*/
-void QtxWorkstack::updateState()
+void QtxWorkstackTabBar::setTabId( const int index, const int id )
{
- updateState( mySplit );
+ setTabData( index, id );
}
/*!
- Update splitters
+ \brief Get tab page index by specified identifier.
+ \param id tab page ID
+ \return tab page index or -1 if not found
*/
-void QtxWorkstack::updateState( QSplitter* split )
+int QtxWorkstackTabBar::indexOf( const int id ) const
{
- QPtrList<QSplitter> recList;
- splitters( split, recList, false );
- for ( QPtrListIterator<QSplitter> itr( recList ); itr.current(); ++itr )
- updateState( itr.current() );
-
- QPtrList<QSplitter> splitList;
- splitters( split, splitList, false );
-
- QPtrList<QtxWorkstackArea> areaList;
- areas( split, areaList, false );
-
- bool vis = false;
- for ( QPtrListIterator<QtxWorkstackArea> it( areaList ); it.current(); ++it )
+ int index = -1;
+ for ( int i = 0; i < (int)count() && index < 0; i++ )
{
- if ( it.current()->isEmpty() )
- it.current()->hide();
- else
- {
- it.current()->show();
- vis = true;
- }
+ if ( tabId( i ) == id )
+ index = i;
}
-
- if ( split == mySplit )
- return;
-
- for ( QPtrListIterator<QSplitter> iter( splitList ); iter.current() && !vis; ++iter )
- vis = iter.current()->isVisibleTo( iter.current()->parentWidget() );
-
- if ( areaList.isEmpty() && splitList.isEmpty() )
- delete split;
- else if ( vis )
- split->show();
- else
- split->hide();
+ return index;
}
/*!
- Gets splitter info for debug
- \param split - splitter
- \param info - string to be filled with info
+ \brief Check if the tab bar is active.
+ \return \c true if tab bar is active
*/
-void QtxWorkstack::splitterInfo( QSplitter* split, QString& info ) const
+bool QtxWorkstackTabBar::isActive() const
{
- if ( !split )
- return;
-
- const QObjectList* objs = split->children();
- if ( objs )
- {
- // make up a sizes string: integer values are separated by ':' char
- QValueList<int> sizes = split->sizes();
- QString sizesStr;
- for ( QValueList<int>::Iterator sIt = sizes.begin(); sIt != sizes.end(); ++sIt ) {
- if ( *sIt > 1 ) // size 1 pixel usually means empty Workstack area, which will NOT be re-created,
- sizesStr += QString( ":%1" ).arg( *sIt ); // so we don't need to store its size
- }
- if ( !sizesStr.isEmpty() ) // cut the first ':'
- sizesStr = sizesStr.right( sizesStr.length()-1 );
-
- // count all QSplitter-s and QtxWorkstackArea-s
- // int nChilds( 0 );
- // QObjectListIt it( *objs );
- // for ( ; it.current(); ++it )
- // {
- // if ( it.current()->inherits( "QSplitter" ) ||
- // it.current()->inherits( "QtxWorkstackArea" ) )
- // nChilds++;
- // }
-
- info += QString( "(splitter orientation=%1 sizes=%3 " ).arg( split->orientation() ).arg( sizesStr );
-
- for ( QObjectListIt it( *objs ); it.current(); ++it )
- {
- if ( it.current()->inherits( "QSplitter" ) )
- splitterInfo( (QSplitter*)it.current(), info );
- else if ( it.current()->inherits( "QtxWorkstackArea" ) ) {
- QtxWorkstackArea* area = (QtxWorkstackArea*)it.current();
- if ( area->isEmpty() )
- continue;
- info += QString( "(views active='%1'" ).arg( area->activeWidget()->name() );
- QWidgetList views = area->widgetList();
- for ( QWidgetListIt wIt( views ); wIt.current(); ++wIt )
- info += QString( " '%1'" ).arg( wIt.current()->name() );
- info += ')';
- }
- }
- }
- info += ')';
+ return myActive;
}
-
-//Cuts starting '(' symbol and ending '(' symbol
-void cutBrackets( QString& parameters )
+/*!
+ \brief Set tab bar active/inactive.
+ \param on new active state
+*/
+void QtxWorkstackTabBar::setActive( const bool on )
{
- QChar c1 = parameters[0];
- QChar c2 = parameters[int(parameters.length()-1)];
- if ( !parameters.isEmpty() && c1 == '(' && c2 == ')' )
- parameters = parameters.mid( 1, parameters.length()-2 );
+ if ( myActive == on )
+ return;
+
+ myActive = on;
+ updateActiveState();
}
-/*
- for strings like "(splitter orientation=0 children=2 sizes=332:478" returns values of
- parameters. For example, getValue( example, "children" ) returns "2"
- getValue( example, "sizes" ) returns "332:478"
+/*!
+ \brief Update tab bar according to the 'active' state.
*/
-QString getValue( const QString& str, const QString& valName )
+void QtxWorkstackTabBar::updateActiveState()
{
- int i = str.find( valName );
- if ( i != -1 ) {
- int equal_i = str.find( '=', i );
- if ( equal_i != -1 ) {
- int space_i = str.find( ' ', ++equal_i );
- if ( space_i != -1 )
- return str.mid( equal_i, space_i-equal_i );
- }
- }
- return QString( "" );
+ QColor bc = palette().color( QPalette::Text );
+ QColor ac = isActive() ? palette().color( QPalette::Highlight ) : bc;
+ for ( int i = 0; i < (int)count(); i++ )
+ setTabTextColor( i, currentIndex() == i ? ac : bc );
}
-/*
- checks format of splitter parameters string
+/*!
+ \brief Called when current tab page is changed.
+ \param idx tab page index (not used)
*/
-bool checkFormat( const QString& parameters )
+void QtxWorkstackTabBar::onCurrentChanged( int /*index*/ )
{
- QString params( parameters );
- // 1. begins and ends with brackets
- QChar c1 = params[0];
- QChar c2 = params[int(params.length()-1)];
- bool ok = ( c1 == '(' && c2 == ')' );
- if ( !ok ) return ok;
- ::cutBrackets( params );
- // 2. has splitter word
- ok = ( params.left( 8 ) == "splitter" );
- if ( !ok ) return ok;
- // 3. has children? = '(' is found
- int i = params.find( '(' );
- ok = i != -1;
- if ( !ok ) return ok;
- params = params.left( i ); // cut all children, they will be checked later
- // 4. has orientation word and correct value
- ::getValue( params, "orientation" ).toInt( &ok );
- if ( !ok ) return ok;
- // 5. has sizes word and values
- ok = ! ::getValue( params, "sizes" ).isEmpty();
- if ( !ok ) return ok;
- // 6. check children -> number of '(' == number of ')' in original string
- ok = ( parameters.contains( '(' ) == parameters.contains( ')' ) );
- return ok;
+ updateActiveState();
}
-/*
- Returns children of splitter in a list. Children are separated by '(' and ')' symbols
+/*!
+ \brief Customize mouse move event handler.
+ \param e mouse event
*/
-QStringList getChildren( const QString& str )
+void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
{
- QStringList lst;
- if ( !str.startsWith( "(" ) )
- return lst;
-
- int i = 1,
- nOpen = 1, // count brackets: '(' increments nOpen, ')' decrements
- start = 0;
- while ( i < (int)str.length() )
+ if ( myId != -1 && !tabRect( indexOf( myId ) ).contains( e->pos() ) )
{
- if ( str[i] == '(' )
- {
- nOpen++;
- if ( nOpen == 1 )
- start = i;
- }
- else if ( str[i] == ')' )
- {
- nOpen--;
- if ( nOpen == 0 )
- lst.append( str.mid( start, i-start+1 ) );
- }
- i++;
+ myId = -1;
+ emit dragActiveTab();
}
- return lst;
+ QTabBar::mouseMoveEvent( e );
}
-// for a string like "views active='AnotherView' 'GLView' 'AnotherView'"
-// getViewName( example, 0 ) returns "GLView",
-// getViewName( example, 1 ) -> "AnotherView", etc.
-QString getViewName( const QString& str, int i )
+/*!
+ \brief Customize mouse press event handler.
+ \param e mouse event
+*/
+void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
{
- QRegExp exp( "\\s'\\w+'" );
- int start = 0; // start index of view name in the string
- int num = 0 ; // index of found match
- while ( ( start = exp.search( str, start ) ) != -1 && num < i ) {
- start += exp.matchedLength();
- num ++;
- }
- if ( start != -1 ) // +2 and -3 avoid starting space and starting and ending ' symbols
- return str.mid( start+2, exp.matchedLength()-3 );
-
- return QString( "" );
-}
+ QTabBar::mousePressEvent( e );
-// returns widget with given name
-QWidget* getView( const QWidget* parent, const QString& aName )
-{
- QWidget* view = 0;
- QObjectList *l = parent->topLevelWidget()->queryList( "QWidget", aName, false, true );
- if ( !l->isEmpty() )
- view = ::qt_cast<QWidget*>( l->first() );
- delete l;
- return view;
+ if ( e->button() == Qt::LeftButton )
+ myId = tabId( currentIndex() );
}
/*!
- Installs a splitter described by given parameters string
+ \brief Customize mouse release event handler.
+ \param e mouse event
*/
-void QtxWorkstack::setSplitter( QSplitter* splitter, const QString& parameters, QMap< QSplitter*, QValueList<int> >& sMap )
+void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
{
- if ( !::checkFormat( parameters ) ) {
- printf( "\nInvalid format of workstack parameters. Positions of viewers can not be restored.\n" );
- return;
- }
-
- QString params( parameters );
- ::cutBrackets( params );
-
- // get splitter sizes and store it in the map for future setting
- QValueList<int> sizes;
- QStringList sizesLst = QStringList::split( ':', ::getValue( params, "sizes" ) );
- QStringList::Iterator it;
- for ( it = sizesLst.begin(); it != sizesLst.end(); ++it )
- sizes.append( (*it).toInt() );
- sMap[ splitter ] = sizes;
+ QTabBar::mouseReleaseEvent( e );
- // set orientation of splitter
- int orient = ::getValue( params, "orientation" ).toInt();
- splitter->setOrientation( (Qt::Orientation)orient );
+ myId = -1;
- // get children
- QString options = params.left( params.find( '(' ) );
- QString childrenStr = params.right( params.length()-options.length() );
- QStringList children = ::getChildren( childrenStr );
+ if ( e->button() == Qt::RightButton )
+ emit contextMenuRequested( e->globalPos() );
+}
- // debug output..
- // printf (" splitter orient=%d, sizes_count=%d, children=%d\n", orient, sizes.count(), children.count() );
- // for ( QStringList::Iterator tit = children.begin(); tit != children.end(); ++tit )
- // printf (" |-> child = [%s]\n", (*tit).latin1() );
+/*!
+ \brief Customize context menu event handler.
+ \param e context menu event
+*/
+void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
+{
+ if ( e->reason() != QContextMenuEvent::Mouse )
+ emit contextMenuRequested( e->globalPos() );
+}
- for ( QStringList::Iterator it = children.begin(); it != children.end(); ++it ) {
- if ( (*it).startsWith( "(splitter" ) ) {
- QSplitter* newSplitter = new QSplitter( splitter );
- setSplitter( newSplitter, *it, sMap );
- }
- else if ( (*it).startsWith( "(views" ) ) {
- QtxWorkstackArea* newArea = createArea( splitter );
- QString activeViewName = ::getValue( *it, "active" );
- QWidget* activeView( 0 );
- activeViewName = activeViewName.mid( 1, activeViewName.length()-2 ); // chop off ' symbols
- int i = 0;
- QString viewName = ::getViewName( *it, i );
- while ( !viewName.isEmpty() ) {
- if ( QWidget* view = ::getView( splitter, viewName ) ) {
- newArea->insertWidget( view );
- if ( activeViewName == view->name() )
- activeView = view;
- }
- viewName = ::getViewName( *it, ++i );
- }
- if ( activeView )
- newArea->setActiveWidget( activeView );
- }
+/*
+void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
+{
+ if ( currentTab() != t->identifier() )
+ {
+ QFont fnt = p->font();
+ fnt.setUnderline( false );
+ p->setFont( fnt );
}
+ QTabBar::paintLabel( p, br, t, has_focus );
}
+*/
/*!
- Restore workstack's configuration stored in 'parameters' string
+ \fn void QtxWorkstackTabBar::dragActiveTab()
+ \brief Emitted when dragging operation is started.
*/
-QtxWorkstack& QtxWorkstack::operator<<( const QString& parameters )
-{
- // clear the main splitter - remove all child splitters and empty areas from it
- QPtrList<QSplitter> splitList;
- QPtrList<QtxWorkstackArea> areaList;
- splitters( mySplit, splitList, false );
- areas( mySplit, areaList, false );
- for ( QPtrListIterator<QSplitter> iter( splitList ); iter.current(); ++iter )
- delete iter.current();
- for ( QPtrListIterator<QtxWorkstackArea> it( areaList ); it.current(); ++it )
- if ( it.current()->isEmpty() )
- delete it.current();
-
- // restore splitter recursively
- QMap< QSplitter*, QValueList<int> > sMap;
- setSplitter( mySplit, parameters, sMap );
- // now mySplit may contains empty area (where all views were located before restoring)
- // in order setSize to work correctly we have to exclude this area
- areaList.clear();
- areas( mySplit, areaList, false );
- for ( QPtrListIterator<QtxWorkstackArea> delIt( areaList ); delIt.current(); ++delIt )
- if ( delIt.current()->isEmpty() )
- delete delIt.current();
+/*!
+ \fn void QtxWorkstackTabBar::contextMenuRequested( QPoint p )
+ \brief Emitted when context popup menu is requested.
+ \param p point popup menu to be shown at
+*/
- qApp->processEvents();
+/*!
+ \class QtxWorkstack
+ \brief Workstack widget.
- // restore splitters' sizes (map of sizes is filled in setSplitters)
- for ( QMap< QSplitter*, QValueList<int> >::Iterator itm = sMap.begin(); itm != sMap.end(); ++itm )
- itm.key()->setSizes( itm.data() );
+ Organizes the child widgets in the tabbed space.
+ Allows splitting the working area to arrange the child widgets in
+ arbitrary way. Any widgets can be moved to another working area with
+ drag-n-drop operation.
- return (*this);
-}
+ This widget can be used as workspace of the application main window,
+ for example, as kind of implementation of multi-document interface.
+*/
/*!
- Example of string produced by operator>> :
- "(splitter orientation=0 sizes=186:624 (views active='OCCViewer_0_0' 'OCCViewer_0_0')
-/ (views active='VTKViewer_0_0' 'VTKViewer_0_0'))"
+ \brief Constructor.
+ \param parent parent widget
*/
-QtxWorkstack& QtxWorkstack::operator>>( QString& outParameters )
+QtxWorkstack::QtxWorkstack( QWidget* parent )
+: QWidget( parent ),
+ myWin( 0 ),
+ myArea( 0 ),
+ myWorkWin( 0 ),
+ myWorkArea( 0 )
{
- splitterInfo( mySplit, outParameters );
- return (*this);
-}
+ myActionsMap.insert( SplitVertical, new QtxAction( QString(), tr( "Split vertically" ), 0, this ) );
+ myActionsMap.insert( SplitHorizontal, new QtxAction( QString(), tr( "Split horizontally" ), 0, this ) );
+ myActionsMap.insert( Close, new QtxAction( QString(), tr( "Close" ), 0, this ) );
+ myActionsMap.insert( Rename, new QtxAction( QString(), tr( "Rename" ), 0, this ) );
+ connect( myActionsMap[SplitVertical], SIGNAL( triggered( bool ) ), this, SLOT( splitVertical() ) );
+ connect( myActionsMap[SplitHorizontal], SIGNAL( triggered( bool ) ), this, SLOT( splitHorizontal() ) );
+ connect( myActionsMap[Close], SIGNAL( triggered( bool ) ), this, SLOT( onCloseWindow() ) );
+ connect( myActionsMap[Rename], SIGNAL( triggered( bool ) ), this, SLOT( onRename() ) );
-/*!
- Constructor
-*/
-QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
-: QWidget( parent )
-{
QVBoxLayout* base = new QVBoxLayout( this );
+ base->setMargin( 0 );
- QHBox* top = new QHBox( this );
- base->addWidget( top );
-
- myBar = new QtxWorkstackTabBar( top );
-
- QPushButton* close = new QPushButton( top );
- close->setPixmap( style().stylePixmap( QStyle::SP_TitleBarCloseButton ) );
- close->setAutoDefault( true );
- close->setFlat( true );
- myClose = close;
-
- top->setStretchFactor( myBar, 1 );
-
- myStack = new QWidgetStack( this );
-
- base->addWidget( myStack, 1 );
+ mySplit = new QSplitter( this );
+ mySplit->setChildrenCollapsible( false );
+ base->addWidget( mySplit );
+}
- connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
- connect( myBar, SIGNAL( selected( int ) ), this, SLOT( onSelected( int ) ) );
- connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
- connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
+/*!
+ \brief Destructor.
+*/
+QtxWorkstack::~QtxWorkstack()
+{
+}
- updateState();
+/*!
+ \brief Get all child widgets in all workareas.
+ \return list of widgets in all workareas
+*/
+QWidgetList QtxWorkstack::windowList() const
+{
+ QList<QtxWorkstackArea*> lst;
+ areas( mySplit, lst, true );
- updateActiveState();
+ QWidgetList widList;
+ for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end(); ++it )
+ {
+ QWidgetList wids = (*it)->widgetList();
+ for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
+ widList.append( *itr );
+ }
- qApp->installEventFilter( this );
+ return widList;
}
/*!
- Destructor
+ \brief Get all child widgets in the active workarea.
+ \return list of widgets in active workarea
*/
-QtxWorkstackArea::~QtxWorkstackArea()
+QWidgetList QtxWorkstack::splitWindowList() const
{
- qApp->removeEventFilter( this );
+ return myArea ? myArea->widgetList() : QWidgetList();
}
/*!
- \return true if area is empty
+ \brief Get active widget.
+ \return active widget
*/
-bool QtxWorkstackArea::isEmpty() const
+QWidget* QtxWorkstack::activeWindow() const
{
- bool res = false;
- for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !res; ++it )
- res = it.data().vis;
- return !res;
+ return myWin;
}
/*!
- Adds widget to area
- \param wid - widget
- \param idx - index
+ \brief Split workstack.
+
+ Splitting is possible only if there are two or more widgets in the workarea.
+ This function splits current workarea to two new ones.
+
+ \param o splitting orientation (Qt::Orientation)
*/
-void QtxWorkstackArea::insertWidget( QWidget* wid, const int idx )
+void QtxWorkstack::split( const int o )
{
- if ( !wid )
+ QtxWorkstackArea* area = myWorkArea;
+ if ( !area )
+ area = activeArea();
+ if ( !area )
return;
- int pos = myList.find( wid );
- if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
+ if ( area->widgetList().count() < 2 )
return;
- myList.removeRef( wid );
- pos = idx < 0 ? myList.count() : idx;
- myList.insert( QMIN( pos, (int)myList.count() ), wid );
- if ( !myInfo.contains( wid ) )
- {
- QtxWorkstackChild* child = new QtxWorkstackChild( wid, myStack );
- myChild.insert( wid, child );
- myInfo.insert( wid, WidgetInfo() );
- myInfo[wid].id = generateId();
- myInfo[wid].vis = wid->isVisibleTo( wid->parentWidget() );
+ QWidget* curWid = area->activeWidget();
+ if ( !curWid )
+ return;
- connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
- connect( wid, SIGNAL( destroyed() ), this, SLOT( onWidgetDestroyed() ) );
- connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
- connect( child, SIGNAL( hided( QtxWorkstackChild* ) ), this, SLOT( onChildHided( QtxWorkstackChild* ) ) );
- connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
- connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
- }
+ QSplitter* s = splitter( area );
+ QList<QtxWorkstackArea*> areaList;
+ areas( s, areaList );
- updateState();
+ QList<QSplitter*> splitList;
+ splitters( s, splitList );
- setWidgetActive( wid );
- wid->setFocus();
-}
+ QSplitter* trg = 0;
+ if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
+ trg = s;
-/*!
- Creates and shows popup menu for area
- \param p - popup position
-*/
-void QtxWorkstackArea::onContextMenuRequested( QPoint p )
-{
- const QtxWorkstackTabBar* bar = ::qt_cast<const QtxWorkstackTabBar*>( sender() );
- if ( !bar )
+ if ( !trg )
+ trg = wrapSplitter( area );
+
+ if ( !trg )
return;
- QWidget* wid = 0;
- QTab* tab = myBar->tabAt( tabAt( p ) );
- if ( tab )
- wid = widget( tab->identifier() );
+ trg->setOrientation( (Qt::Orientation)o );
- emit contextMenuRequested( wid, p );
+ QtxWorkstackArea* newArea = createArea( 0 );
+ trg->insertWidget( trg->indexOf( area ) + 1, newArea );
+
+ area->removeWidget( curWid );
+ newArea->insertWidget( curWid );
+
+ distributeSpace( trg );
+
+ curWid->show();
+ curWid->setFocus();
}
/*!
- SLOT: called when widget added to area is destroyed, removes widget from area
+ \brief Split workarea of the given widget on two parts.
+
+ Splitting is possible only if there are two or more widgets in the workarea.
+ This function splits current workarea to two new ones.
+
+ \param wid widget belonging to the workstack
+ \param o splitting orientation type (Qt::Orientation)
+ \param type splitting type (QtxWorkstack::SplitType)
*/
-void QtxWorkstackArea::onWidgetDestroyed()
+void QtxWorkstack::Split( QWidget* wid, const Qt::Orientation o, const SplitType type )
{
- if ( sender() )
- removeWidget( (QWidget*)sender(), false );
+ if (!wid) return;
+
+ // find area of the given widget
+ QtxWorkstackArea* area = NULL;
+ QList<QtxWorkstackArea*> allAreas;
+ areas(mySplit, allAreas, true);
+
+
+ for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
+ {
+ if ( (*it)->contains( wid ) )
+ area = *it;
+ }
+
+ if ( !area )
+ return;
+
+ QWidgetList wids = area->widgetList();
+ if ( wids.count() < 2 )
+ return;
+
+ QSplitter* s = splitter( area );
+ QList<QtxWorkstackArea*> areaList;
+ areas( s, areaList );
+
+ QList<QSplitter*> splitList;
+ splitters(s, splitList);
+
+ QSplitter* trg = 0;
+ if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
+ trg = s;
+
+ if (!trg) trg = wrapSplitter(area);
+ if (!trg) return;
+
+ trg->setOrientation(o);
+
+ QtxWorkstackArea* newArea = createArea(0);
+ insertWidget(newArea, trg, area);
+
+ switch ( type )
+ {
+ case SplitStay:
+ for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
+ {
+ QWidget* wid_i = *itr;
+ if ( wid_i != wid )
+ {
+ area->removeWidget( wid_i );
+ newArea->insertWidget( wid_i );
+ }
+ }
+ break;
+ case SplitAt:
+ {
+ QWidgetList::iterator itr = wids.begin();
+ for ( ; itr != wids.end() && *itr != wid; ++itr )
+ {
+ }
+ for ( ; itr != wids.end(); ++itr )
+ {
+ area->removeWidget( *itr );
+ newArea->insertWidget( *itr );
+ }
+ }
+ break;
+ case SplitMove:
+ area->removeWidget( wid );
+ newArea->insertWidget( wid );
+ break;
+ }
+
+ distributeSpace( trg );
}
/*!
- Removes widget from area
- \param wid - widget
- \param del - auto deleting
+ \brief Move widget(s) from the source workarea into the target workarea
+ or reorder widgets inside one workarea.
+
+ Move \a wid2 in target workarea. Put it right after \a wid1.
+ If \a all parameter is \c true, all widgets from source workarea
+ will be moved including \a wid2 and source workarea will be deleted then.
+
+ If \a wid1 and \a wid2 belongs to one workarea, widgets will be just reordered
+ in that workarea.
+
+ \param wid1 widget from target workarea
+ \param wid2 widget from source workarea
+ \param all if \c true, all widgets from source workarea will
+ be moved into the target one, else only the \a wid2 will be moved
*/
-void QtxWorkstackArea::removeWidget( QWidget* wid, const bool del )
+void QtxWorkstack::Attract( QWidget* wid1, QWidget* wid2, const bool all )
{
- if ( !myList.contains( wid ) )
+ if ( !wid1 || !wid2 )
return;
- if ( myBar->tab( widgetId( wid ) ) )
- myBar->removeTab( myBar->tab( widgetId( wid ) ) );
- myStack->removeWidget( child( wid ) );
+ // find area of the widgets
+ QtxWorkstackArea *area1 = 0, *area2 = 0;
+ QList<QtxWorkstackArea*> allAreas;
+ areas( mySplit, allAreas, true );
+ for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !( area1 && area2 ); ++it )
+ {
+ if ( (*it)->contains( wid1 ) )
+ area1 = *it;
- myList.remove( wid );
- myInfo.remove( wid );
- myChild.remove( wid );
+ if ( (*it)->contains( wid2 ) )
+ area2 = *it;
+ }
+
+ if ( !area1 || !area2 )
+ return;
- if( del )
+ QWidget* curWid = area1->activeWidget();
+ if ( !curWid )
+ return;
+
+ if ( area1 == area2 )
{
- delete child( wid );
- if( myList.isEmpty() )
- delete this;
+ if ( all )
+ {
+ // Set wid1 at first position, wid2 at second
+ area1->insertWidget( wid1 );
+ area1->insertWidget( wid2, 1 );
+ }
else
- updateState();
+ {
+ // Set wid2 right after wid1
+ area1->removeWidget( wid2 );
+ int wid1_ind = 0;
+ QWidgetList wids1 = area1->widgetList();
+ for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
+ area1->insertWidget( wid2, wid1_ind + 1 );
+ }
}
else
- updateState();
-}
-
-/*!
- \return list of visible widgets
-*/
-QWidgetList QtxWorkstackArea::widgetList() const
-{
- QWidgetList lst;
- for ( QWidgetListIt it( myList ); it.current(); ++it )
{
- if ( widgetVisibility( it.current() ) )
- lst.append( it.current() );
+ int wid1_ind = 0;
+ QWidgetList wids1 = area1->widgetList();
+ for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
+ if ( all )
+ {
+ // Set wid2 right after wid1, other widgets from area2 right after wid2
+ QWidgetList wids2 = area2->widgetList();
+ QWidgetList::iterator itr2 = wids2.begin();
+ for ( int ind = wid1_ind + 1; itr2 != wids2.end(); ++itr2, ++ind )
+ {
+ area2->removeWidget( *itr2 );
+ if ( *itr2 == wid2 )
+ area1->insertWidget( *itr2, wid1_ind + 1 );
+ else
+ area1->insertWidget( *itr2, ind );
+ }
+ }
+ else
+ {
+ // Set wid2 right after wid1
+ area2->removeWidget( wid2 );
+ area1->insertWidget( wid2, wid1_ind + 1 );
+ }
}
- return lst;
-}
-/*!
- \return active widget
-*/
-QWidget* QtxWorkstackArea::activeWidget() const
-{
- return widget( myBar->currentTab() );
+ area1->setActiveWidget( curWid );
}
/*!
- Sets widget as active
- \param wid - widget
+ \brief Calculate sizes of the splitter widget for the workarea.
+ \internal
*/
-void QtxWorkstackArea::setActiveWidget( QWidget* wid )
+static void setSizes (QIntList& szList, const int item_ind,
+ const int new_near, const int new_this, const int new_farr)
{
- myBar->setCurrentTab( widgetId( wid ) );
+ // set size to all items before an item # <item_ind>
+ int cur_pos = 0;
+ QIntList::iterator its = szList.begin();
+ for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
+ *its = new_near;
+ }
+ if (its == szList.end()) return;
+ // set size to item # <item_ind>
+ *its = new_this;
+ ++its;
+ // set size to all items after an item # <item_ind>
+ for (; its != szList.end(); ++its) {
+ *its = new_farr;
+ }
}
/*!
- \return true if area contains widget
- \param wid - widget
-*/
-bool QtxWorkstackArea::contains( QWidget* wid ) const
-{
- return myList.contains( wid );
-}
+ \brief Set position of the widget relatively to its parent splitter.
-/*!
- Shows area
+ Orientation of positioning will correspond to the splitter orientation.
+
+ \param wid widget
+ \param pos position relatively to the splitter; value in the range [0..1]
*/
-void QtxWorkstackArea::show()
+void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
{
- QMap<QWidget*, bool> map;
- for ( QWidgetListIt it( myList ); it.current(); ++it )
+ if ( position < 0.0 || 1.0 < position)
+ return;
+
+ if ( !wid )
+ return;
+
+ // find area of the given widget
+ QtxWorkstackArea* area = NULL;
+ QList<QtxWorkstackArea*> allAreas;
+ areas( mySplit, allAreas, true );
+ for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
+ {
+ if ( (*it)->contains( wid ) )
+ area = *it;
+ }
+
+ if ( !area )
+ return;
+
+ QSplitter* split = splitter( area );
+ if ( !split )
+ return;
+
+ // find index of the area in its splitter
+ int item_ind = -1;
+ bool isFound = false;
+ const QObjectList& was = split->children();
+ for ( QObjectList::const_iterator ito = was.begin(); ito != was.end() && !isFound; ++ito, ++item_ind )
{
- map.insert( it.current(), isBlocked( it.current() ) );
- setBlocked( it.current(), true );
+ if ( *ito == area )
+ isFound = true;
}
- QWidget::show();
+ if ( !isFound || item_ind == 0 )
+ return;
+
+ QIntList szList = split->sizes();
+ int splitter_size = ( split->orientation() == Qt::Horizontal ? split->width() : split->height());
+ int nb = szList.count();
- for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
- setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
+ int new_prev = int( splitter_size * position / item_ind );
+ int new_next = int( splitter_size * ( 1.0 - position ) / ( nb - item_ind ) );
+ setSizes( szList, item_ind, new_prev, new_next, new_next );
+ split->setSizes( szList );
}
/*!
- Hides area
+ \brief Set position of the widget relatively to the entire workstack.
+
+ If \a o is \c Qt::Horizontal, the horizontal position of \a wid will be changed.
+ If \a o is \c Qt::Vertical, the vertical position of \a wid will be changed.
+
+ \param wid widget
+ \param o orientation of positioning (\c Qt::Horizontal or \c Qt::Vertical)
+ \param pos position relatively to the workstack; value in range [0..1]
*/
-void QtxWorkstackArea::hide()
+void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
+ const double position )
{
- QMap<QWidget*, bool> map;
- for ( QWidgetListIt it( myList ); it.current(); ++it )
+ if ( position < 0.0 || 1.0 < position )
+ return;
+
+ if ( !wid )
+ return;
+
+ int splitter_size = o == Qt::Horizontal ? mySplit->width() : mySplit->height();
+ int need_pos = int( position * splitter_size );
+ int splitter_pos = 0;
+
+ if ( setPosition( wid, mySplit, o, need_pos, splitter_pos ) != 0 )
{
- map.insert( it.current(), isBlocked( it.current() ) );
- setBlocked( it.current(), true );
+ // impossible to set required position
}
+}
- QWidget::hide();
+/*!
+ \brief Set accelerator key-combination for the action with specified \a id.
+ \param id action ID
+ \param accel action accelerator
+*/
+void QtxWorkstack::setAccel( const int id, const int accel )
+{
+ if ( !myActionsMap.contains( id ) )
+ return;
- for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
- setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
+ myActionsMap[id]->setShortcut( accel );
}
/*!
- \return true if area is active
+ \brief Get the action's accelerator key-combination.
+ \param id action ID
+ \return action accelerator
*/
-bool QtxWorkstackArea::isActive() const
+int QtxWorkstack::accel( const int id ) const
{
- QtxWorkstack* ws = workstack();
- if ( !ws )
- return false;
+ int res = 0;
+ if ( myActionsMap.contains( id ) )
+ res = myActionsMap[id]->shortcut();
+ return res;
+}
- return ws->activeArea() == this;
+/*!
+ \brief Check if the action is enabled.
+ \param id action ID
+ \return \c true if action is enabled
+*/
+bool QtxWorkstack::isActionEnabled( const int id ) const
+{
+ bool res = false;
+ if ( myActionsMap.contains( id ) )
+ res = myActionsMap[id]->isEnabled();
+ return res;
}
/*!
- Update active state of tab bar
+ \brief Enable/disable action.
+ \param id action ID
+ \param on if \c true, enable the action, else disable it
*/
-void QtxWorkstackArea::updateActiveState()
+void QtxWorkstack::setActionEnabled( const int id, const bool on )
{
- myBar->setActive( isActive() );
+ if ( myActionsMap.contains( id ) )
+ myActionsMap[id]->setEnabled( on );
}
/*!
- \return corresponding workstack
+ \brief Calculate sizes of the splitter widget for the workarea.
+ \internal
*/
-QtxWorkstack* QtxWorkstackArea::workstack() const
+static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
+ const int item_ind, const int item_rel_pos,
+ const int need_pos, const int splitter_pos)
{
- QtxWorkstack* ws = 0;
- QWidget* wid = parentWidget();
- while ( wid && !ws )
- {
- if ( wid->inherits( "QtxWorkstack" ) )
- ws = (QtxWorkstack*)wid;
- wid = wid->parentWidget();
+ if (item_ind == 0) { // cannot move in this splitter
+ return (need_pos - splitter_pos);
}
- return ws;
+
+ int delta = 0;
+ int new_prev = 0;
+ int new_this = szList[item_ind];
+ int new_next = 0;
+
+ bool isToCheck = false;
+
+ if (need_pos < splitter_pos) {
+ // Set size of all previous workareas to zero <--
+ if (item_ind == nb - 1) {
+ // item iz last in the splitter, it will occupy all the splitter
+ new_this = splitter_size;
+ } else {
+ // recompute size of next items in splitter
+ new_next = (splitter_size - new_this) / (nb - item_ind - 1);
+ }
+ delta = need_pos - splitter_pos;
+
+ } else if (need_pos > (splitter_pos + splitter_size)) {
+ // Set size of all next workareas to zero -->
+ // recompute size of previous items in splitter
+ new_this = 0;
+ new_prev = (splitter_size - new_this) / item_ind;
+ delta = need_pos - (splitter_pos + splitter_size - new_this);
+
+ } else { // required position lays inside this splitter
+ // Move workarea inside splitter into required position <->
+ int new_item_rel_pos = need_pos - splitter_pos;
+ new_prev = new_item_rel_pos / item_ind;
+ if (need_pos < (splitter_pos + item_rel_pos)) {
+ // Make previous workareas smaller, next - bigger
+ // No problem to keep old size of the widget
+ } else {
+ // Make previous workareas bigger, next - smaller
+ if (new_this > splitter_size - new_item_rel_pos) {
+ new_this = splitter_size - new_item_rel_pos;
+ }
+ // jfa to do: in this case fixed size of next widgets could prevent right resizing
+ isToCheck = true;
+ }
+ if (item_ind == nb - 1) {
+ new_this = splitter_size - new_item_rel_pos;
+ } else {
+ new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
+ }
+ delta = 0;
+ }
+
+ setSizes (szList, item_ind, new_prev, new_this, new_next);
+ return delta;
}
/*!
- Custom event filter
+ \brief Set position of the widget.
+
+ Called from SetRelativePosition() public method.
+
+ \param wid widget to be moved
+ \param split currently processed splitter (goes from more common
+ to more particular splitter in recursion calls)
+ \param o orientation of positioning
+ \param need_pos required position of the given widget in pixels
+ (from top/left side of workstack area)
+ \param splitter_pos position of the splitter \a split
+ (from top/left side of workstack area)
+ \return difference between a required and a distinguished position
*/
-bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
+int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
+ const int need_pos, const int splitter_pos )
{
- if ( o->isWidgetType() )
+ if ( !wid || !split )
+ return need_pos - splitter_pos;
+
+ // Find corresponding sub-splitter.
+ // Find also index of appropriate item in current splitter.
+ int cur_ind = 0, item_ind = 0;
+ bool isBottom = false, isFound = false;
+ QSplitter* sub_split = NULL;
+ const QObjectList& objs = split->children();
+ for ( QObjectList::const_iterator it = objs.begin(); it != objs.end() && !isFound; ++it )
{
- QWidget* wid = (QWidget*)o;
- if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
+ QtxWorkstackArea* area = ::qobject_cast<QtxWorkstackArea*>( *it );
+ if ( area )
{
- bool ok = false;
- while ( !ok && wid && wid != myClose )
+ if ( area->contains( wid ) )
{
- ok = wid == this;
- wid = wid->parentWidget();
+ item_ind = cur_ind;
+ isBottom = true;
+ isFound = true;
}
- if ( ok )
- QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
+ cur_ind++;
+ }
+ else if ( (*it)->inherits( "QSplitter" ) )
+ {
+ QList<QtxWorkstackArea*> areaList;
+ areas( (QSplitter*)(*it), areaList, true );
+ for ( QList<QtxWorkstackArea*>::iterator ita = areaList.begin(); ita != areaList.end() && !isFound; ++ita )
+ {
+ if ( (*ita)->contains( wid ) )
+ {
+ item_ind = cur_ind;
+ isFound = true;
+ sub_split = (QSplitter*)*it;
+ }
+ }
+ cur_ind++;
}
}
- return false;
-}
-/*!
- \return rectangle of area in order to draw drop rectangle on area
-*/
-QRect QtxWorkstackArea::floatRect() const
-{
- QRect r = myStack->geometry();
- return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
-}
+ if ( !isFound )
+ return ( need_pos - splitter_pos );
+
+ if ( split->orientation() == o )
+ {
+ // Find coordinates of near and far sides of the appropriate item relatively current splitter
+ int splitter_size = ( o == Qt::Horizontal ? split->width() : split->height() );
+ QIntList szList = split->sizes();
+ int nb = szList.count();
+ int item_rel_pos = 0; // position of near side of item relatively this splitter
+ for (int i = 0; i < item_ind; i++) {
+ item_rel_pos += szList[i];
+ }
+ int item_size = szList[item_ind]; // size of item
+ int item_pos = splitter_pos + item_rel_pos;
+
+ // Resize splitter items to complete the conditions
+ if (isBottom) {
+ // I. Bottom of splitters stack reached
+
+ int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
+ split->setSizes(szList);
+ // Recompute delta, as some windows can reject given size
+ int new_item_rel_pos = 0;
+ QIntList szList1 = split->sizes();
+ for (int i = 0; i < item_ind; i++) {
+ new_item_rel_pos += szList1[i];
+ }
+ delta = need_pos - (splitter_pos + new_item_rel_pos);
+ return delta;
+
+ } else {
+ // II. Bottom of splitters stack is not yet reached
+
+ if (item_ind == 0) { // cannot move in this splitter
+ // Process in sub-splitter
+ return setPosition(wid, sub_split, o, need_pos, splitter_pos);
+ }
+
+ int new_prev = 0;
+ int new_this = szList[item_ind];
+ int new_next = 0;
+
+ if (need_pos < splitter_pos) {
+ // Set size of all previous workareas to zero <--
+ if (item_ind == nb - 1) {
+ new_this = splitter_size;
+ } else {
+ new_next = (splitter_size - new_this) / (nb - item_ind - 1);
+ }
+ setSizes (szList, item_ind, new_prev, new_this, new_next);
+ split->setSizes(szList);
+ // Recompute splitter_pos, as some windows can reject given size
+ int new_item_rel_pos = 0;
+ QIntList szList1 = split->sizes();
+ for (int i = 0; i < item_ind; i++) {
+ new_item_rel_pos += szList1[i];
+ }
+ // Process in sub-splitter
+ return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
+ } else if (need_pos > (splitter_pos + splitter_size)) {
+ // Set size of all next workareas to zero -->
+ new_prev = (splitter_size - new_this) / item_ind;
+ setSizes (szList, item_ind, new_prev, new_this, new_next);
+ split->setSizes(szList);
+ // Recompute splitter_pos, as some windows can reject given size
+ int new_item_rel_pos = 0;
+ QIntList szList1 = split->sizes();
+ for (int i = 0; i < item_ind; i++) {
+ new_item_rel_pos += szList1[i];
+ }
+ // Process in sub-splitter
+ return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
+ } else {
+ // Set appropriate size of all previous/next items <->
+ int new_item_rel_pos = item_rel_pos;
+ if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
+ // Move item inside splitter into required position <->
+ int new_this = szList[item_ind];
+ int new_next = 0;
+ new_item_rel_pos = need_pos - splitter_pos;
+ if ((item_pos + item_size) < need_pos) {
+ //new_item_rel_pos = need_pos - (item_pos + item_size);
+ new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
+ }
+ int new_prev = new_item_rel_pos / item_ind;
+ if (need_pos < (splitter_pos + item_rel_pos)) {
+ // Make previous workareas smaller, next - bigger
+ // No problem to keep old size of the widget
+ } else {
+ // Make previous workareas bigger, next - smaller
+ if (new_this > splitter_size - new_item_rel_pos) {
+ new_this = splitter_size - new_item_rel_pos;
+ }
+ }
+ if (item_ind == nb - 1) {
+ new_this = splitter_size - new_item_rel_pos;
+ } else {
+ new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
+ }
+ setSizes (szList, item_ind, new_prev, new_this, new_next);
+ split->setSizes(szList);
+ // Recompute new_item_rel_pos, as some windows can reject given size
+ new_item_rel_pos = 0;
+ QIntList szList1 = split->sizes();
+ for (int i = 0; i < item_ind; i++) {
+ new_item_rel_pos += szList1[i];
+ }
+ } else {
+ // Do nothing
+ }
+ // Process in sub-splitter
+ int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
+ if (add_pos == 0)
+ return 0;
-/*!
- \return rectangle of tab in order to draw drop rectangle on tab
- \param idx - tab index
-*/
-QRect QtxWorkstackArea::floatTab( const int idx ) const
-{
- return myBar->tabRect( idx );
-}
+ // this can be if corresponding workarea is first in sub-splitter
+ // or sub-splitter has another orientation
-/*!
- \return tab covering point
- \param p - point
-*/
-int QtxWorkstackArea::tabAt( const QPoint& p ) const
-{
- int idx = -1;
- for ( int i = 0; i < myBar->count() && idx == -1; i++ )
- {
- QRect r = myBar->tabRect( i );
- if ( r.isValid() && r.contains( p ) )
- idx = i;
- }
- return idx;
-}
+ // Resize ones again to reach precize position <->
+ int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
-/*!
- Event handler for custom events
-*/
-void QtxWorkstackArea::customEvent( QCustomEvent* e )
-{
- switch ( e->type() )
- {
- case ActivateWidget:
- emit activated( activeWidget() );
- break;
- case FocusWidget:
- if ( activeWidget() )
- {
- if ( !activeWidget()->focusWidget() )
- activeWidget()->setFocus();
- else {
- if ( activeWidget()->focusWidget()->hasFocus()) {
- QFocusEvent in(QEvent::FocusIn);
- QApplication::sendEvent(this, &in);
- }
- else
- activeWidget()->focusWidget()->setFocus();
+ // Move workarea inside splitter into required position <->
+ int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
+ new_item_rel_pos, need_pos_1, splitter_pos);
+ split->setSizes(szList);
+ // Recompute new_item_rel_pos, as some windows can reject given size
+ new_item_rel_pos = 0;
+ QIntList szList1 = split->sizes();
+ for (int i = 0; i < item_ind; i++) {
+ new_item_rel_pos += szList1[i];
+ }
+ delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
+ return delta_1;
}
}
- break;
- case RemoveWidget:
- removeWidget( (QWidget*)e->data() );
- break;
+ } else {
+ return setPosition(wid, sub_split, o, need_pos, splitter_pos);
}
-}
-
-/*!
- Custom focus in event handler
-*/
-void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
-{
- QWidget::focusInEvent( e );
- emit activated( activeWidget() );
+ return 0;
}
/*!
- Custom mouse press event handler
+ \brief Redistribute space among widgets equally.
+ \param split splitter
*/
-void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
+void QtxWorkstack::distributeSpace( QSplitter* split ) const
{
- QWidget::mousePressEvent( e );
+ if ( !split )
+ return;
- emit activated( activeWidget() );
+ QIntList szList = split->sizes();
+ int size = ( split->orientation() == Qt::Horizontal ?
+ split->width() : split->height() ) / szList.count();
+ for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
+ *it = size;
+ split->setSizes( szList );
}
/*!
- SLOT: called if button close is pressed
+ \brief Split widgets vertically.
*/
-void QtxWorkstackArea::onClose()
+void QtxWorkstack::splitVertical()
{
- QWidget* wid = activeWidget();
- if ( wid )
- wid->close();
+ split( Qt::Horizontal );
}
/*!
- SLOT: called if tab page is selected
+ \brief Split widgets horizontally.
*/
-void QtxWorkstackArea::onSelected( int id )
+void QtxWorkstack::splitHorizontal()
{
- updateCurrent();
-
- emit activated( activeWidget() );
+ split( Qt::Vertical );
}
/*!
- SLOT: called if active tab page is dragged
+ \brief Called when user activates "Rename" menu item.
+
+ Changes widget title.
*/
-void QtxWorkstackArea::onDragActiveTab()
+void QtxWorkstack::onRename()
{
- QtxWorkstackChild* c = child( activeWidget() );
- if ( !c )
+ if ( !myWorkWin )
return;
- new QtxWorkstackDrag( workstack(), c );
+ bool ok = false;
+ QString newName = QInputDialog::getText( topLevelWidget(), tr( "Rename" ), tr( "Enter new name:" ),
+ QLineEdit::Normal, myWorkWin->windowTitle(), &ok );
+ if ( ok && !newName.isEmpty() )
+ myWorkWin->setWindowTitle( newName );
}
/*!
- SLOT: called on child is destroyed, removes from area
+ \brief Wrap area into the new splitter.
+ \param workarea
+ \return new splitter
*/
-void QtxWorkstackArea::onChildDestroyed( QObject* obj )
+QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
{
- QtxWorkstackChild* child = (QtxWorkstackChild*)obj;
- myStack->removeWidget( child );
-
- QWidget* wid = 0;
- for ( ChildMap::ConstIterator it = myChild.begin(); it != myChild.end() && !wid; ++it )
- {
- if ( it.data() == child )
- wid = it.key();
- }
-
- myChild.remove( wid );
-
- QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)RemoveWidget, wid ) );
-}
+ if ( !area )
+ return 0;
-/*!
- SLOT: called on child is shown
-*/
-void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
-{
- setWidgetShown( c->widget(), true );
-}
+ QSplitter* pSplit = splitter( area );
+ if ( !pSplit )
+ return 0;
-/*!
- SLOT: called on child is hidden
-*/
-void QtxWorkstackArea::onChildHided( QtxWorkstackChild* c )
-{
- setWidgetShown( c->widget(), false );
-}
+ bool upd = pSplit->updatesEnabled();
+ pSplit->setUpdatesEnabled( false );
-/*!
- SLOT: called on child is activated
-*/
-void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
-{
- setWidgetActive( c->widget() );
-}
+ QIntList szList = pSplit->sizes();
-/*!
- SLOT: called on child caption is changed
-*/
-void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
-{
- updateTab( c->widget() );
-}
+ QSplitter* wrap = new QSplitter( 0 );
+ wrap->setChildrenCollapsible( false );
+ pSplit->insertWidget( pSplit->indexOf( area ) + 1, wrap );
+ wrap->setVisible( true );
+ wrap->addWidget( area );
-/*!
- Raises widget when active tab is changed
-*/
-void QtxWorkstackArea::updateCurrent()
-{
- QMap<QWidget*, bool> map;
- for ( QWidgetListIt it( myList ); it.current(); ++it )
- {
- map.insert( it.current(), isBlocked( it.current() ) );
- setBlocked( it.current(), true );
- }
+ pSplit->setSizes( szList );
- myStack->raiseWidget( myBar->currentTab() );
+ pSplit->setUpdatesEnabled( upd );
- for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
- setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
+ return wrap;
}
/*!
- Updates tab
- \param wid - tab widget
+ \brief Reparent and add widget.
+ \param wid widget
+ \param pWid parent widget
+ \param after widget after which \a wid should be added
*/
-void QtxWorkstackArea::updateTab( QWidget* wid )
+void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
{
- QTab* tab = myBar->tab( widgetId( wid ) );
- if ( !tab )
+ if ( !wid || !pWid )
return;
- QIconSet icoSet;
- if ( wid->icon() )
+ QWidgetList moveList;
+ const QObjectList& lst = pWid->children();
+ bool found = false;
+ for ( QObjectList::const_iterator it = lst.begin(); it != lst.end(); ++it )
{
- QPixmap pix = *wid->icon();
- pix.convertFromImage( pix.convertToImage().smoothScale( pix.width(), 16, QImage::ScaleMin ) );
- icoSet = QIconSet( pix );
+ if ( found && ( (*it)->inherits( "QSplitter" ) ||
+ (*it)->inherits( "QtxWorkstackArea" ) ) )
+ moveList.append( (QWidget*)(*it) );
+ if ( *it == after )
+ found = true;
}
- tab->setIconSet( icoSet );
- tab->setText( wid->caption() );
-}
-
-/*!
- \return widget
- \param id - widget id
-*/
-QWidget* QtxWorkstackArea::widget( const int id ) const
-{
- QWidget* wid = 0;
- for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !wid; ++it )
+ QMap<QWidget*, bool> map;
+ for ( QWidgetList::iterator it = moveList.begin(); it != moveList.end(); ++it )
{
- if ( it.data().id == id )
- wid = it.key();
+ map.insert( *it, (*it)->isVisibleTo( (*it)->parentWidget() ) );
+ (*it)->setParent( 0 );
+ (*it)->hide();
}
- return wid;
-}
-/*!
- \return widget id
- \param wid - widget
-*/
-int QtxWorkstackArea::widgetId( QWidget* wid ) const
-{
- int id = -1;
- if ( myInfo.contains( wid ) )
- id = myInfo[wid].id;
- return id;
-}
+ wid->setParent( pWid );
-/*!
- \return true if widget is visible
- \param wid - widget
-*/
-bool QtxWorkstackArea::widgetVisibility( QWidget* wid ) const
-{
- bool res = false;
- if ( myInfo.contains( wid ) )
- res = myInfo[wid].vis;
- return res;
+ for ( QWidgetList::iterator itr = moveList.begin(); itr != moveList.end(); ++itr )
+ {
+ (*itr)->setParent( pWid );
+ (*itr)->setShown( map.contains( *itr ) ? map[*itr] : false );
+ }
}
/*!
- Sets widget as active
- \param wid - widget
+ \brief Close active window.
*/
-void QtxWorkstackArea::setWidgetActive( QWidget* wid )
+void QtxWorkstack::onCloseWindow()
{
- int id = widgetId( wid );
- if ( id < 0 )
- return;
-
- myBar->setCurrentTab( id );
+ if ( myWorkWin )
+ myWorkWin->close();
+ else if( activeWindow() )
+ activeWindow()->close();
}
/*!
- Shows/hides widget
- \param wid - widget
- \param on - new shown state
-*/
-void QtxWorkstackArea::setWidgetShown( QWidget* wid, const bool on )
-{
- if ( isBlocked( wid ) || !myInfo.contains( wid ) || myInfo[wid].vis == on )
- return;
+ \brief Called when workarea is destroyed.
- myInfo[wid].vis = on;
- updateState();
-}
+ Set input focus to the neighbour area.
-/*!
- Update
+ \param obj workarea being destroyed
*/
-void QtxWorkstackArea::updateState()
+void QtxWorkstack::onDestroyed( QObject* obj )
{
- bool updBar = myBar->isUpdatesEnabled();
- bool updStk = myStack->isUpdatesEnabled();
- myBar->setUpdatesEnabled( false );
- myStack->setUpdatesEnabled( false );
-
- bool block = myBar->signalsBlocked();
- myBar->blockSignals( true );
-
- QWidget* prev = activeWidget();
-
- int idx = 0;
- for ( QWidgetListIt it( myList ); it.current(); ++it )
- {
- QWidget* wid = it.current();
- int id = widgetId( wid );
-
- if ( id < 0 )
- continue;
-
- bool vis = widgetVisibility( wid );
+ QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
- if ( myBar->tab( id ) && ( !vis || myBar->indexOf( id ) != idx ) )
- myBar->removeTab( myBar->tab( id ) );
+ if ( area == myArea )
+ myArea = 0;
- if ( !myBar->tab( id ) && vis )
- {
- QTab* tab = new QTab( wid->caption() );
- myBar->insertTab( tab, idx );
- tab->setIdentifier( id );
- }
+ if ( !myArea )
+ {
+ QtxWorkstackArea* cur = neighbourArea( area );
+ if ( cur )
+ cur->setFocus();
+ }
- updateTab( wid );
+ QApplication::postEvent( this, new QEvent( QEvent::User ) );
+}
- bool block = isBlocked( wid );
- setBlocked( wid, true );
+/*!
+ \brief Called on window activating.
+ \param area workarea being activated (not used)
+*/
+void QtxWorkstack::onWindowActivated( QWidget* /*area*/ )
+{
+ const QObject* obj = sender();
+ if ( !obj->inherits( "QtxWorkstackArea" ) )
+ return;
- QtxWorkstackChild* cont = child( wid );
+ setActiveArea( (QtxWorkstackArea*)obj );
+}
- if ( !vis )
- myStack->removeWidget( cont );
- else if ( !myStack->widget( id ) )
- myStack->addWidget( cont, id );
+/*!
+ \brief Called on window deactivating.
+ \param area workarea being deactivated
+*/
+void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
+{
+ if ( myArea != area )
+ return;
- if ( vis )
- idx++;
+ QList<QtxWorkstackArea*> lst;
+ areas( mySplit, lst, true );
- setBlocked( wid, block );
- }
+ int idx = lst.indexOf( area );
+ if ( idx == -1 )
+ return;
- int curId = widgetId( prev );
- if ( !myBar->tab( curId ) )
- {
- QWidget* wid = 0;
- int pos = myList.find( prev );
- for ( int i = pos - 1; i >= 0 && !wid; i-- )
- {
- if ( widgetVisibility( myList.at( i ) ) )
- wid = myList.at( i );
- }
+ myWin = 0;
+ myArea = 0;
- for ( int j = pos + 1; j < (int)myList.count() && !wid; j++ )
- {
- if ( widgetVisibility( myList.at( j ) ) )
- wid = myList.at( j );
- }
+ QtxWorkstackArea* newArea = neighbourArea( area );
+ if ( newArea && newArea->activeWidget() )
+ newArea->activeWidget()->setFocus();
- if ( wid )
- curId = widgetId( wid );
- }
+ QApplication::postEvent( this, new QEvent( QEvent::User ) );
+}
- myBar->setCurrentTab( curId );
+/*!
+ \brief Create and show popup menu for workarea.
+ \param w workarea
+ \param p popup position
+*/
+void QtxWorkstack::onContextMenuRequested( QWidget* w, QPoint p )
+{
+ QtxWorkstackArea* anArea = ::qobject_cast<QtxWorkstackArea*>( (QObject*)sender() );
+ if ( !anArea )
+ anArea = activeArea();
- myBar->blockSignals( block );
+ if ( !anArea )
+ return;
- updateCurrent();
+ QWidgetList lst = anArea->widgetList();
+ if ( lst.isEmpty() )
+ return;
- myBar->setUpdatesEnabled( updBar );
- myStack->setUpdatesEnabled( updStk );
- if ( updBar )
- myBar->update();
- if ( updStk )
- myStack->update();
+ myWorkWin = w;
+ myWorkArea = anArea;
- QResizeEvent re( myBar->size(), myBar->size() );
- QApplication::sendEvent( myBar, &re );
+ QMenu* pm = new QMenu();
- if ( isEmpty() )
+ if ( lst.count() > 1 )
{
- hide();
- emit deactivated( this );
+ if ( myActionsMap[SplitVertical]->isEnabled() )
+ pm->addAction( myActionsMap[SplitVertical] );
+ if ( myActionsMap[SplitHorizontal]->isEnabled() )
+ pm->addAction( myActionsMap[SplitHorizontal] );
+ pm->addSeparator();
}
- else
+
+ if ( w )
{
- show();
- if ( prev != activeWidget() )
- emit activated( activeWidget() );
+ if ( myActionsMap[Close]->isEnabled() )
+ pm->addAction( myActionsMap[Close] );
+ if ( myActionsMap[Rename]->isEnabled() )
+ pm->addAction( myActionsMap[Rename] );
}
-}
-/*!
- \return first unshared widget id
-*/
-int QtxWorkstackArea::generateId() const
-{
- QMap<int, int> map;
+ Qtx::simplifySeparators( pm );
- for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end(); ++it )
- map.insert( it.data().id, 0 );
+ if ( !pm->actions().isEmpty() )
+ pm->exec( p );
- int id = 0;
- while ( map.contains( id ) )
- id++;
+ delete pm;
- return id;
+ myWorkWin = 0;
+ myWorkArea = 0;
}
/*!
- \return true if widget is blocked
- \param wid - widget
+ \brief Add child widget.
+ \param w widget
+ \param f widget flags
+ \return child widget container
*/
-bool QtxWorkstackArea::isBlocked( QWidget* wid ) const
+QWidget* QtxWorkstack::addWindow( QWidget* w, Qt::WindowFlags f )
{
- return myBlock.contains( wid );
+ if ( !w )
+ return 0;
+
+ return targetArea()->insertWidget( w, -1, f );
}
/*!
- Blocks widget
- \param wid - widget
- \param on - new blocked state
+ \brief Handle custom events.
+ \param e custom event (not used)
*/
-void QtxWorkstackArea::setBlocked( QWidget* wid, const bool on )
+void QtxWorkstack::customEvent( QEvent* /*e*/ )
{
- if ( on )
- myBlock.insert( wid, 0 );
- else
- myBlock.remove( wid );
+ updateState();
}
/*!
- \return child corresponding to widget
- \param wid - widget
+ \brief Get splitter corresponding to the workarea.
+ \param workarea
+ \return splitter corresponding to the workarea
*/
-QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
+QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
{
- QtxWorkstackChild* res = 0;
- if ( myChild.contains( wid ) )
- res = myChild[wid];
- return res;
+ if ( !area )
+ return 0;
+
+ QSplitter* split = 0;
+
+ QWidget* wid = area->parentWidget();
+ if ( wid && wid->inherits( "QSplitter" ) )
+ split = (QSplitter*)wid;
+
+ return split;
}
/*!
- Constructor
+ \brief Get list of child splitters.
+ \param split parent splitter
+ \param splitList list to be filled with child splitters
+ \param rec if \c true, perform recursive search of children
*/
-QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent )
-: QHBox( parent ),
-myWidget( wid )
+void QtxWorkstack::splitters( QSplitter* split, QList<QSplitter*>& splitList, const bool rec ) const
{
- myWidget->reparent( this, QPoint( 0, 0 ), myWidget->isVisibleTo( myWidget->parentWidget() ) );
- myWidget->installEventFilter( this );
+ if ( !split )
+ return;
- connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
+ const QObjectList& objs = split->children();
+ for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
+ {
+ if ( rec )
+ splitters( (QSplitter*)*it, splitList, rec );
+ if ( (*it)->inherits( "QSplitter" ) )
+ splitList.append( (QSplitter*)*it );
+ }
}
/*!
- Destructor
+ \brief Get list of child workareas.
+ \param split parent splitter
+ \param areaList list to be filled with child workareas
+ \param rec if \c true, perform recursive search of children
*/
-QtxWorkstackChild::~QtxWorkstackChild()
+void QtxWorkstack::areas( QSplitter* split, QList<QtxWorkstackArea*>& areaList, const bool rec ) const
{
- qApp->removeEventFilter( this );
-
- if ( !widget() )
+ if ( !split )
return;
- widget()->removeEventFilter( this );
- widget()->reparent( 0, QPoint( 0, 0 ), false );
- disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
+ const QObjectList& objs = split->children();
+ for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
+ {
+ if ( (*it)->inherits( "QtxWorkstackArea" ) )
+ areaList.append( (QtxWorkstackArea*)*it );
+ else if ( rec && (*it)->inherits( "QSplitter" ) )
+ areas( (QSplitter*)*it, areaList, rec );
+ }
}
/*!
- \return corresponding widget
+ \brief Get active workarea.
+ \return active workarea
*/
-QWidget* QtxWorkstackChild::widget() const
+QtxWorkstackArea* QtxWorkstack::activeArea() const
{
- return myWidget;
+ return myArea;
}
/*!
- Custom event filter
+ \brief Get target area (for which the current operation should be done).
+
+ Returns active workarea or current area (if there is no active workarea).
+ If there are no workareas, create new workarea and return it.
+
+ \return workarea
*/
-bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
+QtxWorkstackArea* QtxWorkstack::targetArea()
{
- if ( o->isWidgetType() )
+ QtxWorkstackArea* area = activeArea();
+ if ( !area )
+ area = currentArea();
+ if ( !area )
{
- if ( e->type() == QEvent::CaptionChange || e->type() == QEvent::IconChange )
- emit captionChanged( this );
-
- if ( !e->spontaneous() && ( e->type() == QEvent::Show || e->type() == QEvent::ShowToParent ) )
- emit shown( this );
+ QList<QtxWorkstackArea*> lst;
+ areas( mySplit, lst );
+ if ( !lst.isEmpty() )
+ area = lst.first();
+ }
- if ( !e->spontaneous() && ( e->type() == QEvent::Hide || e->type() == QEvent::HideToParent ) )
- emit hided( this );
+ if ( !area )
+ area = createArea( mySplit );
- if ( e->type() == QEvent::FocusIn )
- emit activated( this );
- }
- return QHBox::eventFilter( o, e );
+ return area;
}
/*!
- SLOT: called on object is destroyed
-*/
-void QtxWorkstackChild::onDestroyed( QObject* obj )
-{
- if ( obj != widget() )
- return;
+ \brief Get current workarea.
- myWidget = 0;
- deleteLater();
-}
+ Current workarea is that one which has input focus.
-/*!
- Custom child event handler
+ \return current area
*/
-void QtxWorkstackChild::childEvent( QChildEvent* e )
+QtxWorkstackArea* QtxWorkstack::currentArea() const
{
- if ( e->type() == QEvent::ChildRemoved && e->child() == widget() )
+ QtxWorkstackArea* area = 0;
+ QWidget* wid = focusWidget();
+ while ( wid && !area )
{
- myWidget = 0;
- deleteLater();
+ if ( wid->inherits( "QtxWorkstackArea" ) )
+ area = (QtxWorkstackArea*)wid;
+ wid = wid->parentWidget();
}
- QHBox::childEvent( e );
+
+ return area;
}
/*!
- Constructor
+ \brief Create new workarea.
+ \param parent parent widget
+ \return created workarea
*/
-QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
-: QTabBar( parent ),
-myId( -1 )
+QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
{
+ QtxWorkstackArea* area = new QtxWorkstackArea( parent );
+
+ connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
+ connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
+ connect( area, SIGNAL( contextMenuRequested( QWidget*, QPoint ) ),
+ this, SLOT( onContextMenuRequested( QWidget*, QPoint ) ) );
+ connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
+
+ return area;
}
/*!
- Destructor
+ \brief Set active workarea.
+ \param workarea
*/
-QtxWorkstackTabBar::~QtxWorkstackTabBar()
+void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
{
+ QWidget* oldCur = myWin;
+
+ QtxWorkstackArea* oldArea = myArea;
+
+ myArea = area;
+
+ if ( myArea != oldArea )
+ {
+ if ( oldArea )
+ oldArea->updateActiveState();
+ if ( myArea )
+ myArea->updateActiveState();
+ }
+
+ if ( myArea )
+ myWin = myArea->activeWidget();
+
+ if ( myWin && oldCur != myWin )
+ emit windowActivated( myWin );
}
/*!
- Sets tab bar as active or inactive
- \param on - new active state
+ \brief Get workarea which is nearest to \a area.
+ \param area area for which neighbour is searched
+ \return neighbour area (or 0 if not found)
*/
-void QtxWorkstackTabBar::setActive( const bool on )
+QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
{
- QFont aFont = font();
- aFont.setUnderline( on );
- QColorGroup aColGrp;
- QPalette aPal = palette();
- if ( !on ) {
- aPal.setColor( QColorGroup::HighlightedText, aColGrp.foreground() );
- aPal.setColor( QColorGroup::Highlight, colorGroup().dark().light( DARK_COLOR_LIGHT ) );
- setPalette( aPal );
+ QList<QtxWorkstackArea*> lst;
+ areas( mySplit, lst, true );
+ int pos = lst.indexOf( area );
+ if ( pos < 0 )
+ return 0;
+
+ QtxWorkstackArea* na = 0;
+ for ( int i = pos - 1; i >= 0 && !na; i-- )
+ {
+ if ( !lst.at( i )->isEmpty() )
+ na = lst.at( i );
}
- else {
- aPal.setColor( QColorGroup::HighlightedText, aColGrp.highlightedText() );
- aPal.setColor( QColorGroup::Highlight, aColGrp.highlight() );
- unsetPalette();
+
+ for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
+ {
+ if ( !lst.at( j )->isEmpty() )
+ na = lst.at( j );
}
- setFont( aFont );
+ return na;
+}
- update();
+/*!
+ \brief Get workarea covering point.
+ \return workarea
+ \param p point
+*/
+QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
+{
+ QtxWorkstackArea* area = 0;
+ QList<QtxWorkstackArea*> lst;
+ areas( mySplit, lst, true );
+ for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end() && !area; ++it )
+ {
+ QtxWorkstackArea* cur = *it;
+ QRect r = cur->geometry();
+ if ( cur->parentWidget() )
+ r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
+ if ( r.contains( p ) )
+ area = cur;
+ }
+ return area;
}
/*!
- \return tab rectangle
- \param idx - tab index
+ \brief Update internal state.
*/
-QRect QtxWorkstackTabBar::tabRect( const int idx ) const
+void QtxWorkstack::updateState()
{
- QRect r;
- QTab* t = tabAt( idx );
- if ( t )
- {
- r = t->rect();
- r.setLeft( QMAX( r.left(), 0 ) );
-
- int x1 = tabAt( 0 )->rect().left();
- int x2 = tabAt( count() - 1 )->rect().right();
-
- int bw = 0;
- if ( QABS( x2 - x1 ) > width() )
-#if defined QT_VERSION && QT_VERSION >= 0x30300
- bw = 2 * style().pixelMetric( QStyle::PM_TabBarScrollButtonWidth, this );
-#else
- bw = 2 * 16;
-#endif
-
- int limit = width() - bw;
- r.setRight( QMIN( r.right(), limit ) );
-
- r = QRect( mapToGlobal( r.topLeft() ), r.size() );
- }
- return r;
+ updateState( mySplit );
}
/*!
- Custom mouse move event handler
+ \brief Update splitter state.
+ \param split splitter to be updated
*/
-void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
+void QtxWorkstack::updateState( QSplitter* split )
{
- if ( myId != -1 && !tab( myId )->rect().contains( e->pos() ) )
+ QList<QSplitter*> recList;
+ splitters( split, recList, false );
+ for ( QList<QSplitter*>::iterator itr = recList.begin(); itr != recList.end(); ++itr )
+ updateState( *itr );
+
+ QList<QSplitter*> splitList;
+ splitters( split, splitList, false );
+
+ QList<QtxWorkstackArea*> areaList;
+ areas( split, areaList, false );
+
+ bool vis = false;
+ for ( QList<QtxWorkstackArea*>::iterator it = areaList.begin(); it != areaList.end(); ++it )
{
- myId = -1;
- emit dragActiveTab();
+ if ( (*it)->isEmpty() )
+ (*it)->hide();
+ else
+ {
+ (*it)->show();
+ vis = true;
+ }
}
- QTabBar::mouseMoveEvent( e );
-}
+ if ( split == mySplit )
+ return;
-/*!
- Custom mouse press event handler
-*/
-void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
-{
- QTabBar::mousePressEvent( e );
+ for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end() && !vis; ++iter )
+ vis = (*iter)->isVisibleTo( (*iter)->parentWidget() );
- if ( e->button() == LeftButton )
- myId = currentTab();
+ if ( areaList.isEmpty() && splitList.isEmpty() )
+ delete split;
+ else if ( vis )
+ split->show();
+ else
+ split->hide();
}
/*!
- Custom mouse release event handler
+ \brief Get splitter info (for debug purposes)
+ \param split splitter
+ \param info string to be filled with splitter data.
*/
-void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
+void QtxWorkstack::splitterInfo( QSplitter* split, QString& info ) const
{
- QTabBar::mouseReleaseEvent( e );
+ if ( !split )
+ return;
- myId = -1;
+ const QObjectList& objs = split->children();
- if ( e->button() == RightButton )
- emit contextMenuRequested( e->globalPos() );
+ QString sizesStr;
+ QList<int> sizes = split->sizes();
+ for ( QList<int>::iterator sIt = sizes.begin(); sIt != sizes.end(); ++sIt )
+ {
+ if ( *sIt > 1 ) // size 1 pixel usually means empty Workstack area, which will NOT be re-created,
+ sizesStr += QString( ":%1" ).arg( *sIt ); // so we don't need to store its size
+ }
+
+ if ( !sizesStr.isEmpty() ) // cut the first ':'
+ sizesStr = sizesStr.right( sizesStr.length() - 1 );
+
+ info += QString( "(splitter orientation=%1 sizes=%3 " ).arg( split->orientation() ).arg( sizesStr );
+
+ for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
+ {
+ if ( (*it)->inherits( "QSplitter" ) )
+ splitterInfo( (QSplitter*)*it, info );
+ else if ( (*it)->inherits( "QtxWorkstackArea" ) )
+ {
+ QtxWorkstackArea* area = (QtxWorkstackArea*)*it;
+ if ( area->isEmpty() )
+ continue;
+ info += QString( "(views active='%1'" ).arg( area->activeWidget()->objectName() );
+ QWidgetList views = area->widgetList();
+ for ( QWidgetList::iterator wIt = views.begin(); wIt != views.end(); ++wIt )
+ info += QString( " '%1'" ).arg( (*wIt)->objectName() );
+ info += ')';
+ }
+ }
+
+ info += ')';
+ printf( (const char*)QString( info + '\n' ).toLatin1() );
}
/*!
- Custom context menu event handler
+ \brief Remove round brackets symbols from the string.
+ \internal
+ \param parameters string to be processed
*/
-void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
+static void cutBrackets( QString& parameters )
{
- if ( e->reason() != QContextMenuEvent::Mouse )
- emit contextMenuRequested( e->globalPos() );
+ QChar c1 = parameters[0];
+ QChar c2 = parameters[int(parameters.length()-1)];
+ if ( !parameters.isEmpty() && c1 == '(' && c2 == ')' )
+ parameters = parameters.mid( 1, parameters.length()-2 );
}
/*!
- Draws label of tab bar
+ \brief Parse string to get some parameter value.
+ \internal
+
+ String \a str can contain the parameters description of kind "<param>=<value> ...".
+ For example:
+ \code
+ QString s = "splitter orientation=0 children=2 sizes=332:478";
+ QString orient_val = getValue( s, "children" ); // orient_val contains "2"
+ QString size_val = getValue( s, "sizes" ); // val contains "332:478"
+ \endcode
+
+ \param str string to be processed
+ \param valName parameter name
+ \return parameter value (or null QStrinhg if parameter is not found)
*/
-void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
+static QString getValue( const QString& str, const QString& valName )
{
- if ( currentTab() != t->identifier() )
+ int i = str.indexOf( valName );
+ if ( i != -1 )
{
- QFont fnt = p->font();
- fnt.setUnderline( false );
- p->setFont( fnt );
+ int equal_i = str.indexOf( '=', i );
+ if ( equal_i != -1 )
+ {
+ int space_i = str.indexOf( ' ', ++equal_i );
+ if ( space_i != -1 )
+ return str.mid( equal_i, space_i - equal_i );
+ }
}
- QTabBar::paintLabel( p, br, t, has_focus );
+ return QString();
}
/*!
- Constructor
+ \brief Check format of splitter parameters string.
+ \internal
+ \param parameters splitter parameters description
+ \return \c true on success and \c false on error
*/
-QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
-: QObject( 0 ),
-myWS( ws ),
-myTab( -1 ),
-myArea( 0 ),
-myPainter( 0 ),
-myChild( child )
+static bool checkFormat( const QString& parameters )
{
- qApp->installEventFilter( this );
+ QString params( parameters );
+ // 1. begins and ends with brackets
+ QChar c1 = params[0];
+ QChar c2 = params[int(params.length()-1)];
+ bool ok = ( c1 == '(' && c2 == ')' );
+ if ( !ok ) return ok;
+ ::cutBrackets( params );
+ // 2. has splitter word
+ ok = ( params.left( 8 ) == "splitter" );
+ if ( !ok ) return ok;
+ // 3. has children? = '(' is found
+ int i = params.indexOf( '(' );
+ ok = i != -1;
+ if ( !ok ) return ok;
+ params = params.left( i ); // cut all children, they will be checked later
+ // 4. has orientation word and correct value
+ ::getValue( params, "orientation" ).toInt( &ok );
+ if ( !ok ) return ok;
+ // 5. has sizes word and values
+ ok = ! ::getValue( params, "sizes" ).isEmpty();
+ if ( !ok ) return ok;
+ // 6. check children -> number of '(' == number of ')' in original string
+ ok = ( parameters.contains( '(' ) == parameters.contains( ')' ) );
+ return ok;
}
/*!
- Destructor
+ \brief Get splitter's children descriptions from the string.
+ \internal
+
+ Child widgets descriptions are separated by '(' and ')' symbols.
+
+ \param str string to be processed
+ \return child widgets descriptions
*/
-QtxWorkstackDrag::~QtxWorkstackDrag()
+static QStringList getChildren( const QString& str )
{
- qApp->removeEventFilter( this );
+ QStringList lst;
+ if ( !str.startsWith( "(" ) )
+ return lst;
- endDrawRect();
+ int i = 1,
+ nOpen = 1, // count brackets: '(' increments nOpen, ')' decrements
+ start = 0;
+ while ( i < (int)str.length() )
+ {
+ if ( str[i] == '(' )
+ {
+ nOpen++;
+ if ( nOpen == 1 )
+ start = i;
+ }
+ else if ( str[i] == ')' )
+ {
+ nOpen--;
+ if ( nOpen == 0 )
+ lst.append( str.mid( start, i-start+1 ) );
+ }
+ i++;
+ }
+
+ return lst;
}
/*!
- Custom event filter
+ \brief Get view name by index.
+ \internal
+
+ Example:
+ \code
+ QString s = "views active='AnotherView' 'GLView' 'AnotherView'";
+ QString a0 = getViewName( s, 0 ); // --> a0 contains "GLView"
+ QString a1 = getViewName( s, 1 ); // --> a1 contains "AnotherView"
+ \endcode
+
+ \param str string to be processed
+ \param i index
+ \return view name
*/
-bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
+static QString getViewName( const QString& str, int i )
{
- switch ( e->type() )
+ QRegExp exp( "\\s'\\w+'" );
+ int start = 0; // start index of view name in the string
+ int num = 0 ; // index of found match
+ while ( ( start = exp.indexIn( str, start ) ) != -1 && num < i )
{
- case QEvent::MouseMove:
- updateTarget( ((QMouseEvent*)e)->globalPos() );
- break;
- case QEvent::MouseButtonRelease:
- drawRect();
- endDrawRect();
- dropWidget();
- deleteLater();
- break;
- default:
- return false;
+ start += exp.matchedLength();
+ num ++;
}
- return true;
-}
+ if ( start != -1 ) // +2 and -3 avoid starting space and starting and ending ' symbols
+ return str.mid( start + 2, exp.matchedLength() - 3 );
-/*!
- Updates internal field with widget-target for dropping
- \param p - current point of dragging
-*/
-void QtxWorkstackDrag::updateTarget( const QPoint& p )
-{
- int tab = -1;
- QtxWorkstackArea* area = detectTarget( p, tab );
- setTarget( area, tab );
+ return QString();
}
/*!
- \return target area for dropping by point
- \param p - current point of dragging
- \param tab - index of tab to dropping
+ \brief Get child widget with specified name.
+ \internal
+ \param parent parent widget
+ \param aName child widget name
+ \return child widget or 0 if not found
*/
-QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
+static QWidget* getView( const QWidget* parent, const QString& aName )
{
- if ( p.isNull() )
- return 0;
-
- QtxWorkstackArea* area = myWS->areaAt( p );
- if ( area )
- tab = area->tabAt( p );
- return area;
+ QWidget* view = 0;
+ QList<QWidget*> l = qFindChildren<QWidget*>( parent->topLevelWidget(), aName );
+ if ( !l.isEmpty() )
+ view = ::qobject_cast<QWidget*>( l.first() );
+ return view;
}
/*!
- Changes target area for dropping
- \param area - new target area
- \param tab - tab index
+ \brief Setup splitter according to the specified parameters string.
+ \param splitter splitter to be set up
+ \param parameters splitter parameters description
+ \param sMap map containing resulting child splitters sizes
*/
-void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
+void QtxWorkstack::setSplitter( QSplitter* splitter, const QString& parameters, QMap<QSplitter*, QList<int> >& sMap )
{
- if ( !area || ( myArea == area && tab == myTab ) )
+ printf( QString( parameters + '\n' ).toLatin1() );
+ if ( !::checkFormat( parameters ) ) {
+ printf( "\nInvalid format of workstack parameters. Positions of viewers can not be restored.\n" );
return;
+ }
- startDrawRect();
+ QString params( parameters );
+ ::cutBrackets( params );
- if ( myArea )
- drawRect();
+ // get splitter sizes and store it in the map for future setting
+ QList<int> sizes;
+ QStringList sizesLst = ::getValue( params, "sizes" ).split( ':', QString::SkipEmptyParts );
+ QStringList::Iterator it;
+ for ( it = sizesLst.begin(); it != sizesLst.end(); ++it )
+ sizes.append( (*it).toInt() );
+ sMap[ splitter ] = sizes;
- myTab = tab;
- myArea = area;
+ // set orientation of splitter
+ int orient = ::getValue( params, "orientation" ).toInt();
+ splitter->setOrientation( (Qt::Orientation)orient );
- if ( myArea )
- drawRect();
-}
+ // get children
+ QString options = params.left( params.indexOf( '(' ) );
+ QString childrenStr = params.right( params.length()-options.length() );
+ QStringList children = ::getChildren( childrenStr );
-/*!
- Called on widget drop, inserts dropped widget to area
-*/
-void QtxWorkstackDrag::dropWidget()
-{
- if ( myArea )
- myArea->insertWidget( myChild->widget(), myTab );
+ // debug output..
+ // printf (" splitter orient=%d, sizes_count=%d, children=%d\n", orient, sizes.count(), children.count() );
+ // for ( QStringList::Iterator tit = children.begin(); tit != children.end(); ++tit )
+ // printf (" |-> child = [%s]\n", (*tit).latin1() );
+
+ for ( it = children.begin(); it != children.end(); ++it )
+ {
+ if ( (*it).startsWith( "(splitter" ) )
+ {
+ QSplitter* newSplitter = new QSplitter( splitter );
+ setSplitter( newSplitter, *it, sMap );
+ }
+ else if ( (*it).startsWith( "(views" ) )
+ {
+ QtxWorkstackArea* newArea = createArea( splitter );
+ QString activeViewName = ::getValue( *it, "active" );
+ QWidget* activeView = 0;
+ activeViewName = activeViewName.mid( 1, activeViewName.length()-2 ); // chop off ' symbols
+ int i = 0;
+ QString viewName = ::getViewName( *it, i );
+ while ( !viewName.isEmpty() )
+ {
+ if ( QWidget* view = ::getView( splitter, viewName ) )
+ {
+ newArea->insertWidget( view );
+ if ( activeViewName == view->objectName() )
+ activeView = view;
+ }
+ viewName = ::getViewName( *it, ++i );
+ }
+ if ( activeView )
+ newArea->setActiveWidget( activeView );
+ }
+ }
}
/*!
- Draws float rect
+ \brief Restore workstack configuration from the state description string.
+ \param parameters workstack state description
+ \return reference to this workstack
*/
-void QtxWorkstackDrag::drawRect()
+QtxWorkstack& QtxWorkstack::operator<<( const QString& parameters )
{
- if ( !myPainter || !myArea )
- return;
+ // clear the main splitter - remove all child splitters and empty areas from it
+ QList<QSplitter*> splitList;
+ QList<QtxWorkstackArea*> areaList;
+ splitters( mySplit, splitList, false );
+ areas( mySplit, areaList, false );
+ for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end(); ++iter )
+ delete *iter;
- QRect r = myArea->floatRect();
- int m = myPainter->pen().width();
+ for ( QList<QtxWorkstackArea*>::iterator it = areaList.begin(); it != areaList.end(); ++it )
+ {
+ if ( (*it)->isEmpty() )
+ delete *it;
+ }
- r.setTop( r.top() + m + 2 );
- r.setLeft( r.left() + m + 2 );
- r.setRight( r.right() - m - 2 );
- r.setBottom( r.bottom() - m - 2 );
+ // restore splitter recursively
+ QMap< QSplitter*, QList<int> > sMap;
+ setSplitter( mySplit, parameters, sMap );
- myPainter->drawRect( r );
+ // now mySplit may contains empty area (where all views were located before restoring)
+ // in order setSize to work correctly we have to exclude this area
+ areaList.clear();
+ areas( mySplit, areaList, false );
+ for ( QList<QtxWorkstackArea*>::iterator delIt = areaList.begin(); delIt != areaList.end(); ++delIt )
+ {
+ if ( (*delIt)->isEmpty() )
+ delete *delIt;
+ }
- QRect tr = myArea->floatTab( myTab );
- tr.setTop( tr.top() + m );
- tr.setLeft( tr.left() + m );
- tr.setRight( tr.right() - m );
- tr.setBottom( tr.bottom() - m );
+ QApplication::instance()->processEvents();
+
+ // restore splitters' sizes (map of sizes is filled in setSplitters)
+ for ( QMap< QSplitter*, QList<int> >::iterator itm = sMap.begin(); itm != sMap.end(); ++itm )
+ itm.key()->setSizes( itm.value() );
- myPainter->drawRect( tr );
+ return (*this);
}
/*!
- Deletes internal painter
+ \brief Dump workstack configuration to the state description string.
+ \param parameters resulting workstack state description
+ \return reference to this workstack
*/
-void QtxWorkstackDrag::endDrawRect()
+QtxWorkstack& QtxWorkstack::operator>>( QString& outParameters )
{
- delete myPainter;
- myPainter = 0;
+ splitterInfo( mySplit, outParameters );
+ return (*this);
}
/*!
- Initialize internal painter
+ \fn void QtxWorkstack::windowActivated( QWidget* w )
+ \brief Emitted when the workstack's child widget \w is activated.
+ \param w widget being activated
*/
-void QtxWorkstackDrag::startDrawRect()
-{
- if ( myPainter )
- return;
-
- int scr = QApplication::desktop()->screenNumber( (QWidget*)this );
- QWidget* paint_on = QApplication::desktop()->screen( scr );
-
- myPainter = new QPainter( paint_on, true );
- myPainter->setPen( QPen( gray, 3 ) );
- myPainter->setRasterOp( XorROP );
-}