source: vendor/trolltech/current/src/widgets/qsplitter.cpp

Last change on this file was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 35.0 KB
Line 
1/****************************************************************************
2** $Id: qsplitter.cpp 2 2005-11-16 15:49:26Z dmik $
3**
4** Implementation of QSplitter class
5**
6** Created : 980105
7**
8** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
9**
10** This file is part of the widgets module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qsplitter.h"
39#ifndef QT_NO_SPLITTER
40
41#include "qlayout.h"
42#include "../kernel/qlayoutengine_p.h"
43#include "qapplication.h"
44#include "qbitmap.h"
45#include "qdrawutil.h"
46#include "qmemarray.h"
47#include "qobjectlist.h"
48#include "qpainter.h"
49#include "qptrlist.h"
50#include "qstyle.h"
51
52class QSplitterHandle : public QWidget
53{
54 Q_OBJECT
55public:
56 QSplitterHandle( Orientation o,
57 QSplitter *parent, const char* name=0 );
58 void setOrientation( Orientation o );
59 Orientation orientation() const { return orient; }
60
61 bool opaque() const { return s->opaqueResize(); }
62
63 QSize sizeHint() const;
64
65 int id() const { return myId; } // d->list.at(id())->wid == this
66 void setId( int i ) { myId = i; }
67
68protected:
69 void paintEvent( QPaintEvent * );
70 void mouseMoveEvent( QMouseEvent * );
71 void mousePressEvent( QMouseEvent * );
72 void mouseReleaseEvent( QMouseEvent * );
73
74private:
75 Orientation orient;
76 bool opaq;
77 int myId;
78
79 QSplitter *s;
80};
81
82#include "qsplitter.moc"
83
84const uint Default = 2;
85
86static int mouseOffset;
87static int opaqueOldPos = -1; // this assumes that there's only one mouse
88
89static QPoint toggle( QWidget *w, QPoint pos )
90{
91 QSize minS = qSmartMinSize( w );
92 return -pos - QPoint( minS.width(), minS.height() );
93}
94
95static bool isCollapsed( QWidget *w )
96{
97 return w->x() < 0 || w->y() < 0;
98}
99
100static QPoint topLeft( QWidget *w )
101{
102 if ( isCollapsed(w) ) {
103 return toggle( w, w->pos() );
104 } else {
105 return w->pos();
106 }
107}
108
109static QPoint bottomRight( QWidget *w )
110{
111 if ( isCollapsed(w) ) {
112 return toggle( w, w->pos() ) - QPoint( 1, 1 );
113 } else {
114 return w->geometry().bottomRight();
115 }
116}
117
118QSplitterHandle::QSplitterHandle( Orientation o, QSplitter *parent,
119 const char * name )
120 : QWidget( parent, name )
121{
122 s = parent;
123 setOrientation( o );
124}
125
126QSize QSplitterHandle::sizeHint() const
127{
128 int hw = s->handleWidth();
129 return parentWidget()->style().sizeFromContents( QStyle::CT_Splitter, s,
130 QSize(hw, hw) )
131 .expandedTo( QApplication::globalStrut() );
132}
133
134void QSplitterHandle::setOrientation( Orientation o )
135{
136 orient = o;
137#ifndef QT_NO_CURSOR
138 setCursor( o == QSplitter::Horizontal ? splitHCursor : splitVCursor );
139#endif
140}
141
142void QSplitterHandle::mouseMoveEvent( QMouseEvent *e )
143{
144 if ( !(e->state()&LeftButton) )
145 return;
146 QCOORD pos = s->pick( parentWidget()->mapFromGlobal(e->globalPos()) )
147 - mouseOffset;
148 if ( opaque() ) {
149 s->moveSplitter( pos, id() );
150 } else {
151 s->setRubberband( s->adjustPos(pos, id()) );
152 }
153}
154
155void QSplitterHandle::mousePressEvent( QMouseEvent *e )
156{
157 if ( e->button() == LeftButton )
158 mouseOffset = s->pick( e->pos() );
159}
160
161void QSplitterHandle::mouseReleaseEvent( QMouseEvent *e )
162{
163 if ( !opaque() && e->button() == LeftButton ) {
164 QCOORD pos = s->pick( parentWidget()->mapFromGlobal(e->globalPos()) )
165 - mouseOffset;
166 s->setRubberband( -1 );
167 s->moveSplitter( pos, id() );
168 }
169}
170
171void QSplitterHandle::paintEvent( QPaintEvent * )
172{
173 QPainter p( this );
174 parentWidget()->style().drawPrimitive( QStyle::PE_Splitter, &p, rect(),
175 colorGroup(),
176 (orientation() == Horizontal ?
177 QStyle::Style_Horizontal : 0) );
178}
179
180class QSplitterLayoutStruct : public Qt
181{
182public:
183 QCOORD sizer;
184 uint isHandle : 1;
185 uint collapsible : 2;
186 uint resizeMode : 2;
187 QWidget *wid;
188
189 QSplitterLayoutStruct()
190 : sizer( -1 ), collapsible( Default ) { }
191 QCOORD getSizer( Orientation orient );
192};
193
194QCOORD QSplitterLayoutStruct::getSizer( Orientation orient )
195{
196 if ( sizer == -1 ) {
197 QSize s = wid->sizeHint();
198 if ( !s.isValid() || wid->testWState(WState_Resized) )
199 s = wid->size();
200 sizer = ( orient == Horizontal ) ? s.width() : s.height();
201 }
202 return sizer;
203}
204
205class QSplitterPrivate
206{
207public:
208 QSplitterPrivate()
209 : opaque( FALSE ), firstShow( TRUE ), childrenCollapsible( TRUE ),
210 handleWidth( 0 ) { }
211
212 QPtrList<QSplitterLayoutStruct> list;
213 bool opaque : 8;
214 bool firstShow : 8;
215 bool childrenCollapsible : 8;
216 int handleWidth;
217};
218
219
220/*!
221 \class QSplitter
222 \brief The QSplitter class implements a splitter widget.
223
224 \ingroup organizers
225 \mainclass
226
227 A splitter lets the user control the size of child widgets by
228 dragging the boundary between the children. Any number of widgets
229 may be controlled by a single splitter.
230
231 To show a QListBox, a QListView and a QTextEdit side by side:
232 \code
233 QSplitter *split = new QSplitter( parent );
234 QListBox *lb = new QListBox( split );
235 QListView *lv = new QListView( split );
236 QTextEdit *ed = new QTextEdit( split );
237 \endcode
238
239 QSplitter lays out its children horizontally (side by side); you
240 can use setOrientation(QSplitter::Vertical) to lay out the
241 children vertically.
242
243 By default, all widgets can be as large or as small as the user
244 wishes, between the \l minimumSizeHint() (or \l minimumSize())
245 and \l maximumSize() of the widgets. Use setResizeMode() to
246 specify that a widget should keep its size when the splitter is
247 resized, or set the stretch component of the \l sizePolicy.
248
249 Although QSplitter normally resizes the children only at the end
250 of a resize operation, if you call setOpaqueResize(TRUE) the
251 widgets are resized as often as possible.
252
253 The initial distribution of size between the widgets is determined
254 by the initial size of each widget. You can also use setSizes() to
255 set the sizes of all the widgets. The function sizes() returns the
256 sizes set by the user.
257
258 If you hide() a child its space will be distributed among the
259 other children. It will be reinstated when you show() it again. It
260 is also possible to reorder the widgets within the splitter using
261 moveToFirst() and moveToLast().
262
263 <img src=qsplitter-m.png> <img src=qsplitter-w.png>
264
265 \sa QTabBar
266*/
267
268
269/*!
270 Constructs a horizontal splitter with the \a parent and \a name
271 arguments being passed on to the QFrame constructor.
272*/
273
274QSplitter::QSplitter( QWidget *parent, const char *name )
275 : QFrame( parent, name, WPaintUnclipped )
276{
277 orient = Horizontal;
278 init();
279}
280
281
282/*!
283 Constructs a splitter with orientation \a o with the \a parent and
284 \a name arguments being passed on to the QFrame constructor.
285*/
286
287QSplitter::QSplitter( Orientation o, QWidget *parent, const char *name )
288 : QFrame( parent, name, WPaintUnclipped )
289{
290 orient = o;
291 init();
292}
293
294
295/*!
296 Destroys the splitter and any children.
297*/
298
299QSplitter::~QSplitter()
300{
301 delete d;
302}
303
304
305void QSplitter::init()
306{
307 d = new QSplitterPrivate;
308 d->list.setAutoDelete( TRUE );
309 QSizePolicy sp( QSizePolicy::Expanding, QSizePolicy::Preferred );
310 if ( orient == Vertical )
311 sp.transpose();
312 setSizePolicy( sp );
313 clearWState( WState_OwnSizePolicy );
314}
315
316/*!
317 \fn void QSplitter::refresh()
318
319 Updates the splitter's state. You should not need to call this
320 function.
321*/
322
323
324/*!
325 \property QSplitter::orientation
326 \brief the orientation of the splitter
327
328 By default the orientation is horizontal (the widgets are side by
329 side). The possible orientations are \c Horizontal and
330 \c Vertical.
331*/
332
333void QSplitter::setOrientation( Orientation o )
334{
335 if ( orient == o )
336 return;
337
338 if ( !testWState( WState_OwnSizePolicy ) ) {
339 QSizePolicy sp = sizePolicy();
340 sp.transpose();
341 setSizePolicy( sp );
342 clearWState( WState_OwnSizePolicy );
343 }
344
345 orient = o;
346
347 QSplitterLayoutStruct *s = d->list.first();
348 while ( s ) {
349 if ( s->isHandle )
350 ((QSplitterHandle*)s->wid)->setOrientation( o );
351 s = d->list.next();
352 }
353 recalc( isVisible() );
354}
355
356/*!
357 \property QSplitter::childrenCollapsible
358 \brief whether child widgets can be resized down to size 0 by the user
359
360 By default, children are collapsible. It is possible to enable
361 and disable the collapsing of individual children; see
362 setCollapsible().
363*/
364
365void QSplitter::setChildrenCollapsible( bool collapse )
366{
367 d->childrenCollapsible = collapse;
368}
369
370bool QSplitter::childrenCollapsible() const
371{
372 return d->childrenCollapsible;
373}
374
375/*!
376 Sets whether the child widget \a w is collapsible to \a collapse.
377
378 By default, children are collapsible, meaning that the user can
379 resize them down to size 0, even if they have a non-zero
380 minimumSize() or minimumSizeHint(). This behavior can be changed
381 on a per-widget basis by calling this function, or globally for
382 all the widgets in the splitter by setting the \l
383 childrenCollapsible property.
384
385 \sa childrenCollapsible
386*/
387
388void QSplitter::setCollapsible( QWidget *w, bool collapse )
389{
390 findWidget( w )->collapsible = collapse ? 1 : 0;
391}
392
393/*!
394 \reimp
395*/
396void QSplitter::resizeEvent( QResizeEvent * )
397{
398 doResize();
399}
400
401QSplitterLayoutStruct *QSplitter::findWidget( QWidget *w )
402{
403 processChildEvents();
404 QSplitterLayoutStruct *s = d->list.first();
405 while ( s ) {
406 if ( s->wid == w )
407 return s;
408 s = d->list.next();
409 }
410 return addWidget( w );
411}
412
413/*
414 Inserts the widget \a w at the end (or at the beginning if \a
415 prepend is TRUE) of the splitter's list of widgets.
416
417 It is the responsibility of the caller to make sure that \a w is
418 not already in the splitter and to call recalcId() if needed. (If
419 \a prepend is TRUE, then recalcId() is very probably needed.)
420*/
421
422QSplitterLayoutStruct *QSplitter::addWidget( QWidget *w, bool prepend )
423{
424 QSplitterLayoutStruct *s;
425 QSplitterHandle *newHandle = 0;
426 if ( d->list.count() > 0 ) {
427 s = new QSplitterLayoutStruct;
428 s->resizeMode = KeepSize;
429 QString tmp = "qt_splithandle_";
430 tmp += w->name();
431 newHandle = new QSplitterHandle( orientation(), this, tmp );
432 s->wid = newHandle;
433 newHandle->setId( d->list.count() );
434 s->isHandle = TRUE;
435 s->sizer = pick( newHandle->sizeHint() );
436 if ( prepend )
437 d->list.prepend( s );
438 else
439 d->list.append( s );
440 }
441 s = new QSplitterLayoutStruct;
442 s->resizeMode = DefaultResizeMode;
443 s->wid = w;
444 s->isHandle = FALSE;
445 if ( prepend )
446 d->list.prepend( s );
447 else
448 d->list.append( s );
449 if ( newHandle && isVisible() )
450 newHandle->show(); // will trigger sending of post events
451 return s;
452}
453
454
455/*!
456 Tells the splitter that the child widget described by \a c has
457 been inserted or removed.
458*/
459
460void QSplitter::childEvent( QChildEvent *c )
461{
462 if ( c->type() == QEvent::ChildInserted ) {
463 if ( !c->child()->isWidgetType() )
464 return;
465
466 if ( ((QWidget*)c->child())->testWFlags( WType_TopLevel ) )
467 return;
468
469 QSplitterLayoutStruct *s = d->list.first();
470 while ( s ) {
471 if ( s->wid == c->child() )
472 return;
473 s = d->list.next();
474 }
475 addWidget( (QWidget*)c->child() );
476 recalc( isVisible() );
477 } else if ( c->type() == QEvent::ChildRemoved ) {
478 QSplitterLayoutStruct *prev = 0;
479 if ( d->list.count() > 1 )
480 prev = d->list.at( 1 ); // yes, this is correct
481 QSplitterLayoutStruct *curr = d->list.first();
482 while ( curr ) {
483 if ( curr->wid == c->child() ) {
484 d->list.removeRef( curr );
485 if ( prev && prev->isHandle ) {
486 QWidget *w = prev->wid;
487 d->list.removeRef( prev );
488 delete w; // will call childEvent()
489 }
490 recalcId();
491 doResize();
492 return;
493 }
494 prev = curr;
495 curr = d->list.next();
496 }
497 }
498}
499
500
501/*!
502 Displays a rubber band at position \a p. If \a p is negative, the
503 rubber band is removed.
504*/
505
506void QSplitter::setRubberband( int p )
507{
508 QPainter paint( this );
509 paint.setPen( gray );
510 paint.setBrush( gray );
511 paint.setRasterOp( XorROP );
512 QRect r = contentsRect();
513 const int rBord = 3; // customizable?
514 int hw = handleWidth();
515 if ( orient == Horizontal ) {
516 if ( opaqueOldPos >= 0 )
517 paint.drawRect( opaqueOldPos + hw / 2 - rBord, r.y(),
518 2 * rBord, r.height() );
519 if ( p >= 0 )
520 paint.drawRect( p + hw / 2 - rBord, r.y(), 2 * rBord, r.height() );
521 } else {
522 if ( opaqueOldPos >= 0 )
523 paint.drawRect( r.x(), opaqueOldPos + hw / 2 - rBord,
524 r.width(), 2 * rBord );
525 if ( p >= 0 )
526 paint.drawRect( r.x(), p + hw / 2 - rBord, r.width(), 2 * rBord );
527 }
528 opaqueOldPos = p;
529}
530
531
532/*!
533 \reimp
534*/
535
536bool QSplitter::event( QEvent *e )
537{
538 switch ( e->type() ) {
539 case QEvent::Show:
540 if ( !d->firstShow )
541 break;
542 d->firstShow = FALSE;
543 // fall through
544 case QEvent::LayoutHint:
545 recalc( isVisible() );
546 break;
547 default:
548 ;
549 }
550 return QWidget::event( e );
551}
552
553
554/*!
555 \obsolete
556
557 Draws the splitter handle in the rectangle described by \a x, \a y,
558 \a w, \a h using painter \a p.
559 \sa QStyle::drawPrimitive()
560*/
561
562// ### Remove this in 4.0
563
564void QSplitter::drawSplitter( QPainter *p,
565 QCOORD x, QCOORD y, QCOORD w, QCOORD h )
566{
567 style().drawPrimitive(QStyle::PE_Splitter, p, QRect(x, y, w, h), colorGroup(),
568 (orientation() == Horizontal ?
569 QStyle::Style_Horizontal : 0));
570}
571
572
573/*!
574 Returns the ID of the widget to the right of or below the widget
575 \a w, or 0 if there is no such widget (i.e. it is either not in
576 this QSplitter or \a w is at the end).
577*/
578
579int QSplitter::idAfter( QWidget* w ) const
580{
581 QSplitterLayoutStruct *s = d->list.first();
582 bool seen_w = FALSE;
583 while ( s ) {
584 if ( s->isHandle && seen_w )
585 return d->list.at();
586 if ( !s->isHandle && s->wid == w )
587 seen_w = TRUE;
588 s = d->list.next();
589 }
590 return 0;
591}
592
593
594/*!
595 Moves the left/top edge of the splitter handle with ID \a id as
596 close as possible to position \a p, which is the distance from the
597 left (or top) edge of the widget.
598
599 For Arabic, Hebrew and other right-to-left languages the layout is
600 reversed. \a p is then the distance from the right (or top) edge
601 of the widget.
602
603 \sa idAfter()
604*/
605void QSplitter::moveSplitter( QCOORD p, int id )
606{
607 QSplitterLayoutStruct *s = d->list.at( id );
608 int farMin;
609 int min;
610 int max;
611 int farMax;
612
613 p = adjustPos( p, id, &farMin, &min, &max, &farMax );
614 int oldP = pick( s->wid->pos() );
615
616 if ( QApplication::reverseLayout() && orient == Horizontal ) {
617 int q = p + s->wid->width();
618 doMove( FALSE, q, id - 1, -1, (q > oldP), (p > max) );
619 doMove( TRUE, q, id, -1, (q > oldP), (p < min) );
620 } else {
621 doMove( FALSE, p, id, +1, (p < oldP), (p > max) );
622 doMove( TRUE, p, id - 1, +1, (p < oldP), (p < min) );
623 }
624 storeSizes();
625}
626
627
628void QSplitter::setGeo( QWidget *w, int p, int s, bool splitterMoved )
629{
630 QRect r;
631 if ( orient == Horizontal ) {
632 if ( QApplication::reverseLayout() && orient == Horizontal
633 && !splitterMoved )
634 p = contentsRect().width() - p - s;
635 r.setRect( p, contentsRect().y(), s, contentsRect().height() );
636 } else {
637 r.setRect( contentsRect().x(), p, contentsRect().width(), s );
638 }
639
640 /*
641 Hide the child widget, but without calling hide() so that the
642 splitter handle is still shown.
643 */
644 if ( !w->isHidden() && s <= 0 && pick(qSmartMinSize(w)) > 0 )
645 r.moveTopLeft( toggle(w, r.topLeft()) );
646 w->setGeometry( r );
647}
648
649
650void QSplitter::doMove( bool backwards, int pos, int id, int delta, bool upLeft,
651 bool mayCollapse )
652{
653 if ( id < 0 || id >= (int) d->list.count() )
654 return;
655
656 QSplitterLayoutStruct *s = d->list.at( id );
657 QWidget *w = s->wid;
658
659 int nextId = backwards ? id - delta : id + delta;
660
661 if ( w->isHidden() ) {
662 doMove( backwards, pos, nextId, delta, upLeft, TRUE );
663 } else {
664 if ( s->isHandle ) {
665 int dd = s->getSizer( orient );
666 int nextPos = backwards ? pos - dd : pos + dd;
667 int left = backwards ? pos - dd : pos;
668 setGeo( w, left, dd, TRUE );
669 doMove( backwards, nextPos, nextId, delta, upLeft, mayCollapse );
670 } else {
671 int dd = backwards ? pos - pick( topLeft(w) )
672 : pick( bottomRight(w) ) - pos + 1;
673 if ( dd > 0 || (!isCollapsed(w) && !mayCollapse) ) {
674 dd = QMAX( pick(qSmartMinSize(w)),
675 QMIN(dd, pick(w->maximumSize())) );
676 } else {
677 dd = 0;
678 }
679 setGeo( w, backwards ? pos - dd : pos, dd, TRUE );
680 doMove( backwards, backwards ? pos - dd : pos + dd, nextId, delta,
681 upLeft, TRUE );
682 }
683 }
684}
685
686int QSplitter::findWidgetJustBeforeOrJustAfter( int id, int delta, int &collapsibleSize )
687{
688 id += delta;
689 do {
690 QWidget *w = d->list.at( id )->wid;
691 if ( !w->isHidden() ) {
692 if ( collapsible(d->list.at(id)) )
693 collapsibleSize = pick( qSmartMinSize(w) );
694 return id;
695 }
696 id += 2 * delta; // go to previous (or next) widget, skip the handle
697 } while ( id >= 0 && id < (int)d->list.count() );
698
699 return -1;
700}
701
702void QSplitter::getRange( int id, int *farMin, int *min, int *max, int *farMax )
703{
704 int n = d->list.count();
705 if ( id <= 0 || id >= n - 1 )
706 return;
707
708 int collapsibleSizeBefore = 0;
709 int idJustBefore = findWidgetJustBeforeOrJustAfter( id, -1, collapsibleSizeBefore );
710
711 int collapsibleSizeAfter = 0;
712 int idJustAfter = findWidgetJustBeforeOrJustAfter( id, +1, collapsibleSizeAfter );
713
714 int minBefore = 0;
715 int minAfter = 0;
716 int maxBefore = 0;
717 int maxAfter = 0;
718 int i;
719
720 for ( i = 0; i < id; i++ )
721 addContribution( i, &minBefore, &maxBefore, i == idJustBefore );
722 for ( i = id; i < n; i++ )
723 addContribution( i, &minAfter, &maxAfter, i == idJustAfter );
724
725 QRect r = contentsRect();
726 int farMinVal;
727 int minVal;
728 int maxVal;
729 int farMaxVal;
730
731 int smartMinBefore = QMAX( minBefore, pick(r.size()) - maxAfter );
732 int smartMaxBefore = QMIN( maxBefore, pick(r.size()) - minAfter );
733
734 if ( orient == Vertical || !QApplication::reverseLayout() ) {
735 minVal = pick( r.topLeft() ) + smartMinBefore;
736 maxVal = pick( r.topLeft() ) + smartMaxBefore;
737
738 farMinVal = minVal;
739 if ( minBefore - collapsibleSizeBefore >= pick(r.size()) - maxAfter )
740 farMinVal -= collapsibleSizeBefore;
741 farMaxVal = maxVal;
742 if ( pick(r.size()) - (minAfter - collapsibleSizeAfter) <= maxBefore )
743 farMaxVal += collapsibleSizeAfter;
744 } else {
745 int hw = handleWidth();
746 minVal = r.width() - smartMaxBefore - hw;
747 maxVal = r.width() - smartMinBefore - hw;
748
749 farMinVal = minVal;
750 if ( pick(r.size()) - (minAfter - collapsibleSizeAfter) <= maxBefore )
751 farMinVal -= collapsibleSizeAfter;
752 farMaxVal = maxVal;
753 if ( minBefore - collapsibleSizeBefore >= pick(r.size()) - maxAfter )
754 farMaxVal += collapsibleSizeBefore;
755 }
756
757 if ( farMin )
758 *farMin = farMinVal;
759 if ( min )
760 *min = minVal;
761 if ( max )
762 *max = maxVal;
763 if ( farMax )
764 *farMax = farMaxVal;
765}
766
767/*!
768 Returns the valid range of the splitter with ID \a id in \a *min
769 and \a *max if \a min and \a max are not 0.
770
771 \sa idAfter()
772*/
773
774void QSplitter::getRange( int id, int *min, int *max )
775{
776 getRange( id, min, 0, 0, max );
777}
778
779
780/*!
781 Returns the closest legal position to \a pos of the widget with ID
782 \a id.
783
784 \sa idAfter()
785*/
786
787int QSplitter::adjustPos( int pos, int id )
788{
789 int x, i, n, u;
790 return adjustPos( pos, id, &u, &n, &i, &x );
791}
792
793int QSplitter::adjustPos( int pos, int id, int *farMin, int *min, int *max,
794 int *farMax )
795{
796 const int Threshold = 40;
797
798 getRange( id, farMin, min, max, farMax );
799
800 if ( pos >= *min ) {
801 if ( pos <= *max ) {
802 return pos;
803 } else {
804 int delta = pos - *max;
805 int width = *farMax - *max;
806
807 if ( delta > width / 2 && delta >= QMIN(Threshold, width) ) {
808 return *farMax;
809 } else {
810 return *max;
811 }
812 }
813 } else {
814 int delta = *min - pos;
815 int width = *min - *farMin;
816
817 if ( delta > width / 2 && delta >= QMIN(Threshold, width) ) {
818 return *farMin;
819 } else {
820 return *min;
821 }
822 }
823}
824
825bool QSplitter::collapsible( QSplitterLayoutStruct *s )
826{
827 if (pick(qSmartMinSize(s->wid)) == 1)
828 return false;
829 if ( s->collapsible != Default ) {
830 return (bool) s->collapsible;
831 } else {
832 return d->childrenCollapsible;
833 }
834}
835
836void QSplitter::doResize()
837{
838 QRect r = contentsRect();
839 int n = d->list.count();
840 QMemArray<QLayoutStruct> a( n );
841
842 for ( int pass = 0; pass < 2; pass++ ) {
843 int numAutoWithStretch = 0;
844 int numAutoWithoutStretch = 0;
845
846 for ( int i = 0; i < n; i++ ) {
847 a[i].init();
848 QSplitterLayoutStruct *s = d->list.at( i );
849 if ( s->wid->isHidden() || isCollapsed(s->wid) ) {
850 a[i].maximumSize = 0;
851 } else if ( s->isHandle ) {
852 a[i].sizeHint = a[i].minimumSize = a[i].maximumSize = s->sizer;
853 a[i].empty = FALSE;
854 } else {
855 int mode = s->resizeMode;
856 int stretch = 1;
857
858 if ( mode == DefaultResizeMode ) {
859 QSizePolicy p = s->wid->sizePolicy();
860 int sizePolicyStretch =
861 pick( QSize(p.horStretch(), p.verStretch()) );
862 if ( sizePolicyStretch > 0 ) {
863 mode = Stretch;
864 stretch = sizePolicyStretch;
865 numAutoWithStretch++;
866 } else {
867 /*
868 Do things differently on the second pass,
869 if there's one. A second pass is necessary
870 if it was found out during the first pass
871 that all DefaultResizeMode items are
872 KeepSize items. In that case, we make them
873 all Stretch items instead, for a more Qt
874 3.0-compatible behavior.
875 */
876 mode = ( pass == 0 ) ? KeepSize : Stretch;
877 numAutoWithoutStretch++;
878 }
879 }
880
881 a[i].minimumSize = pick( qSmartMinSize(s->wid) );
882 a[i].maximumSize = pick( s->wid->maximumSize() );
883 a[i].empty = FALSE;
884
885 if ( mode == Stretch ) {
886 if ( s->getSizer(orient) > 1 )
887 stretch *= s->getSizer( orient );
888 // QMIN(): ad hoc work-around for layout engine limitation
889 a[i].stretch = QMIN( stretch, 8192 );
890 a[i].sizeHint = a[i].minimumSize;
891 } else if ( mode == KeepSize ) {
892 a[i].sizeHint = s->getSizer( orient );
893 } else { // mode == FollowSizeHint
894 a[i].sizeHint = pick( s->wid->sizeHint() );
895 }
896 }
897 }
898
899 // a second pass would yield the same results
900 if ( numAutoWithStretch > 0 || numAutoWithoutStretch == 0 )
901 break;
902 }
903
904 qGeomCalc( a, 0, n, pick( r.topLeft() ), pick( r.size() ), 0 );
905
906 for ( int i = 0; i < n; i++ ) {
907 QSplitterLayoutStruct *s = d->list.at(i);
908 setGeo( s->wid, a[i].pos, a[i].size, FALSE );
909 }
910}
911
912void QSplitter::recalc( bool update )
913{
914 int fi = 2 * frameWidth();
915 int maxl = fi;
916 int minl = fi;
917 int maxt = QWIDGETSIZE_MAX;
918 int mint = fi;
919 int n = d->list.count();
920 bool first = TRUE;
921
922 /*
923 Splitter handles before the first visible widget or right
924 before a hidden widget must be hidden.
925 */
926 for ( int i = 0; i < n; i++ ) {
927 QSplitterLayoutStruct *s = d->list.at( i );
928 if ( !s->isHandle ) {
929 QSplitterLayoutStruct *p = 0;
930 if ( i > 0 )
931 p = d->list.at( i - 1 );
932
933 // may trigger new recalc
934 if ( p && p->isHandle )
935 p->wid->setHidden( first || s->wid->isHidden() );
936
937 if ( !s->wid->isHidden() )
938 first = FALSE;
939 }
940 }
941
942 bool empty = TRUE;
943 for ( int j = 0; j < n; j++ ) {
944 QSplitterLayoutStruct *s = d->list.at( j );
945 if ( !s->wid->isHidden() ) {
946 empty = FALSE;
947 if ( s->isHandle ) {
948 minl += s->getSizer( orient );
949 maxl += s->getSizer( orient );
950 } else {
951 QSize minS = qSmartMinSize( s->wid );
952 minl += pick( minS );
953 maxl += pick( s->wid->maximumSize() );
954 mint = QMAX( mint, trans(minS) );
955 int tm = trans( s->wid->maximumSize() );
956 if ( tm > 0 )
957 maxt = QMIN( maxt, tm );
958 }
959 }
960 }
961 if ( empty ) {
962 if ( ::qt_cast<QSplitter*>(parentWidget()) ) {
963 // nested splitters; be nice
964 maxl = maxt = 0;
965 } else {
966 // QSplitter with no children yet
967 maxl = QWIDGETSIZE_MAX;
968 }
969 } else {
970 maxl = QMIN( maxl, QWIDGETSIZE_MAX );
971 }
972 if ( maxt < mint )
973 maxt = mint;
974
975 if ( orient == Horizontal ) {
976 setMaximumSize( maxl, maxt );
977 setMinimumSize( minl, mint );
978 } else {
979 setMaximumSize( maxt, maxl );
980 setMinimumSize( mint, minl );
981 }
982 if ( update )
983 doResize();
984 else
985 d->firstShow = TRUE;
986}
987
988/*!
989 \enum QSplitter::ResizeMode
990
991 This enum type describes how QSplitter will resize each of its
992 child widgets.
993
994 \value Auto The widget will be resized according to the stretch
995 factors set in its sizePolicy().
996
997 \value Stretch The widget will be resized when the splitter
998 itself is resized.
999
1000 \value KeepSize QSplitter will try to keep the widget's size
1001 unchanged.
1002
1003 \value FollowSizeHint QSplitter will resize the widget when the
1004 widget's size hint changes.
1005*/
1006
1007/*!
1008 Sets resize mode of widget \a w to \a mode. (The default is \c
1009 Auto.)
1010*/
1011
1012void QSplitter::setResizeMode( QWidget *w, ResizeMode mode )
1013{
1014 findWidget( w )->resizeMode = mode;
1015}
1016
1017
1018/*!
1019 \property QSplitter::opaqueResize
1020 \brief whether resizing is opaque
1021
1022 Opaque resizing is off by default.
1023*/
1024
1025bool QSplitter::opaqueResize() const
1026{
1027 return d->opaque;
1028}
1029
1030
1031void QSplitter::setOpaqueResize( bool on )
1032{
1033 d->opaque = on;
1034}
1035
1036
1037/*!
1038 Moves widget \a w to the leftmost/top position.
1039*/
1040
1041void QSplitter::moveToFirst( QWidget *w )
1042{
1043 processChildEvents();
1044 bool found = FALSE;
1045 QSplitterLayoutStruct *s = d->list.first();
1046 while ( s ) {
1047 if ( s->wid == w ) {
1048 found = TRUE;
1049 QSplitterLayoutStruct *p = d->list.prev();
1050 if ( p ) { // not already at first place
1051 d->list.take(); // take p
1052 d->list.take(); // take s
1053 d->list.prepend( p );
1054 d->list.prepend( s );
1055 }
1056 break;
1057 }
1058 s = d->list.next();
1059 }
1060 if ( !found )
1061 addWidget( w, TRUE );
1062 recalcId();
1063}
1064
1065
1066/*!
1067 Moves widget \a w to the rightmost/bottom position.
1068*/
1069
1070void QSplitter::moveToLast( QWidget *w )
1071{
1072 processChildEvents();
1073 bool found = FALSE;
1074 QSplitterLayoutStruct *s = d->list.first();
1075 while ( s ) {
1076 if ( s->wid == w ) {
1077 found = TRUE;
1078 d->list.take(); // take s
1079 QSplitterLayoutStruct *p = d->list.current();
1080 if ( p ) { // the splitter handle after s
1081 d->list.take(); // take p
1082 d->list.append( p );
1083 }
1084 d->list.append( s );
1085 break;
1086 }
1087 s = d->list.next();
1088 }
1089 if ( !found )
1090 addWidget( w );
1091 recalcId();
1092}
1093
1094
1095void QSplitter::recalcId()
1096{
1097 int n = d->list.count();
1098 for ( int i = 0; i < n; i++ ) {
1099 QSplitterLayoutStruct *s = d->list.at( i );
1100 if ( s->isHandle )
1101 ((QSplitterHandle*)s->wid)->setId( i );
1102 }
1103}
1104
1105
1106/*!
1107 \reimp
1108*/
1109QSize QSplitter::sizeHint() const
1110{
1111 constPolish();
1112 int l = 0;
1113 int t = 0;
1114 if ( children() ) {
1115 const QObjectList * c = children();
1116 QObjectListIt it( *c );
1117 QObject * o;
1118
1119 while( (o = it.current()) != 0 ) {
1120 ++it;
1121 if ( o->isWidgetType() && !((QWidget*)o)->isHidden() ) {
1122 QSize s = ((QWidget*)o)->sizeHint();
1123 if ( s.isValid() ) {
1124 l += pick( s );
1125 t = QMAX( t, trans( s ) );
1126 }
1127 }
1128 }
1129 }
1130 return orientation() == Horizontal ? QSize( l, t ) : QSize( t, l );
1131}
1132
1133
1134/*!
1135 \reimp
1136*/
1137
1138QSize QSplitter::minimumSizeHint() const
1139{
1140 constPolish();
1141 int l = 0;
1142 int t = 0;
1143 if ( children() ) {
1144 const QObjectList * c = children();
1145 QObjectListIt it( *c );
1146 QObject * o;
1147
1148 while ( (o = it.current()) != 0 ) {
1149 ++it;
1150 if ( o->isWidgetType() && !((QWidget*)o)->isHidden() ) {
1151 QSize s = qSmartMinSize( (QWidget*)o );
1152 if ( s.isValid() ) {
1153 l += pick( s );
1154 t = QMAX( t, trans( s ) );
1155 }
1156 }
1157 }
1158 }
1159 return orientation() == Horizontal ? QSize( l, t ) : QSize( t, l );
1160}
1161
1162
1163void QSplitter::storeSizes()
1164{
1165 QSplitterLayoutStruct *s = d->list.first();
1166 while ( s ) {
1167 if ( !s->isHandle )
1168 s->sizer = pick( s->wid->size() );
1169 s = d->list.next();
1170 }
1171}
1172
1173
1174void QSplitter::addContribution( int id, int *min, int *max,
1175 bool mayCollapse )
1176{
1177 QSplitterLayoutStruct *s = d->list.at( id );
1178 if ( !s->wid->isHidden() ) {
1179 if ( s->isHandle ) {
1180 *min += s->getSizer( orient );
1181 *max += s->getSizer( orient );
1182 } else {
1183 if ( mayCollapse || !isCollapsed(s->wid) )
1184 *min += pick( qSmartMinSize(s->wid) );
1185 *max += pick( s->wid->maximumSize() );
1186 }
1187 }
1188}
1189
1190
1191/*!
1192 Returns a list of the size parameters of all the widgets in this
1193 splitter.
1194
1195 If the splitter's orientation is horizontal, the list is a list of
1196 widget widths; if the orientation is vertical, the list is a list
1197 of widget heights.
1198
1199 Giving the values to another splitter's setSizes() function will
1200 produce a splitter with the same layout as this one.
1201
1202 Note that if you want to iterate over the list, you should iterate
1203 over a copy, e.g.
1204 \code
1205 QValueList<int> list = mySplitter.sizes();
1206 QValueList<int>::Iterator it = list.begin();
1207 while( it != list.end() ) {
1208 myProcessing( *it );
1209 ++it;
1210 }
1211 \endcode
1212
1213 \sa setSizes()
1214*/
1215
1216QValueList<int> QSplitter::sizes() const
1217{
1218 if ( !testWState(WState_Polished) )
1219 constPolish();
1220
1221 QValueList<int> list;
1222 QSplitterLayoutStruct *s = d->list.first();
1223 while ( s ) {
1224 if ( !s->isHandle )
1225 list.append( isCollapsed(s->wid) ? 0 : pick(s->wid->size()));
1226 s = d->list.next();
1227 }
1228 return list;
1229}
1230
1231/*!
1232 Sets the size parameters to the values given in the \a list. If
1233 the splitter is horizontal, the values set the widths of each
1234 widget going from left to right. If the splitter is vertical, the
1235 values set the heights of each widget going from top to bottom.
1236 Extra values in the \a list are ignored.
1237
1238 If \a list contains too few values, the result is undefined but
1239 the program will still be well-behaved.
1240
1241 Note that the values in \a list should be the height/width that
1242 the widgets should be resized to.
1243
1244 \sa sizes()
1245*/
1246
1247void QSplitter::setSizes( QValueList<int> list )
1248{
1249 processChildEvents();
1250 QValueList<int>::Iterator it = list.begin();
1251 QSplitterLayoutStruct *s = d->list.first();
1252 while ( s && it != list.end() ) {
1253 if ( !s->isHandle ) {
1254 s->sizer = QMAX( *it, 0 );
1255 int smartMinSize = pick( qSmartMinSize(s->wid) );
1256 // Make sure that we reset the collapsed state.
1257 if ( s->sizer == 0 ) {
1258 if ( collapsible(s) && smartMinSize > 0 ) {
1259 s->wid->move( -1, -1 );
1260 } else {
1261 s->sizer = smartMinSize;
1262 s->wid->move( 0, 0 );
1263 }
1264 } else {
1265 if ( s->sizer < smartMinSize )
1266 s->sizer = smartMinSize;
1267 s->wid->move( 0, 0 );
1268 }
1269 ++it;
1270 }
1271 s = d->list.next();
1272 }
1273 doResize();
1274}
1275
1276/*!
1277 \property QSplitter::handleWidth
1278 \brief the width of the splitter handle
1279*/
1280
1281int QSplitter::handleWidth() const
1282{
1283 if ( d->handleWidth > 0 ) {
1284 return d->handleWidth;
1285 } else {
1286 return style().pixelMetric( QStyle::PM_SplitterWidth, this );
1287 }
1288}
1289
1290void QSplitter::setHandleWidth( int width )
1291{
1292 d->handleWidth = width;
1293 updateHandles();
1294}
1295
1296/*!
1297 Processes all posted child events, ensuring that the internal state of
1298 the splitter is kept consistent.
1299*/
1300
1301void QSplitter::processChildEvents()
1302{
1303 QApplication::sendPostedEvents( this, QEvent::ChildInserted );
1304}
1305
1306/*!
1307 \reimp
1308*/
1309
1310void QSplitter::styleChange( QStyle& old )
1311{
1312 updateHandles();
1313 QFrame::styleChange( old );
1314}
1315
1316void QSplitter::updateHandles()
1317{
1318 int hw = handleWidth();
1319 QSplitterLayoutStruct *s = d->list.first();
1320 while ( s ) {
1321 if ( s->isHandle )
1322 s->sizer = hw;
1323 s = d->list.next();
1324 }
1325 recalc( isVisible() );
1326}
1327
1328#ifndef QT_NO_TEXTSTREAM
1329/*!
1330 \relates QSplitter
1331
1332 Writes the sizes and the hidden state of the widgets in the
1333 splitter \a splitter to the text stream \a ts.
1334
1335 \sa operator>>(), sizes(), QWidget::isHidden()
1336*/
1337
1338QTextStream& operator<<( QTextStream& ts, const QSplitter& splitter )
1339{
1340 QSplitterLayoutStruct *s = splitter.d->list.first();
1341 bool first = TRUE;
1342 ts << "[";
1343
1344 while ( s != 0 ) {
1345 if ( !s->isHandle ) {
1346 if ( !first )
1347 ts << ",";
1348
1349 if ( s->wid->isHidden() ) {
1350 ts << "H";
1351 } else if ( isCollapsed(s->wid) ) {
1352 ts << 0;
1353 } else {
1354 ts << s->getSizer( splitter.orientation() );
1355 }
1356 first = FALSE;
1357 }
1358 s = splitter.d->list.next();
1359 }
1360 ts << "]" << endl;
1361 return ts;
1362}
1363
1364/*!
1365 \relates QSplitter
1366
1367 Reads the sizes and the hidden state of the widgets in the
1368 splitter \a splitter from the text stream \a ts. The sizes must
1369 have been previously written by the operator<<() function.
1370
1371 \sa operator<<(), setSizes(), QWidget::hide()
1372*/
1373
1374QTextStream& operator>>( QTextStream& ts, QSplitter& splitter )
1375{
1376#undef SKIP_SPACES
1377#define SKIP_SPACES() \
1378 while ( line[i].isSpace() ) \
1379 i++
1380
1381 splitter.processChildEvents();
1382 QSplitterLayoutStruct *s = splitter.d->list.first();
1383 QString line = ts.readLine();
1384 int i = 0;
1385
1386 SKIP_SPACES();
1387 if ( line[i] == '[' ) {
1388 i++;
1389 SKIP_SPACES();
1390 while ( line[i] != ']' ) {
1391 while ( s != 0 && s->isHandle )
1392 s = splitter.d->list.next();
1393 if ( s == 0 )
1394 break;
1395
1396 if ( line[i].upper() == 'H' ) {
1397 s->wid->hide();
1398 i++;
1399 } else {
1400 s->wid->show();
1401 int dim = 0;
1402 while ( line[i].digitValue() >= 0 ) {
1403 dim *= 10;
1404 dim += line[i].digitValue();
1405 i++;
1406 }
1407 s->sizer = dim;
1408 if ( dim == 0 )
1409 splitter.setGeo( s->wid, 0, 0, FALSE );
1410 }
1411 SKIP_SPACES();
1412 if ( line[i] == ',' ) {
1413 i++;
1414 } else {
1415 break;
1416 }
1417 SKIP_SPACES();
1418 s = splitter.d->list.next();
1419 }
1420 }
1421 splitter.doResize();
1422 return ts;
1423}
1424#endif
1425
1426#endif
Note: See TracBrowser for help on using the repository browser.