source: trunk/src/widgets/qsplitter.cpp@ 168

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

Fixed a bunch of compiler warnings

  • Property svn:keywords set to Id
File size: 35.6 KB
Line 
1/****************************************************************************
2** $Id: qsplitter.cpp 20 2005-11-17 18:00:27Z 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( NoPen );
510 paint.setBrush( gray );
511 paint.setRasterOp( XorROP );
512 QRect r = contentsRect();
513 const int rBord = 3; // customizable?
514 int hw = handleWidth();
515
516 int p1 = 0, p2 = 0, op1 = 0, op2 = 0, tmp = 0;
517 if ( p >= 0 ) {
518 p1 = p + hw / 2 - rBord;
519 p2 = p1 + 2 * rBord - 1;
520 }
521 if ( opaqueOldPos >= 0 ) {
522 op1 = opaqueOldPos + hw / 2 - rBord;
523 op2 = op1 + 2 * rBord - 1;
524 // exclude overlapping area to avoid flicker
525 if ( p >= 0 ) {
526 if ( p2 >= op1 && p2 <= op2 ) {
527 tmp = op1;
528 op1 = p2 + 1;
529 p2 = tmp - 1;
530 } else if ( p1 >= op1 && p1 <= op2 ) {
531 tmp = op2;
532 op2 = p1 - 1;
533 p1 = tmp + 1;
534 }
535 }
536 }
537 if ( orient == Horizontal ) {
538 if ( opaqueOldPos >= 0 )
539 paint.drawRect( op1, r.y(), op2 - op1 + 1, r.height() );
540 if ( p >= 0 )
541 paint.drawRect( p1, r.y(), p2 - p1 + 1, r.height() );
542 } else {
543 if ( opaqueOldPos >= 0 )
544 paint.drawRect( r.x(), op1, r.width(), op2 - op1 + 1 );
545 if ( p >= 0 )
546 paint.drawRect( r.x(), p1, r.width(), p2 - p1 + 1 );
547 }
548 opaqueOldPos = p;
549}
550
551
552/*!
553 \reimp
554*/
555
556bool QSplitter::event( QEvent *e )
557{
558 switch ( e->type() ) {
559 case QEvent::Show:
560 if ( !d->firstShow )
561 break;
562 d->firstShow = FALSE;
563 // fall through
564 case QEvent::LayoutHint:
565 recalc( isVisible() );
566 break;
567 default:
568 ;
569 }
570 return QWidget::event( e );
571}
572
573
574/*!
575 \obsolete
576
577 Draws the splitter handle in the rectangle described by \a x, \a y,
578 \a w, \a h using painter \a p.
579 \sa QStyle::drawPrimitive()
580*/
581
582// ### Remove this in 4.0
583
584void QSplitter::drawSplitter( QPainter *p,
585 QCOORD x, QCOORD y, QCOORD w, QCOORD h )
586{
587 style().drawPrimitive(QStyle::PE_Splitter, p, QRect(x, y, w, h), colorGroup(),
588 (orientation() == Horizontal ?
589 QStyle::Style_Horizontal : 0));
590}
591
592
593/*!
594 Returns the ID of the widget to the right of or below the widget
595 \a w, or 0 if there is no such widget (i.e. it is either not in
596 this QSplitter or \a w is at the end).
597*/
598
599int QSplitter::idAfter( QWidget* w ) const
600{
601 QSplitterLayoutStruct *s = d->list.first();
602 bool seen_w = FALSE;
603 while ( s ) {
604 if ( s->isHandle && seen_w )
605 return d->list.at();
606 if ( !s->isHandle && s->wid == w )
607 seen_w = TRUE;
608 s = d->list.next();
609 }
610 return 0;
611}
612
613
614/*!
615 Moves the left/top edge of the splitter handle with ID \a id as
616 close as possible to position \a p, which is the distance from the
617 left (or top) edge of the widget.
618
619 For Arabic, Hebrew and other right-to-left languages the layout is
620 reversed. \a p is then the distance from the right (or top) edge
621 of the widget.
622
623 \sa idAfter()
624*/
625void QSplitter::moveSplitter( QCOORD p, int id )
626{
627 QSplitterLayoutStruct *s = d->list.at( id );
628 int farMin;
629 int min;
630 int max;
631 int farMax;
632
633 p = adjustPos( p, id, &farMin, &min, &max, &farMax );
634 int oldP = pick( s->wid->pos() );
635
636 if ( QApplication::reverseLayout() && orient == Horizontal ) {
637 int q = p + s->wid->width();
638 doMove( FALSE, q, id - 1, -1, (q > oldP), (p > max) );
639 doMove( TRUE, q, id, -1, (q > oldP), (p < min) );
640 } else {
641 doMove( FALSE, p, id, +1, (p < oldP), (p > max) );
642 doMove( TRUE, p, id - 1, +1, (p < oldP), (p < min) );
643 }
644 storeSizes();
645}
646
647
648void QSplitter::setGeo( QWidget *w, int p, int s, bool splitterMoved )
649{
650 QRect r;
651 if ( orient == Horizontal ) {
652 if ( QApplication::reverseLayout() && orient == Horizontal
653 && !splitterMoved )
654 p = contentsRect().width() - p - s;
655 r.setRect( p, contentsRect().y(), s, contentsRect().height() );
656 } else {
657 r.setRect( contentsRect().x(), p, contentsRect().width(), s );
658 }
659
660 /*
661 Hide the child widget, but without calling hide() so that the
662 splitter handle is still shown.
663 */
664 if ( !w->isHidden() && s <= 0 && pick(qSmartMinSize(w)) > 0 )
665 r.moveTopLeft( toggle(w, r.topLeft()) );
666 w->setGeometry( r );
667}
668
669
670void QSplitter::doMove( bool backwards, int pos, int id, int delta, bool upLeft,
671 bool mayCollapse )
672{
673 if ( id < 0 || id >= (int) d->list.count() )
674 return;
675
676 QSplitterLayoutStruct *s = d->list.at( id );
677 QWidget *w = s->wid;
678
679 int nextId = backwards ? id - delta : id + delta;
680
681 if ( w->isHidden() ) {
682 doMove( backwards, pos, nextId, delta, upLeft, TRUE );
683 } else {
684 if ( s->isHandle ) {
685 int dd = s->getSizer( orient );
686 int nextPos = backwards ? pos - dd : pos + dd;
687 int left = backwards ? pos - dd : pos;
688 setGeo( w, left, dd, TRUE );
689 doMove( backwards, nextPos, nextId, delta, upLeft, mayCollapse );
690 } else {
691 int dd = backwards ? pos - pick( topLeft(w) )
692 : pick( bottomRight(w) ) - pos + 1;
693 if ( dd > 0 || (!isCollapsed(w) && !mayCollapse) ) {
694 dd = QMAX( pick(qSmartMinSize(w)),
695 QMIN(dd, pick(w->maximumSize())) );
696 } else {
697 dd = 0;
698 }
699 setGeo( w, backwards ? pos - dd : pos, dd, TRUE );
700 doMove( backwards, backwards ? pos - dd : pos + dd, nextId, delta,
701 upLeft, TRUE );
702 }
703 }
704}
705
706int QSplitter::findWidgetJustBeforeOrJustAfter( int id, int delta, int &collapsibleSize )
707{
708 id += delta;
709 do {
710 QWidget *w = d->list.at( id )->wid;
711 if ( !w->isHidden() ) {
712 if ( collapsible(d->list.at(id)) )
713 collapsibleSize = pick( qSmartMinSize(w) );
714 return id;
715 }
716 id += 2 * delta; // go to previous (or next) widget, skip the handle
717 } while ( id >= 0 && id < (int)d->list.count() );
718
719 return -1;
720}
721
722void QSplitter::getRange( int id, int *farMin, int *min, int *max, int *farMax )
723{
724 int n = d->list.count();
725 if ( id <= 0 || id >= n - 1 )
726 return;
727
728 int collapsibleSizeBefore = 0;
729 int idJustBefore = findWidgetJustBeforeOrJustAfter( id, -1, collapsibleSizeBefore );
730
731 int collapsibleSizeAfter = 0;
732 int idJustAfter = findWidgetJustBeforeOrJustAfter( id, +1, collapsibleSizeAfter );
733
734 int minBefore = 0;
735 int minAfter = 0;
736 int maxBefore = 0;
737 int maxAfter = 0;
738 int i;
739
740 for ( i = 0; i < id; i++ )
741 addContribution( i, &minBefore, &maxBefore, i == idJustBefore );
742 for ( i = id; i < n; i++ )
743 addContribution( i, &minAfter, &maxAfter, i == idJustAfter );
744
745 QRect r = contentsRect();
746 int farMinVal;
747 int minVal;
748 int maxVal;
749 int farMaxVal;
750
751 int smartMinBefore = QMAX( minBefore, pick(r.size()) - maxAfter );
752 int smartMaxBefore = QMIN( maxBefore, pick(r.size()) - minAfter );
753
754 if ( orient == Vertical || !QApplication::reverseLayout() ) {
755 minVal = pick( r.topLeft() ) + smartMinBefore;
756 maxVal = pick( r.topLeft() ) + smartMaxBefore;
757
758 farMinVal = minVal;
759 if ( minBefore - collapsibleSizeBefore >= pick(r.size()) - maxAfter )
760 farMinVal -= collapsibleSizeBefore;
761 farMaxVal = maxVal;
762 if ( pick(r.size()) - (minAfter - collapsibleSizeAfter) <= maxBefore )
763 farMaxVal += collapsibleSizeAfter;
764 } else {
765 int hw = handleWidth();
766 minVal = r.width() - smartMaxBefore - hw;
767 maxVal = r.width() - smartMinBefore - hw;
768
769 farMinVal = minVal;
770 if ( pick(r.size()) - (minAfter - collapsibleSizeAfter) <= maxBefore )
771 farMinVal -= collapsibleSizeAfter;
772 farMaxVal = maxVal;
773 if ( minBefore - collapsibleSizeBefore >= pick(r.size()) - maxAfter )
774 farMaxVal += collapsibleSizeBefore;
775 }
776
777 if ( farMin )
778 *farMin = farMinVal;
779 if ( min )
780 *min = minVal;
781 if ( max )
782 *max = maxVal;
783 if ( farMax )
784 *farMax = farMaxVal;
785}
786
787/*!
788 Returns the valid range of the splitter with ID \a id in \a *min
789 and \a *max if \a min and \a max are not 0.
790
791 \sa idAfter()
792*/
793
794void QSplitter::getRange( int id, int *min, int *max )
795{
796 getRange( id, min, 0, 0, max );
797}
798
799
800/*!
801 Returns the closest legal position to \a pos of the widget with ID
802 \a id.
803
804 \sa idAfter()
805*/
806
807int QSplitter::adjustPos( int pos, int id )
808{
809 int x, i, n, u;
810 return adjustPos( pos, id, &u, &n, &i, &x );
811}
812
813int QSplitter::adjustPos( int pos, int id, int *farMin, int *min, int *max,
814 int *farMax )
815{
816 const int Threshold = 40;
817
818 getRange( id, farMin, min, max, farMax );
819
820 if ( pos >= *min ) {
821 if ( pos <= *max ) {
822 return pos;
823 } else {
824 int delta = pos - *max;
825 int width = *farMax - *max;
826
827 if ( delta > width / 2 && delta >= QMIN(Threshold, width) ) {
828 return *farMax;
829 } else {
830 return *max;
831 }
832 }
833 } else {
834 int delta = *min - pos;
835 int width = *min - *farMin;
836
837 if ( delta > width / 2 && delta >= QMIN(Threshold, width) ) {
838 return *farMin;
839 } else {
840 return *min;
841 }
842 }
843}
844
845bool QSplitter::collapsible( QSplitterLayoutStruct *s )
846{
847 if (pick(qSmartMinSize(s->wid)) == 1)
848 return false;
849 if ( s->collapsible != Default ) {
850 return (bool) s->collapsible;
851 } else {
852 return d->childrenCollapsible;
853 }
854}
855
856void QSplitter::doResize()
857{
858 QRect r = contentsRect();
859 int n = d->list.count();
860 QMemArray<QLayoutStruct> a( n );
861
862 for ( int pass = 0; pass < 2; pass++ ) {
863 int numAutoWithStretch = 0;
864 int numAutoWithoutStretch = 0;
865
866 for ( int i = 0; i < n; i++ ) {
867 a[i].init();
868 QSplitterLayoutStruct *s = d->list.at( i );
869 if ( s->wid->isHidden() || isCollapsed(s->wid) ) {
870 a[i].maximumSize = 0;
871 } else if ( s->isHandle ) {
872 a[i].sizeHint = a[i].minimumSize = a[i].maximumSize = s->sizer;
873 a[i].empty = FALSE;
874 } else {
875 int mode = s->resizeMode;
876 int stretch = 1;
877
878 if ( mode == DefaultResizeMode ) {
879 QSizePolicy p = s->wid->sizePolicy();
880 int sizePolicyStretch =
881 pick( QSize(p.horStretch(), p.verStretch()) );
882 if ( sizePolicyStretch > 0 ) {
883 mode = Stretch;
884 stretch = sizePolicyStretch;
885 numAutoWithStretch++;
886 } else {
887 /*
888 Do things differently on the second pass,
889 if there's one. A second pass is necessary
890 if it was found out during the first pass
891 that all DefaultResizeMode items are
892 KeepSize items. In that case, we make them
893 all Stretch items instead, for a more Qt
894 3.0-compatible behavior.
895 */
896 mode = ( pass == 0 ) ? KeepSize : Stretch;
897 numAutoWithoutStretch++;
898 }
899 }
900
901 a[i].minimumSize = pick( qSmartMinSize(s->wid) );
902 a[i].maximumSize = pick( s->wid->maximumSize() );
903 a[i].empty = FALSE;
904
905 if ( mode == Stretch ) {
906 if ( s->getSizer(orient) > 1 )
907 stretch *= s->getSizer( orient );
908 // QMIN(): ad hoc work-around for layout engine limitation
909 a[i].stretch = QMIN( stretch, 8192 );
910 a[i].sizeHint = a[i].minimumSize;
911 } else if ( mode == KeepSize ) {
912 a[i].sizeHint = s->getSizer( orient );
913 } else { // mode == FollowSizeHint
914 a[i].sizeHint = pick( s->wid->sizeHint() );
915 }
916 }
917 }
918
919 // a second pass would yield the same results
920 if ( numAutoWithStretch > 0 || numAutoWithoutStretch == 0 )
921 break;
922 }
923
924 qGeomCalc( a, 0, n, pick( r.topLeft() ), pick( r.size() ), 0 );
925
926 for ( int i = 0; i < n; i++ ) {
927 QSplitterLayoutStruct *s = d->list.at(i);
928 setGeo( s->wid, a[i].pos, a[i].size, FALSE );
929 }
930}
931
932void QSplitter::recalc( bool update )
933{
934 int fi = 2 * frameWidth();
935 int maxl = fi;
936 int minl = fi;
937 int maxt = QWIDGETSIZE_MAX;
938 int mint = fi;
939 int n = d->list.count();
940 bool first = TRUE;
941
942 /*
943 Splitter handles before the first visible widget or right
944 before a hidden widget must be hidden.
945 */
946 for ( int i = 0; i < n; i++ ) {
947 QSplitterLayoutStruct *s = d->list.at( i );
948 if ( !s->isHandle ) {
949 QSplitterLayoutStruct *p = 0;
950 if ( i > 0 )
951 p = d->list.at( i - 1 );
952
953 // may trigger new recalc
954 if ( p && p->isHandle )
955 p->wid->setHidden( first || s->wid->isHidden() );
956
957 if ( !s->wid->isHidden() )
958 first = FALSE;
959 }
960 }
961
962 bool empty = TRUE;
963 for ( int j = 0; j < n; j++ ) {
964 QSplitterLayoutStruct *s = d->list.at( j );
965 if ( !s->wid->isHidden() ) {
966 empty = FALSE;
967 if ( s->isHandle ) {
968 minl += s->getSizer( orient );
969 maxl += s->getSizer( orient );
970 } else {
971 QSize minS = qSmartMinSize( s->wid );
972 minl += pick( minS );
973 maxl += pick( s->wid->maximumSize() );
974 mint = QMAX( mint, trans(minS) );
975 int tm = trans( s->wid->maximumSize() );
976 if ( tm > 0 )
977 maxt = QMIN( maxt, tm );
978 }
979 }
980 }
981 if ( empty ) {
982 if ( ::qt_cast<QSplitter*>(parentWidget()) ) {
983 // nested splitters; be nice
984 maxl = maxt = 0;
985 } else {
986 // QSplitter with no children yet
987 maxl = QWIDGETSIZE_MAX;
988 }
989 } else {
990 maxl = QMIN( maxl, QWIDGETSIZE_MAX );
991 }
992 if ( maxt < mint )
993 maxt = mint;
994
995 if ( orient == Horizontal ) {
996 setMaximumSize( maxl, maxt );
997 setMinimumSize( minl, mint );
998 } else {
999 setMaximumSize( maxt, maxl );
1000 setMinimumSize( mint, minl );
1001 }
1002 if ( update )
1003 doResize();
1004 else
1005 d->firstShow = TRUE;
1006}
1007
1008/*!
1009 \enum QSplitter::ResizeMode
1010
1011 This enum type describes how QSplitter will resize each of its
1012 child widgets.
1013
1014 \value Auto The widget will be resized according to the stretch
1015 factors set in its sizePolicy().
1016
1017 \value Stretch The widget will be resized when the splitter
1018 itself is resized.
1019
1020 \value KeepSize QSplitter will try to keep the widget's size
1021 unchanged.
1022
1023 \value FollowSizeHint QSplitter will resize the widget when the
1024 widget's size hint changes.
1025*/
1026
1027/*!
1028 Sets resize mode of widget \a w to \a mode. (The default is \c
1029 Auto.)
1030*/
1031
1032void QSplitter::setResizeMode( QWidget *w, ResizeMode mode )
1033{
1034 findWidget( w )->resizeMode = mode;
1035}
1036
1037
1038/*!
1039 \property QSplitter::opaqueResize
1040 \brief whether resizing is opaque
1041
1042 Opaque resizing is off by default.
1043*/
1044
1045bool QSplitter::opaqueResize() const
1046{
1047 return d->opaque;
1048}
1049
1050
1051void QSplitter::setOpaqueResize( bool on )
1052{
1053 d->opaque = on;
1054}
1055
1056
1057/*!
1058 Moves widget \a w to the leftmost/top position.
1059*/
1060
1061void QSplitter::moveToFirst( QWidget *w )
1062{
1063 processChildEvents();
1064 bool found = FALSE;
1065 QSplitterLayoutStruct *s = d->list.first();
1066 while ( s ) {
1067 if ( s->wid == w ) {
1068 found = TRUE;
1069 QSplitterLayoutStruct *p = d->list.prev();
1070 if ( p ) { // not already at first place
1071 d->list.take(); // take p
1072 d->list.take(); // take s
1073 d->list.prepend( p );
1074 d->list.prepend( s );
1075 }
1076 break;
1077 }
1078 s = d->list.next();
1079 }
1080 if ( !found )
1081 addWidget( w, TRUE );
1082 recalcId();
1083}
1084
1085
1086/*!
1087 Moves widget \a w to the rightmost/bottom position.
1088*/
1089
1090void QSplitter::moveToLast( QWidget *w )
1091{
1092 processChildEvents();
1093 bool found = FALSE;
1094 QSplitterLayoutStruct *s = d->list.first();
1095 while ( s ) {
1096 if ( s->wid == w ) {
1097 found = TRUE;
1098 d->list.take(); // take s
1099 QSplitterLayoutStruct *p = d->list.current();
1100 if ( p ) { // the splitter handle after s
1101 d->list.take(); // take p
1102 d->list.append( p );
1103 }
1104 d->list.append( s );
1105 break;
1106 }
1107 s = d->list.next();
1108 }
1109 if ( !found )
1110 addWidget( w );
1111 recalcId();
1112}
1113
1114
1115void QSplitter::recalcId()
1116{
1117 int n = d->list.count();
1118 for ( int i = 0; i < n; i++ ) {
1119 QSplitterLayoutStruct *s = d->list.at( i );
1120 if ( s->isHandle )
1121 ((QSplitterHandle*)s->wid)->setId( i );
1122 }
1123}
1124
1125
1126/*!
1127 \reimp
1128*/
1129QSize QSplitter::sizeHint() const
1130{
1131 constPolish();
1132 int l = 0;
1133 int t = 0;
1134 if ( children() ) {
1135 const QObjectList * c = children();
1136 QObjectListIt it( *c );
1137 QObject * o;
1138
1139 while( (o = it.current()) != 0 ) {
1140 ++it;
1141 if ( o->isWidgetType() && !((QWidget*)o)->isHidden() ) {
1142 QSize s = ((QWidget*)o)->sizeHint();
1143 if ( s.isValid() ) {
1144 l += pick( s );
1145 t = QMAX( t, trans( s ) );
1146 }
1147 }
1148 }
1149 }
1150 return orientation() == Horizontal ? QSize( l, t ) : QSize( t, l );
1151}
1152
1153
1154/*!
1155 \reimp
1156*/
1157
1158QSize QSplitter::minimumSizeHint() const
1159{
1160 constPolish();
1161 int l = 0;
1162 int t = 0;
1163 if ( children() ) {
1164 const QObjectList * c = children();
1165 QObjectListIt it( *c );
1166 QObject * o;
1167
1168 while ( (o = it.current()) != 0 ) {
1169 ++it;
1170 if ( o->isWidgetType() && !((QWidget*)o)->isHidden() ) {
1171 QSize s = qSmartMinSize( (QWidget*)o );
1172 if ( s.isValid() ) {
1173 l += pick( s );
1174 t = QMAX( t, trans( s ) );
1175 }
1176 }
1177 }
1178 }
1179 return orientation() == Horizontal ? QSize( l, t ) : QSize( t, l );
1180}
1181
1182
1183void QSplitter::storeSizes()
1184{
1185 QSplitterLayoutStruct *s = d->list.first();
1186 while ( s ) {
1187 if ( !s->isHandle )
1188 s->sizer = pick( s->wid->size() );
1189 s = d->list.next();
1190 }
1191}
1192
1193
1194void QSplitter::addContribution( int id, int *min, int *max,
1195 bool mayCollapse )
1196{
1197 QSplitterLayoutStruct *s = d->list.at( id );
1198 if ( !s->wid->isHidden() ) {
1199 if ( s->isHandle ) {
1200 *min += s->getSizer( orient );
1201 *max += s->getSizer( orient );
1202 } else {
1203 if ( mayCollapse || !isCollapsed(s->wid) )
1204 *min += pick( qSmartMinSize(s->wid) );
1205 *max += pick( s->wid->maximumSize() );
1206 }
1207 }
1208}
1209
1210
1211/*!
1212 Returns a list of the size parameters of all the widgets in this
1213 splitter.
1214
1215 If the splitter's orientation is horizontal, the list is a list of
1216 widget widths; if the orientation is vertical, the list is a list
1217 of widget heights.
1218
1219 Giving the values to another splitter's setSizes() function will
1220 produce a splitter with the same layout as this one.
1221
1222 Note that if you want to iterate over the list, you should iterate
1223 over a copy, e.g.
1224 \code
1225 QValueList<int> list = mySplitter.sizes();
1226 QValueList<int>::Iterator it = list.begin();
1227 while( it != list.end() ) {
1228 myProcessing( *it );
1229 ++it;
1230 }
1231 \endcode
1232
1233 \sa setSizes()
1234*/
1235
1236QValueList<int> QSplitter::sizes() const
1237{
1238 if ( !testWState(WState_Polished) )
1239 constPolish();
1240
1241 QValueList<int> list;
1242 QSplitterLayoutStruct *s = d->list.first();
1243 while ( s ) {
1244 if ( !s->isHandle )
1245 list.append( isCollapsed(s->wid) ? 0 : pick(s->wid->size()));
1246 s = d->list.next();
1247 }
1248 return list;
1249}
1250
1251/*!
1252 Sets the size parameters to the values given in the \a list. If
1253 the splitter is horizontal, the values set the widths of each
1254 widget going from left to right. If the splitter is vertical, the
1255 values set the heights of each widget going from top to bottom.
1256 Extra values in the \a list are ignored.
1257
1258 If \a list contains too few values, the result is undefined but
1259 the program will still be well-behaved.
1260
1261 Note that the values in \a list should be the height/width that
1262 the widgets should be resized to.
1263
1264 \sa sizes()
1265*/
1266
1267void QSplitter::setSizes( QValueList<int> list )
1268{
1269 processChildEvents();
1270 QValueList<int>::Iterator it = list.begin();
1271 QSplitterLayoutStruct *s = d->list.first();
1272 while ( s && it != list.end() ) {
1273 if ( !s->isHandle ) {
1274 s->sizer = QMAX( *it, 0 );
1275 int smartMinSize = pick( qSmartMinSize(s->wid) );
1276 // Make sure that we reset the collapsed state.
1277 if ( s->sizer == 0 ) {
1278 if ( collapsible(s) && smartMinSize > 0 ) {
1279 s->wid->move( -1, -1 );
1280 } else {
1281 s->sizer = smartMinSize;
1282 s->wid->move( 0, 0 );
1283 }
1284 } else {
1285 if ( s->sizer < smartMinSize )
1286 s->sizer = smartMinSize;
1287 s->wid->move( 0, 0 );
1288 }
1289 ++it;
1290 }
1291 s = d->list.next();
1292 }
1293 doResize();
1294}
1295
1296/*!
1297 \property QSplitter::handleWidth
1298 \brief the width of the splitter handle
1299*/
1300
1301int QSplitter::handleWidth() const
1302{
1303 if ( d->handleWidth > 0 ) {
1304 return d->handleWidth;
1305 } else {
1306 return style().pixelMetric( QStyle::PM_SplitterWidth, this );
1307 }
1308}
1309
1310void QSplitter::setHandleWidth( int width )
1311{
1312 d->handleWidth = width;
1313 updateHandles();
1314}
1315
1316/*!
1317 Processes all posted child events, ensuring that the internal state of
1318 the splitter is kept consistent.
1319*/
1320
1321void QSplitter::processChildEvents()
1322{
1323 QApplication::sendPostedEvents( this, QEvent::ChildInserted );
1324}
1325
1326/*!
1327 \reimp
1328*/
1329
1330void QSplitter::styleChange( QStyle& old )
1331{
1332 updateHandles();
1333 QFrame::styleChange( old );
1334}
1335
1336void QSplitter::updateHandles()
1337{
1338 int hw = handleWidth();
1339 QSplitterLayoutStruct *s = d->list.first();
1340 while ( s ) {
1341 if ( s->isHandle )
1342 s->sizer = hw;
1343 s = d->list.next();
1344 }
1345 recalc( isVisible() );
1346}
1347
1348#ifndef QT_NO_TEXTSTREAM
1349/*!
1350 \relates QSplitter
1351
1352 Writes the sizes and the hidden state of the widgets in the
1353 splitter \a splitter to the text stream \a ts.
1354
1355 \sa operator>>(), sizes(), QWidget::isHidden()
1356*/
1357
1358QTextStream& operator<<( QTextStream& ts, const QSplitter& splitter )
1359{
1360 QSplitterLayoutStruct *s = splitter.d->list.first();
1361 bool first = TRUE;
1362 ts << "[";
1363
1364 while ( s != 0 ) {
1365 if ( !s->isHandle ) {
1366 if ( !first )
1367 ts << ",";
1368
1369 if ( s->wid->isHidden() ) {
1370 ts << "H";
1371 } else if ( isCollapsed(s->wid) ) {
1372 ts << 0;
1373 } else {
1374 ts << s->getSizer( splitter.orientation() );
1375 }
1376 first = FALSE;
1377 }
1378 s = splitter.d->list.next();
1379 }
1380 ts << "]" << endl;
1381 return ts;
1382}
1383
1384/*!
1385 \relates QSplitter
1386
1387 Reads the sizes and the hidden state of the widgets in the
1388 splitter \a splitter from the text stream \a ts. The sizes must
1389 have been previously written by the operator<<() function.
1390
1391 \sa operator<<(), setSizes(), QWidget::hide()
1392*/
1393
1394QTextStream& operator>>( QTextStream& ts, QSplitter& splitter )
1395{
1396#undef SKIP_SPACES
1397#define SKIP_SPACES() \
1398 while ( line[i].isSpace() ) \
1399 i++
1400
1401 splitter.processChildEvents();
1402 QSplitterLayoutStruct *s = splitter.d->list.first();
1403 QString line = ts.readLine();
1404 int i = 0;
1405
1406 SKIP_SPACES();
1407 if ( line[i] == '[' ) {
1408 i++;
1409 SKIP_SPACES();
1410 while ( line[i] != ']' ) {
1411 while ( s != 0 && s->isHandle )
1412 s = splitter.d->list.next();
1413 if ( s == 0 )
1414 break;
1415
1416 if ( line[i].upper() == 'H' ) {
1417 s->wid->hide();
1418 i++;
1419 } else {
1420 s->wid->show();
1421 int dim = 0;
1422 while ( line[i].digitValue() >= 0 ) {
1423 dim *= 10;
1424 dim += line[i].digitValue();
1425 i++;
1426 }
1427 s->sizer = dim;
1428 if ( dim == 0 )
1429 splitter.setGeo( s->wid, 0, 0, FALSE );
1430 }
1431 SKIP_SPACES();
1432 if ( line[i] == ',' ) {
1433 i++;
1434 } else {
1435 break;
1436 }
1437 SKIP_SPACES();
1438 s = splitter.d->list.next();
1439 }
1440 }
1441 splitter.doResize();
1442 return ts;
1443}
1444#endif
1445
1446#endif
Note: See TracBrowser for help on using the repository browser.