summaryrefslogtreecommitdiffstats
path: root/src/widgets/qsplitter.cpp
diff options
context:
space:
mode:
authorTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-07-10 15:24:15 -0500
committerTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-07-10 15:24:15 -0500
commitbd0f3345a938b35ce6a12f6150373b0955b8dd12 (patch)
tree7a520322212d48ebcb9fbe1087e7fca28b76185c /src/widgets/qsplitter.cpp
downloadqt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.tar.gz
qt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.zip
Add Qt3 development HEAD version
Diffstat (limited to 'src/widgets/qsplitter.cpp')
-rw-r--r--src/widgets/qsplitter.cpp1429
1 files changed, 1429 insertions, 0 deletions
diff --git a/src/widgets/qsplitter.cpp b/src/widgets/qsplitter.cpp
new file mode 100644
index 0000000..4810911
--- /dev/null
+++ b/src/widgets/qsplitter.cpp
@@ -0,0 +1,1429 @@
+/****************************************************************************
+**
+** Implementation of QSplitter class
+**
+** Created : 980105
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the widgets module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsplitter.h"
+#ifndef QT_NO_SPLITTER
+
+#include "qlayout.h"
+#include "../kernel/qlayoutengine_p.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qdrawutil.h"
+#include "qmemarray.h"
+#include "qobjectlist.h"
+#include "qpainter.h"
+#include "qptrlist.h"
+#include "qstyle.h"
+
+class QSplitterHandle : public QWidget
+{
+ Q_OBJECT
+public:
+ QSplitterHandle( Orientation o,
+ QSplitter *parent, const char* name=0 );
+ void setOrientation( Orientation o );
+ Orientation orientation() const { return orient; }
+
+ bool opaque() const { return s->opaqueResize(); }
+
+ QSize sizeHint() const;
+
+ int id() const { return myId; } // d->list.at(id())->wid == this
+ void setId( int i ) { myId = i; }
+
+protected:
+ void paintEvent( QPaintEvent * );
+ void mouseMoveEvent( QMouseEvent * );
+ void mousePressEvent( QMouseEvent * );
+ void mouseReleaseEvent( QMouseEvent * );
+
+private:
+ Orientation orient;
+ bool opaq;
+ int myId;
+
+ QSplitter *s;
+};
+
+#include "qsplitter.moc"
+
+const uint Default = 2;
+
+static int mouseOffset;
+static int opaqueOldPos = -1; // this assumes that there's only one mouse
+
+static QPoint toggle( QWidget *w, QPoint pos )
+{
+ QSize minS = qSmartMinSize( w );
+ return -pos - QPoint( minS.width(), minS.height() );
+}
+
+static bool isCollapsed( QWidget *w )
+{
+ return w->x() < 0 || w->y() < 0;
+}
+
+static QPoint topLeft( QWidget *w )
+{
+ if ( isCollapsed(w) ) {
+ return toggle( w, w->pos() );
+ } else {
+ return w->pos();
+ }
+}
+
+static QPoint bottomRight( QWidget *w )
+{
+ if ( isCollapsed(w) ) {
+ return toggle( w, w->pos() ) - QPoint( 1, 1 );
+ } else {
+ return w->geometry().bottomRight();
+ }
+}
+
+QSplitterHandle::QSplitterHandle( Orientation o, QSplitter *parent,
+ const char * name )
+ : QWidget( parent, name )
+{
+ s = parent;
+ setOrientation( o );
+}
+
+QSize QSplitterHandle::sizeHint() const
+{
+ int hw = s->handleWidth();
+ return parentWidget()->style().sizeFromContents( QStyle::CT_Splitter, s,
+ QSize(hw, hw) )
+ .expandedTo( QApplication::globalStrut() );
+}
+
+void QSplitterHandle::setOrientation( Orientation o )
+{
+ orient = o;
+#ifndef QT_NO_CURSOR
+ setCursor( o == QSplitter::Horizontal ? splitHCursor : splitVCursor );
+#endif
+}
+
+void QSplitterHandle::mouseMoveEvent( QMouseEvent *e )
+{
+ if ( !(e->state()&LeftButton) )
+ return;
+ QCOORD pos = s->pick( parentWidget()->mapFromGlobal(e->globalPos()) )
+ - mouseOffset;
+ if ( opaque() ) {
+ s->moveSplitter( pos, id() );
+ } else {
+ s->setRubberband( s->adjustPos(pos, id()) );
+ }
+}
+
+void QSplitterHandle::mousePressEvent( QMouseEvent *e )
+{
+ if ( e->button() == LeftButton )
+ mouseOffset = s->pick( e->pos() );
+}
+
+void QSplitterHandle::mouseReleaseEvent( QMouseEvent *e )
+{
+ if ( !opaque() && e->button() == LeftButton ) {
+ QCOORD pos = s->pick( parentWidget()->mapFromGlobal(e->globalPos()) )
+ - mouseOffset;
+ s->setRubberband( -1 );
+ s->moveSplitter( pos, id() );
+ }
+}
+
+void QSplitterHandle::paintEvent( QPaintEvent * )
+{
+ QPainter p( this );
+ parentWidget()->style().drawPrimitive( QStyle::PE_Splitter, &p, rect(),
+ colorGroup(),
+ (orientation() == Horizontal ?
+ QStyle::Style_Horizontal : 0) );
+}
+
+class QSplitterLayoutStruct : public Qt
+{
+public:
+ QCOORD sizer;
+ uint isHandle : 1;
+ uint collapsible : 2;
+ uint resizeMode : 2;
+ QWidget *wid;
+
+ QSplitterLayoutStruct()
+ : sizer( -1 ), collapsible( Default ) { }
+ QCOORD getSizer( Orientation orient );
+};
+
+QCOORD QSplitterLayoutStruct::getSizer( Orientation orient )
+{
+ if ( sizer == -1 ) {
+ QSize s = wid->sizeHint();
+ if ( !s.isValid() || wid->testWState(WState_Resized) )
+ s = wid->size();
+ sizer = ( orient == Horizontal ) ? s.width() : s.height();
+ }
+ return sizer;
+}
+
+class QSplitterPrivate
+{
+public:
+ QSplitterPrivate()
+ : opaque( FALSE ), firstShow( TRUE ), childrenCollapsible( TRUE ),
+ handleWidth( 0 ) { }
+
+ QPtrList<QSplitterLayoutStruct> list;
+ bool opaque : 8;
+ bool firstShow : 8;
+ bool childrenCollapsible : 8;
+ int handleWidth;
+};
+
+
+/*!
+ \class QSplitter
+ \brief The QSplitter class implements a splitter widget.
+
+ \ingroup organizers
+ \mainclass
+
+ A splitter lets the user control the size of child widgets by
+ dragging the boundary between the children. Any number of widgets
+ may be controlled by a single splitter.
+
+ To show a QListBox, a QListView and a QTextEdit side by side:
+ \code
+ QSplitter *split = new QSplitter( parent );
+ QListBox *lb = new QListBox( split );
+ QListView *lv = new QListView( split );
+ QTextEdit *ed = new QTextEdit( split );
+ \endcode
+
+ QSplitter lays out its children horizontally (side by side); you
+ can use setOrientation(QSplitter::Vertical) to lay out the
+ children vertically.
+
+ By default, all widgets can be as large or as small as the user
+ wishes, between the \l minimumSizeHint() (or \l minimumSize())
+ and \l maximumSize() of the widgets. Use setResizeMode() to
+ specify that a widget should keep its size when the splitter is
+ resized, or set the stretch component of the \l sizePolicy.
+
+ Although QSplitter normally resizes the children only at the end
+ of a resize operation, if you call setOpaqueResize(TRUE) the
+ widgets are resized as often as possible.
+
+ The initial distribution of size between the widgets is determined
+ by the initial size of each widget. You can also use setSizes() to
+ set the sizes of all the widgets. The function sizes() returns the
+ sizes set by the user.
+
+ If you hide() a child its space will be distributed among the
+ other children. It will be reinstated when you show() it again. It
+ is also possible to reorder the widgets within the splitter using
+ moveToFirst() and moveToLast().
+
+ <img src=qsplitter-m.png> <img src=qsplitter-w.png>
+
+ \sa QTabBar
+*/
+
+
+/*!
+ Constructs a horizontal splitter with the \a parent and \a name
+ arguments being passed on to the QFrame constructor.
+*/
+
+QSplitter::QSplitter( QWidget *parent, const char *name )
+ : QFrame( parent, name, WPaintUnclipped )
+{
+ orient = Horizontal;
+ init();
+}
+
+
+/*!
+ Constructs a splitter with orientation \a o with the \a parent and
+ \a name arguments being passed on to the QFrame constructor.
+*/
+
+QSplitter::QSplitter( Orientation o, QWidget *parent, const char *name )
+ : QFrame( parent, name, WPaintUnclipped )
+{
+ orient = o;
+ init();
+}
+
+
+/*!
+ Destroys the splitter and any children.
+*/
+
+QSplitter::~QSplitter()
+{
+ delete d;
+}
+
+
+void QSplitter::init()
+{
+ d = new QSplitterPrivate;
+ d->list.setAutoDelete( TRUE );
+ QSizePolicy sp( QSizePolicy::Expanding, QSizePolicy::Preferred );
+ if ( orient == Vertical )
+ sp.transpose();
+ setSizePolicy( sp );
+ clearWState( WState_OwnSizePolicy );
+}
+
+/*!
+ \fn void QSplitter::refresh()
+
+ Updates the splitter's state. You should not need to call this
+ function.
+*/
+
+
+/*!
+ \property QSplitter::orientation
+ \brief the orientation of the splitter
+
+ By default the orientation is horizontal (the widgets are side by
+ side). The possible orientations are \c Horizontal and
+ \c Vertical.
+*/
+
+void QSplitter::setOrientation( Orientation o )
+{
+ if ( orient == o )
+ return;
+
+ if ( !testWState( WState_OwnSizePolicy ) ) {
+ QSizePolicy sp = sizePolicy();
+ sp.transpose();
+ setSizePolicy( sp );
+ clearWState( WState_OwnSizePolicy );
+ }
+
+ orient = o;
+
+ QSplitterLayoutStruct *s = d->list.first();
+ while ( s ) {
+ if ( s->isHandle )
+ ((QSplitterHandle*)s->wid)->setOrientation( o );
+ s = d->list.next();
+ }
+ recalc( isVisible() );
+}
+
+/*!
+ \property QSplitter::childrenCollapsible
+ \brief whether child widgets can be resized down to size 0 by the user
+
+ By default, children are collapsible. It is possible to enable
+ and disable the collapsing of individual children; see
+ setCollapsible().
+*/
+
+void QSplitter::setChildrenCollapsible( bool collapse )
+{
+ d->childrenCollapsible = collapse;
+}
+
+bool QSplitter::childrenCollapsible() const
+{
+ return d->childrenCollapsible;
+}
+
+/*!
+ Sets whether the child widget \a w is collapsible to \a collapse.
+
+ By default, children are collapsible, meaning that the user can
+ resize them down to size 0, even if they have a non-zero
+ minimumSize() or minimumSizeHint(). This behavior can be changed
+ on a per-widget basis by calling this function, or globally for
+ all the widgets in the splitter by setting the \l
+ childrenCollapsible property.
+
+ \sa childrenCollapsible
+*/
+
+void QSplitter::setCollapsible( QWidget *w, bool collapse )
+{
+ findWidget( w )->collapsible = collapse ? 1 : 0;
+}
+
+/*!
+ \reimp
+*/
+void QSplitter::resizeEvent( QResizeEvent * )
+{
+ doResize();
+}
+
+QSplitterLayoutStruct *QSplitter::findWidget( QWidget *w )
+{
+ processChildEvents();
+ QSplitterLayoutStruct *s = d->list.first();
+ while ( s ) {
+ if ( s->wid == w )
+ return s;
+ s = d->list.next();
+ }
+ return addWidget( w );
+}
+
+/*
+ Inserts the widget \a w at the end (or at the beginning if \a
+ prepend is TRUE) of the splitter's list of widgets.
+
+ It is the responsibility of the caller to make sure that \a w is
+ not already in the splitter and to call recalcId() if needed. (If
+ \a prepend is TRUE, then recalcId() is very probably needed.)
+*/
+
+QSplitterLayoutStruct *QSplitter::addWidget( QWidget *w, bool prepend )
+{
+ QSplitterLayoutStruct *s;
+ QSplitterHandle *newHandle = 0;
+ if ( d->list.count() > 0 ) {
+ s = new QSplitterLayoutStruct;
+ s->resizeMode = KeepSize;
+ QString tmp = "qt_splithandle_";
+ tmp += w->name();
+ newHandle = new QSplitterHandle( orientation(), this, tmp );
+ s->wid = newHandle;
+ newHandle->setId( d->list.count() );
+ s->isHandle = TRUE;
+ s->sizer = pick( newHandle->sizeHint() );
+ if ( prepend )
+ d->list.prepend( s );
+ else
+ d->list.append( s );
+ }
+ s = new QSplitterLayoutStruct;
+ s->resizeMode = DefaultResizeMode;
+ s->wid = w;
+ s->isHandle = FALSE;
+ if ( prepend )
+ d->list.prepend( s );
+ else
+ d->list.append( s );
+ if ( newHandle && isVisible() )
+ newHandle->show(); // will trigger sending of post events
+ return s;
+}
+
+
+/*!
+ Tells the splitter that the child widget described by \a c has
+ been inserted or removed.
+*/
+
+void QSplitter::childEvent( QChildEvent *c )
+{
+ if ( c->type() == QEvent::ChildInserted ) {
+ if ( !c->child()->isWidgetType() )
+ return;
+
+ if ( ((QWidget*)c->child())->testWFlags( WType_TopLevel ) )
+ return;
+
+ QSplitterLayoutStruct *s = d->list.first();
+ while ( s ) {
+ if ( s->wid == c->child() )
+ return;
+ s = d->list.next();
+ }
+ addWidget( (QWidget*)c->child() );
+ recalc( isVisible() );
+ } else if ( c->type() == QEvent::ChildRemoved ) {
+ QSplitterLayoutStruct *prev = 0;
+ if ( d->list.count() > 1 )
+ prev = d->list.at( 1 ); // yes, this is correct
+ QSplitterLayoutStruct *curr = d->list.first();
+ while ( curr ) {
+ if ( curr->wid == c->child() ) {
+ d->list.removeRef( curr );
+ if ( prev && prev->isHandle ) {
+ QWidget *w = prev->wid;
+ d->list.removeRef( prev );
+ delete w; // will call childEvent()
+ }
+ recalcId();
+ doResize();
+ return;
+ }
+ prev = curr;
+ curr = d->list.next();
+ }
+ }
+}
+
+
+/*!
+ Displays a rubber band at position \a p. If \a p is negative, the
+ rubber band is removed.
+*/
+
+void QSplitter::setRubberband( int p )
+{
+ QPainter paint( this );
+ paint.setPen( gray );
+ paint.setBrush( gray );
+ paint.setRasterOp( XorROP );
+ QRect r = contentsRect();
+ const int rBord = 3; // customizable?
+ int hw = handleWidth();
+ if ( orient == Horizontal ) {
+ if ( opaqueOldPos >= 0 )
+ paint.drawRect( opaqueOldPos + hw / 2 - rBord, r.y(),
+ 2 * rBord, r.height() );
+ if ( p >= 0 )
+ paint.drawRect( p + hw / 2 - rBord, r.y(), 2 * rBord, r.height() );
+ } else {
+ if ( opaqueOldPos >= 0 )
+ paint.drawRect( r.x(), opaqueOldPos + hw / 2 - rBord,
+ r.width(), 2 * rBord );
+ if ( p >= 0 )
+ paint.drawRect( r.x(), p + hw / 2 - rBord, r.width(), 2 * rBord );
+ }
+ opaqueOldPos = p;
+}
+
+
+/*!
+ \reimp
+*/
+
+bool QSplitter::event( QEvent *e )
+{
+ switch ( e->type() ) {
+ case QEvent::Show:
+ if ( !d->firstShow )
+ break;
+ d->firstShow = FALSE;
+ // fall through
+ case QEvent::LayoutHint:
+ recalc( isVisible() );
+ break;
+ default:
+ ;
+ }
+ return QWidget::event( e );
+}
+
+
+/*!
+ \obsolete
+
+ Draws the splitter handle in the rectangle described by \a x, \a y,
+ \a w, \a h using painter \a p.
+ \sa QStyle::drawPrimitive()
+*/
+
+// ### Remove this in 4.0
+
+void QSplitter::drawSplitter( QPainter *p,
+ QCOORD x, QCOORD y, QCOORD w, QCOORD h )
+{
+ style().drawPrimitive(QStyle::PE_Splitter, p, QRect(x, y, w, h), colorGroup(),
+ (orientation() == Horizontal ?
+ QStyle::Style_Horizontal : 0));
+}
+
+
+/*!
+ Returns the ID of the widget to the right of or below the widget
+ \a w, or 0 if there is no such widget (i.e. it is either not in
+ this QSplitter or \a w is at the end).
+*/
+
+int QSplitter::idAfter( QWidget* w ) const
+{
+ QSplitterLayoutStruct *s = d->list.first();
+ bool seen_w = FALSE;
+ while ( s ) {
+ if ( s->isHandle && seen_w )
+ return d->list.at();
+ if ( !s->isHandle && s->wid == w )
+ seen_w = TRUE;
+ s = d->list.next();
+ }
+ return 0;
+}
+
+
+/*!
+ Moves the left/top edge of the splitter handle with ID \a id as
+ close as possible to position \a p, which is the distance from the
+ left (or top) edge of the widget.
+
+ For Arabic, Hebrew and other right-to-left languages the layout is
+ reversed. \a p is then the distance from the right (or top) edge
+ of the widget.
+
+ \sa idAfter()
+*/
+void QSplitter::moveSplitter( QCOORD p, int id )
+{
+ QSplitterLayoutStruct *s = d->list.at( id );
+ int farMin;
+ int min;
+ int max;
+ int farMax;
+
+ p = adjustPos( p, id, &farMin, &min, &max, &farMax );
+ int oldP = pick( s->wid->pos() );
+
+ if ( QApplication::reverseLayout() && orient == Horizontal ) {
+ int q = p + s->wid->width();
+ doMove( FALSE, q, id - 1, -1, (q > oldP), (p > max) );
+ doMove( TRUE, q, id, -1, (q > oldP), (p < min) );
+ } else {
+ doMove( FALSE, p, id, +1, (p < oldP), (p > max) );
+ doMove( TRUE, p, id - 1, +1, (p < oldP), (p < min) );
+ }
+ storeSizes();
+}
+
+
+void QSplitter::setGeo( QWidget *w, int p, int s, bool splitterMoved )
+{
+ QRect r;
+ if ( orient == Horizontal ) {
+ if ( QApplication::reverseLayout() && orient == Horizontal
+ && !splitterMoved )
+ p = contentsRect().width() - p - s;
+ r.setRect( p, contentsRect().y(), s, contentsRect().height() );
+ } else {
+ r.setRect( contentsRect().x(), p, contentsRect().width(), s );
+ }
+
+ /*
+ Hide the child widget, but without calling hide() so that the
+ splitter handle is still shown.
+ */
+ if ( !w->isHidden() && s <= 0 && pick(qSmartMinSize(w)) > 0 )
+ r.moveTopLeft( toggle(w, r.topLeft()) );
+ w->setGeometry( r );
+}
+
+
+void QSplitter::doMove( bool backwards, int pos, int id, int delta, bool upLeft,
+ bool mayCollapse )
+{
+ if ( id < 0 || id >= (int) d->list.count() )
+ return;
+
+ QSplitterLayoutStruct *s = d->list.at( id );
+ QWidget *w = s->wid;
+
+ int nextId = backwards ? id - delta : id + delta;
+
+ if ( w->isHidden() ) {
+ doMove( backwards, pos, nextId, delta, upLeft, TRUE );
+ } else {
+ if ( s->isHandle ) {
+ int dd = s->getSizer( orient );
+ int nextPos = backwards ? pos - dd : pos + dd;
+ int left = backwards ? pos - dd : pos;
+ setGeo( w, left, dd, TRUE );
+ doMove( backwards, nextPos, nextId, delta, upLeft, mayCollapse );
+ } else {
+ int dd = backwards ? pos - pick( topLeft(w) )
+ : pick( bottomRight(w) ) - pos + 1;
+ if ( dd > 0 || (!isCollapsed(w) && !mayCollapse) ) {
+ dd = QMAX( pick(qSmartMinSize(w)),
+ QMIN(dd, pick(w->maximumSize())) );
+ } else {
+ dd = 0;
+ }
+ setGeo( w, backwards ? pos - dd : pos, dd, TRUE );
+ doMove( backwards, backwards ? pos - dd : pos + dd, nextId, delta,
+ upLeft, TRUE );
+ }
+ }
+}
+
+int QSplitter::findWidgetJustBeforeOrJustAfter( int id, int delta, int &collapsibleSize )
+{
+ id += delta;
+ do {
+ QWidget *w = d->list.at( id )->wid;
+ if ( !w->isHidden() ) {
+ if ( collapsible(d->list.at(id)) )
+ collapsibleSize = pick( qSmartMinSize(w) );
+ return id;
+ }
+ id += 2 * delta; // go to previous (or next) widget, skip the handle
+ } while ( id >= 0 && id < (int)d->list.count() );
+
+ return -1;
+}
+
+void QSplitter::getRange( int id, int *farMin, int *min, int *max, int *farMax )
+{
+ int n = d->list.count();
+ if ( id <= 0 || id >= n - 1 )
+ return;
+
+ int collapsibleSizeBefore = 0;
+ int idJustBefore = findWidgetJustBeforeOrJustAfter( id, -1, collapsibleSizeBefore );
+
+ int collapsibleSizeAfter = 0;
+ int idJustAfter = findWidgetJustBeforeOrJustAfter( id, +1, collapsibleSizeAfter );
+
+ int minBefore = 0;
+ int minAfter = 0;
+ int maxBefore = 0;
+ int maxAfter = 0;
+ int i;
+
+ for ( i = 0; i < id; i++ )
+ addContribution( i, &minBefore, &maxBefore, i == idJustBefore );
+ for ( i = id; i < n; i++ )
+ addContribution( i, &minAfter, &maxAfter, i == idJustAfter );
+
+ QRect r = contentsRect();
+ int farMinVal;
+ int minVal;
+ int maxVal;
+ int farMaxVal;
+
+ int smartMinBefore = QMAX( minBefore, pick(r.size()) - maxAfter );
+ int smartMaxBefore = QMIN( maxBefore, pick(r.size()) - minAfter );
+
+ if ( orient == Vertical || !QApplication::reverseLayout() ) {
+ minVal = pick( r.topLeft() ) + smartMinBefore;
+ maxVal = pick( r.topLeft() ) + smartMaxBefore;
+
+ farMinVal = minVal;
+ if ( minBefore - collapsibleSizeBefore >= pick(r.size()) - maxAfter )
+ farMinVal -= collapsibleSizeBefore;
+ farMaxVal = maxVal;
+ if ( pick(r.size()) - (minAfter - collapsibleSizeAfter) <= maxBefore )
+ farMaxVal += collapsibleSizeAfter;
+ } else {
+ int hw = handleWidth();
+ minVal = r.width() - smartMaxBefore - hw;
+ maxVal = r.width() - smartMinBefore - hw;
+
+ farMinVal = minVal;
+ if ( pick(r.size()) - (minAfter - collapsibleSizeAfter) <= maxBefore )
+ farMinVal -= collapsibleSizeAfter;
+ farMaxVal = maxVal;
+ if ( minBefore - collapsibleSizeBefore >= pick(r.size()) - maxAfter )
+ farMaxVal += collapsibleSizeBefore;
+ }
+
+ if ( farMin )
+ *farMin = farMinVal;
+ if ( min )
+ *min = minVal;
+ if ( max )
+ *max = maxVal;
+ if ( farMax )
+ *farMax = farMaxVal;
+}
+
+/*!
+ Returns the valid range of the splitter with ID \a id in \a *min
+ and \a *max if \a min and \a max are not 0.
+
+ \sa idAfter()
+*/
+
+void QSplitter::getRange( int id, int *min, int *max )
+{
+ getRange( id, min, 0, 0, max );
+}
+
+
+/*!
+ Returns the closest legal position to \a pos of the widget with ID
+ \a id.
+
+ \sa idAfter()
+*/
+
+int QSplitter::adjustPos( int pos, int id )
+{
+ int x, i, n, u;
+ return adjustPos( pos, id, &u, &n, &i, &x );
+}
+
+int QSplitter::adjustPos( int pos, int id, int *farMin, int *min, int *max,
+ int *farMax )
+{
+ const int Threshold = 40;
+
+ getRange( id, farMin, min, max, farMax );
+
+ if ( pos >= *min ) {
+ if ( pos <= *max ) {
+ return pos;
+ } else {
+ int delta = pos - *max;
+ int width = *farMax - *max;
+
+ if ( delta > width / 2 && delta >= QMIN(Threshold, width) ) {
+ return *farMax;
+ } else {
+ return *max;
+ }
+ }
+ } else {
+ int delta = *min - pos;
+ int width = *min - *farMin;
+
+ if ( delta > width / 2 && delta >= QMIN(Threshold, width) ) {
+ return *farMin;
+ } else {
+ return *min;
+ }
+ }
+}
+
+bool QSplitter::collapsible( QSplitterLayoutStruct *s )
+{
+ if (pick(qSmartMinSize(s->wid)) == 1)
+ return FALSE;
+ if ( s->collapsible != Default ) {
+ return (bool) s->collapsible;
+ } else {
+ return d->childrenCollapsible;
+ }
+}
+
+void QSplitter::doResize()
+{
+ QRect r = contentsRect();
+ int n = d->list.count();
+ QMemArray<QLayoutStruct> a( n );
+
+ for ( int pass = 0; pass < 2; pass++ ) {
+ int numAutoWithStretch = 0;
+ int numAutoWithoutStretch = 0;
+
+ for ( int i = 0; i < n; i++ ) {
+ a[i].init();
+ QSplitterLayoutStruct *s = d->list.at( i );
+ if ( s->wid->isHidden() || isCollapsed(s->wid) ) {
+ a[i].maximumSize = 0;
+ } else if ( s->isHandle ) {
+ a[i].sizeHint = a[i].minimumSize = a[i].maximumSize = s->sizer;
+ a[i].empty = FALSE;
+ } else {
+ int mode = s->resizeMode;
+ int stretch = 1;
+
+ if ( mode == DefaultResizeMode ) {
+ QSizePolicy p = s->wid->sizePolicy();
+ int sizePolicyStretch =
+ pick( QSize(p.horStretch(), p.verStretch()) );
+ if ( sizePolicyStretch > 0 ) {
+ mode = Stretch;
+ stretch = sizePolicyStretch;
+ numAutoWithStretch++;
+ } else {
+ /*
+ Do things differently on the second pass,
+ if there's one. A second pass is necessary
+ if it was found out during the first pass
+ that all DefaultResizeMode items are
+ KeepSize items. In that case, we make them
+ all Stretch items instead, for a more Qt
+ 3.0-compatible behavior.
+ */
+ mode = ( pass == 0 ) ? KeepSize : Stretch;
+ numAutoWithoutStretch++;
+ }
+ }
+
+ a[i].minimumSize = pick( qSmartMinSize(s->wid) );
+ a[i].maximumSize = pick( s->wid->maximumSize() );
+ a[i].empty = FALSE;
+
+ if ( mode == Stretch ) {
+ if ( s->getSizer(orient) > 1 )
+ stretch *= s->getSizer( orient );
+ // QMIN(): ad hoc work-around for layout engine limitation
+ a[i].stretch = QMIN( stretch, 8192 );
+ a[i].sizeHint = a[i].minimumSize;
+ } else if ( mode == KeepSize ) {
+ a[i].sizeHint = s->getSizer( orient );
+ } else { // mode == FollowSizeHint
+ a[i].sizeHint = pick( s->wid->sizeHint() );
+ }
+ }
+ }
+
+ // a second pass would yield the same results
+ if ( numAutoWithStretch > 0 || numAutoWithoutStretch == 0 )
+ break;
+ }
+
+ qGeomCalc( a, 0, n, pick( r.topLeft() ), pick( r.size() ), 0 );
+
+ for ( int i = 0; i < n; i++ ) {
+ QSplitterLayoutStruct *s = d->list.at(i);
+ setGeo( s->wid, a[i].pos, a[i].size, FALSE );
+ }
+}
+
+void QSplitter::recalc( bool update )
+{
+ int fi = 2 * frameWidth();
+ int maxl = fi;
+ int minl = fi;
+ int maxt = QWIDGETSIZE_MAX;
+ int mint = fi;
+ int n = d->list.count();
+ bool first = TRUE;
+
+ /*
+ Splitter handles before the first visible widget or right
+ before a hidden widget must be hidden.
+ */
+ for ( int i = 0; i < n; i++ ) {
+ QSplitterLayoutStruct *s = d->list.at( i );
+ if ( !s->isHandle ) {
+ QSplitterLayoutStruct *p = 0;
+ if ( i > 0 )
+ p = d->list.at( i - 1 );
+
+ // may trigger new recalc
+ if ( p && p->isHandle )
+ p->wid->setHidden( first || s->wid->isHidden() );
+
+ if ( !s->wid->isHidden() )
+ first = FALSE;
+ }
+ }
+
+ bool empty = TRUE;
+ for ( int j = 0; j < n; j++ ) {
+ QSplitterLayoutStruct *s = d->list.at( j );
+ if ( !s->wid->isHidden() ) {
+ empty = FALSE;
+ if ( s->isHandle ) {
+ minl += s->getSizer( orient );
+ maxl += s->getSizer( orient );
+ } else {
+ QSize minS = qSmartMinSize( s->wid );
+ minl += pick( minS );
+ maxl += pick( s->wid->maximumSize() );
+ mint = QMAX( mint, trans(minS) );
+ int tm = trans( s->wid->maximumSize() );
+ if ( tm > 0 )
+ maxt = QMIN( maxt, tm );
+ }
+ }
+ }
+ if ( empty ) {
+ if ( ::qt_cast<QSplitter*>(parentWidget()) ) {
+ // nested splitters; be nice
+ maxl = maxt = 0;
+ } else {
+ // QSplitter with no children yet
+ maxl = QWIDGETSIZE_MAX;
+ }
+ } else {
+ maxl = QMIN( maxl, QWIDGETSIZE_MAX );
+ }
+ if ( maxt < mint )
+ maxt = mint;
+
+ if ( orient == Horizontal ) {
+ setMaximumSize( maxl, maxt );
+ setMinimumSize( minl, mint );
+ } else {
+ setMaximumSize( maxt, maxl );
+ setMinimumSize( mint, minl );
+ }
+ if ( update )
+ doResize();
+ else
+ d->firstShow = TRUE;
+}
+
+/*!
+ \enum QSplitter::ResizeMode
+
+ This enum type describes how QSplitter will resize each of its
+ child widgets.
+
+ \value Auto The widget will be resized according to the stretch
+ factors set in its sizePolicy().
+
+ \value Stretch The widget will be resized when the splitter
+ itself is resized.
+
+ \value KeepSize QSplitter will try to keep the widget's size
+ unchanged.
+
+ \value FollowSizeHint QSplitter will resize the widget when the
+ widget's size hint changes.
+*/
+
+/*!
+ Sets resize mode of widget \a w to \a mode. (The default is \c
+ Auto.)
+*/
+
+void QSplitter::setResizeMode( QWidget *w, ResizeMode mode )
+{
+ findWidget( w )->resizeMode = mode;
+}
+
+
+/*!
+ \property QSplitter::opaqueResize
+ \brief whether resizing is opaque
+
+ Opaque resizing is off by default.
+*/
+
+bool QSplitter::opaqueResize() const
+{
+ return d->opaque;
+}
+
+
+void QSplitter::setOpaqueResize( bool on )
+{
+ d->opaque = on;
+}
+
+
+/*!
+ Moves widget \a w to the leftmost/top position.
+*/
+
+void QSplitter::moveToFirst( QWidget *w )
+{
+ processChildEvents();
+ bool found = FALSE;
+ QSplitterLayoutStruct *s = d->list.first();
+ while ( s ) {
+ if ( s->wid == w ) {
+ found = TRUE;
+ QSplitterLayoutStruct *p = d->list.prev();
+ if ( p ) { // not already at first place
+ d->list.take(); // take p
+ d->list.take(); // take s
+ d->list.prepend( p );
+ d->list.prepend( s );
+ }
+ break;
+ }
+ s = d->list.next();
+ }
+ if ( !found )
+ addWidget( w, TRUE );
+ recalcId();
+}
+
+
+/*!
+ Moves widget \a w to the rightmost/bottom position.
+*/
+
+void QSplitter::moveToLast( QWidget *w )
+{
+ processChildEvents();
+ bool found = FALSE;
+ QSplitterLayoutStruct *s = d->list.first();
+ while ( s ) {
+ if ( s->wid == w ) {
+ found = TRUE;
+ d->list.take(); // take s
+ QSplitterLayoutStruct *p = d->list.current();
+ if ( p ) { // the splitter handle after s
+ d->list.take(); // take p
+ d->list.append( p );
+ }
+ d->list.append( s );
+ break;
+ }
+ s = d->list.next();
+ }
+ if ( !found )
+ addWidget( w );
+ recalcId();
+}
+
+
+void QSplitter::recalcId()
+{
+ int n = d->list.count();
+ for ( int i = 0; i < n; i++ ) {
+ QSplitterLayoutStruct *s = d->list.at( i );
+ if ( s->isHandle )
+ ((QSplitterHandle*)s->wid)->setId( i );
+ }
+}
+
+
+/*!
+ \reimp
+*/
+QSize QSplitter::sizeHint() const
+{
+ constPolish();
+ int l = 0;
+ int t = 0;
+ if ( children() ) {
+ const QObjectList * c = children();
+ QObjectListIt it( *c );
+ QObject * o;
+
+ while( (o = it.current()) != 0 ) {
+ ++it;
+ if ( o->isWidgetType() && !((QWidget*)o)->isHidden() ) {
+ QSize s = ((QWidget*)o)->sizeHint();
+ if ( s.isValid() ) {
+ l += pick( s );
+ t = QMAX( t, trans( s ) );
+ }
+ }
+ }
+ }
+ return orientation() == Horizontal ? QSize( l, t ) : QSize( t, l );
+}
+
+
+/*!
+ \reimp
+*/
+
+QSize QSplitter::minimumSizeHint() const
+{
+ constPolish();
+ int l = 0;
+ int t = 0;
+ if ( children() ) {
+ const QObjectList * c = children();
+ QObjectListIt it( *c );
+ QObject * o;
+
+ while ( (o = it.current()) != 0 ) {
+ ++it;
+ if ( o->isWidgetType() && !((QWidget*)o)->isHidden() ) {
+ QSize s = qSmartMinSize( (QWidget*)o );
+ if ( s.isValid() ) {
+ l += pick( s );
+ t = QMAX( t, trans( s ) );
+ }
+ }
+ }
+ }
+ return orientation() == Horizontal ? QSize( l, t ) : QSize( t, l );
+}
+
+
+void QSplitter::storeSizes()
+{
+ QSplitterLayoutStruct *s = d->list.first();
+ while ( s ) {
+ if ( !s->isHandle )
+ s->sizer = pick( s->wid->size() );
+ s = d->list.next();
+ }
+}
+
+
+void QSplitter::addContribution( int id, int *min, int *max,
+ bool mayCollapse )
+{
+ QSplitterLayoutStruct *s = d->list.at( id );
+ if ( !s->wid->isHidden() ) {
+ if ( s->isHandle ) {
+ *min += s->getSizer( orient );
+ *max += s->getSizer( orient );
+ } else {
+ if ( mayCollapse || !isCollapsed(s->wid) )
+ *min += pick( qSmartMinSize(s->wid) );
+ *max += pick( s->wid->maximumSize() );
+ }
+ }
+}
+
+
+/*!
+ Returns a list of the size parameters of all the widgets in this
+ splitter.
+
+ If the splitter's orientation is horizontal, the list is a list of
+ widget widths; if the orientation is vertical, the list is a list
+ of widget heights.
+
+ Giving the values to another splitter's setSizes() function will
+ produce a splitter with the same layout as this one.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \code
+ QValueList<int> list = mySplitter.sizes();
+ QValueList<int>::Iterator it = list.begin();
+ while( it != list.end() ) {
+ myProcessing( *it );
+ ++it;
+ }
+ \endcode
+
+ \sa setSizes()
+*/
+
+QValueList<int> QSplitter::sizes() const
+{
+ if ( !testWState(WState_Polished) )
+ constPolish();
+
+ QValueList<int> list;
+ QSplitterLayoutStruct *s = d->list.first();
+ while ( s ) {
+ if ( !s->isHandle )
+ list.append( isCollapsed(s->wid) ? 0 : pick(s->wid->size()));
+ s = d->list.next();
+ }
+ return list;
+}
+
+/*!
+ Sets the size parameters to the values given in the \a list. If
+ the splitter is horizontal, the values set the widths of each
+ widget going from left to right. If the splitter is vertical, the
+ values set the heights of each widget going from top to bottom.
+ Extra values in the \a list are ignored.
+
+ If \a list contains too few values, the result is undefined but
+ the program will still be well-behaved.
+
+ Note that the values in \a list should be the height/width that
+ the widgets should be resized to.
+
+ \sa sizes()
+*/
+
+void QSplitter::setSizes( QValueList<int> list )
+{
+ processChildEvents();
+ QValueList<int>::Iterator it = list.begin();
+ QSplitterLayoutStruct *s = d->list.first();
+ while ( s && it != list.end() ) {
+ if ( !s->isHandle ) {
+ s->sizer = QMAX( *it, 0 );
+ int smartMinSize = pick( qSmartMinSize(s->wid) );
+ // Make sure that we reset the collapsed state.
+ if ( s->sizer == 0 ) {
+ if ( collapsible(s) && smartMinSize > 0 ) {
+ s->wid->move( -1, -1 );
+ } else {
+ s->sizer = smartMinSize;
+ s->wid->move( 0, 0 );
+ }
+ } else {
+ if ( s->sizer < smartMinSize )
+ s->sizer = smartMinSize;
+ s->wid->move( 0, 0 );
+ }
+ ++it;
+ }
+ s = d->list.next();
+ }
+ doResize();
+}
+
+/*!
+ \property QSplitter::handleWidth
+ \brief the width of the splitter handle
+*/
+
+int QSplitter::handleWidth() const
+{
+ if ( d->handleWidth > 0 ) {
+ return d->handleWidth;
+ } else {
+ return style().pixelMetric( QStyle::PM_SplitterWidth, this );
+ }
+}
+
+void QSplitter::setHandleWidth( int width )
+{
+ d->handleWidth = width;
+ updateHandles();
+}
+
+/*!
+ Processes all posted child events, ensuring that the internal state of
+ the splitter is kept consistent.
+*/
+
+void QSplitter::processChildEvents()
+{
+ QApplication::sendPostedEvents( this, QEvent::ChildInserted );
+}
+
+/*!
+ \reimp
+*/
+
+void QSplitter::styleChange( QStyle& old )
+{
+ updateHandles();
+ QFrame::styleChange( old );
+}
+
+void QSplitter::updateHandles()
+{
+ int hw = handleWidth();
+ QSplitterLayoutStruct *s = d->list.first();
+ while ( s ) {
+ if ( s->isHandle )
+ s->sizer = hw;
+ s = d->list.next();
+ }
+ recalc( isVisible() );
+}
+
+#ifndef QT_NO_TEXTSTREAM
+/*!
+ \relates QSplitter
+
+ Writes the sizes and the hidden state of the widgets in the
+ splitter \a splitter to the text stream \a ts.
+
+ \sa operator>>(), sizes(), QWidget::isHidden()
+*/
+
+QTextStream& operator<<( QTextStream& ts, const QSplitter& splitter )
+{
+ QSplitterLayoutStruct *s = splitter.d->list.first();
+ bool first = TRUE;
+ ts << "[";
+
+ while ( s != 0 ) {
+ if ( !s->isHandle ) {
+ if ( !first )
+ ts << ",";
+
+ if ( s->wid->isHidden() ) {
+ ts << "H";
+ } else if ( isCollapsed(s->wid) ) {
+ ts << 0;
+ } else {
+ ts << s->getSizer( splitter.orientation() );
+ }
+ first = FALSE;
+ }
+ s = splitter.d->list.next();
+ }
+ ts << "]" << endl;
+ return ts;
+}
+
+/*!
+ \relates QSplitter
+
+ Reads the sizes and the hidden state of the widgets in the
+ splitter \a splitter from the text stream \a ts. The sizes must
+ have been previously written by the operator<<() function.
+
+ \sa operator<<(), setSizes(), QWidget::hide()
+*/
+
+QTextStream& operator>>( QTextStream& ts, QSplitter& splitter )
+{
+#undef SKIP_SPACES
+#define SKIP_SPACES() \
+ while ( line[i].isSpace() ) \
+ i++
+
+ splitter.processChildEvents();
+ QSplitterLayoutStruct *s = splitter.d->list.first();
+ QString line = ts.readLine();
+ int i = 0;
+
+ SKIP_SPACES();
+ if ( line[i] == '[' ) {
+ i++;
+ SKIP_SPACES();
+ while ( line[i] != ']' ) {
+ while ( s != 0 && s->isHandle )
+ s = splitter.d->list.next();
+ if ( s == 0 )
+ break;
+
+ if ( line[i].upper() == 'H' ) {
+ s->wid->hide();
+ i++;
+ } else {
+ s->wid->show();
+ int dim = 0;
+ while ( line[i].digitValue() >= 0 ) {
+ dim *= 10;
+ dim += line[i].digitValue();
+ i++;
+ }
+ s->sizer = dim;
+ if ( dim == 0 )
+ splitter.setGeo( s->wid, 0, 0, FALSE );
+ }
+ SKIP_SPACES();
+ if ( line[i] == ',' ) {
+ i++;
+ } else {
+ break;
+ }
+ SKIP_SPACES();
+ s = splitter.d->list.next();
+ }
+ }
+ splitter.doResize();
+ return ts;
+}
+#endif
+
+#endif