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