source: trunk/src/gui/widgets/qsplitter.cpp@ 603

Last change on this file since 603 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 50.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qsplitter.h"
43#ifndef QT_NO_SPLITTER
44
45#include "qapplication.h"
46#include "qcursor.h"
47#include "qdrawutil.h"
48#include "qevent.h"
49#include "qlayout.h"
50#include "qlist.h"
51#include "qpainter.h"
52#include "qrubberband.h"
53#include "qstyle.h"
54#include "qstyleoption.h"
55#include "qtextstream.h"
56#include "qvarlengtharray.h"
57#include "qvector.h"
58#include "private/qlayoutengine_p.h"
59#include "private/qsplitter_p.h"
60#include "qtimer.h"
61#include "qdebug.h"
62
63#include <ctype.h>
64
65QT_BEGIN_NAMESPACE
66
67//#define QSPLITTER_DEBUG
68
69/*!
70 \class QSplitterHandle
71 \brief The QSplitterHandle class provides handle functionality of the splitter.
72
73 \ingroup organizers
74
75 QSplitterHandle is typically what people think about when they think about
76 a splitter. It is the handle that is used to resize the widgets.
77
78 A typical developer using QSplitter will never have to worry about
79 QSplitterHandle. It is provided for developers who want splitter handles
80 that provide extra features, such as popup menus.
81
82 The typical way one would create splitter handles is to subclass QSplitter then
83 reimplement QSplitter::createHandle() to instantiate the custom splitter
84 handle. For example, a minimum QSplitter subclass might look like this:
85
86 \snippet doc/src/snippets/splitterhandle/splitter.h 0
87
88 The \l{QSplitter::}{createHandle()} implementation simply constructs a
89 custom splitter handle, called \c Splitter in this example:
90
91 \snippet doc/src/snippets/splitterhandle/splitter.cpp 1
92
93 Information about a given handle can be obtained using functions like
94 orientation() and opaqueResize(), and is retrieved from its parent splitter.
95 Details like these can be used to give custom handles different appearances
96 depending on the splitter's orientation.
97
98 The complexity of a custom handle subclass depends on the tasks that it
99 needs to perform. A simple subclass might only provide a paintEvent()
100 implementation:
101
102 \snippet doc/src/snippets/splitterhandle/splitter.cpp 0
103
104 In this example, a predefined gradient is set up differently depending on
105 the orientation of the handle. QSplitterHandle provides a reasonable
106 size hint for the handle, so the subclass does not need to provide a
107 reimplementation of sizeHint() unless the handle has special size
108 requirements.
109
110 \sa QSplitter
111*/
112
113/*!
114 Creates a QSplitter handle with the given \a orientation and
115 QSplitter \a parent.
116*/
117QSplitterHandle::QSplitterHandle(Qt::Orientation orientation, QSplitter *parent)
118 : QWidget(*new QSplitterHandlePrivate, parent, 0)
119{
120 Q_D(QSplitterHandle);
121 d->s = parent;
122 setOrientation(orientation);
123}
124
125/*!
126 Sets the orientation of the splitter handle to \a orientation.
127 This is usually propogated from the QSplitter.
128
129 \sa QSplitter::setOrientation()
130*/
131void QSplitterHandle::setOrientation(Qt::Orientation orientation)
132{
133 Q_D(QSplitterHandle);
134 d->orient = orientation;
135#ifndef QT_NO_CURSOR
136 setCursor(orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor);
137#endif
138}
139
140/*!
141 Returns the handle's orientation. This is usually propagated from the QSplitter.
142
143 \sa QSplitter::orientation()
144*/
145Qt::Orientation QSplitterHandle::orientation() const
146{
147 Q_D(const QSplitterHandle);
148 return d->orient;
149}
150
151
152/*!
153 Returns true if widgets are resized dynamically (opaquely), otherwise
154 returns false. This value is controlled by the QSplitter.
155
156 \sa QSplitter::opaqueResize()
157
158*/
159bool QSplitterHandle::opaqueResize() const
160{
161 Q_D(const QSplitterHandle);
162 return d->s->opaqueResize();
163}
164
165
166/*!
167 Returns the splitter associated with this splitter handle.
168
169 \sa QSplitter::handle()
170*/
171QSplitter *QSplitterHandle::splitter() const
172{
173 return d_func()->s;
174}
175
176/*!
177 Tells the splitter to move this handle to position \a pos, which is
178 the distance from the left or top edge of the widget.
179
180 Note that \a pos is also measured from the left (or top) for
181 right-to-left languages. This function will map \a pos to the
182 appropriate position before calling QSplitter::moveSplitter().
183
184 \sa QSplitter::moveSplitter() closestLegalPosition()
185*/
186void QSplitterHandle::moveSplitter(int pos)
187{
188 Q_D(QSplitterHandle);
189 if (d->s->isRightToLeft() && d->orient == Qt::Horizontal)
190 pos = d->s->contentsRect().width() - pos;
191 d->s->moveSplitter(pos, d->s->indexOf(this));
192}
193
194/*!
195 Returns the closest legal position to \a pos of the splitter
196 handle. The positions are measured from the left or top edge of
197 the splitter, even for right-to-left languages.
198
199 \sa QSplitter::closestLegalPosition(), moveSplitter()
200*/
201
202int QSplitterHandle::closestLegalPosition(int pos)
203{
204 Q_D(QSplitterHandle);
205 QSplitter *s = d->s;
206 if (s->isRightToLeft() && d->orient == Qt::Horizontal) {
207 int w = s->contentsRect().width();
208 return w - s->closestLegalPosition(w - pos, s->indexOf(this));
209 }
210 return s->closestLegalPosition(pos, s->indexOf(this));
211}
212
213/*!
214 \reimp
215*/
216QSize QSplitterHandle::sizeHint() const
217{
218 Q_D(const QSplitterHandle);
219 int hw = d->s->handleWidth();
220 QStyleOption opt(0);
221 opt.init(d->s);
222 opt.state = QStyle::State_None;
223 return parentWidget()->style()->sizeFromContents(QStyle::CT_Splitter, &opt, QSize(hw, hw), d->s)
224 .expandedTo(QApplication::globalStrut());
225}
226
227/*!
228 \reimp
229*/
230bool QSplitterHandle::event(QEvent *event)
231{
232 Q_D(QSplitterHandle);
233 switch(event->type()) {
234 case QEvent::HoverEnter:
235 d->hover = true;
236 update();
237 break;
238 case QEvent::HoverLeave:
239 d->hover = false;
240 update();
241 break;
242 default:
243 break;
244 }
245 return QWidget::event(event);
246}
247
248/*!
249 \reimp
250*/
251void QSplitterHandle::mouseMoveEvent(QMouseEvent *e)
252{
253 Q_D(QSplitterHandle);
254 if (!(e->buttons() & Qt::LeftButton))
255 return;
256 int pos = d->pick(parentWidget()->mapFromGlobal(e->globalPos()))
257 - d->mouseOffset;
258 if (opaqueResize()) {
259 moveSplitter(pos);
260 } else {
261 d->s->setRubberBand(closestLegalPosition(pos));
262 }
263}
264
265/*!
266 \reimp
267*/
268void QSplitterHandle::mousePressEvent(QMouseEvent *e)
269{
270 Q_D(QSplitterHandle);
271 if (e->button() == Qt::LeftButton) {
272 d->mouseOffset = d->pick(e->pos());
273 d->pressed = true;
274 update();
275 }
276}
277
278/*!
279 \reimp
280*/
281void QSplitterHandle::mouseReleaseEvent(QMouseEvent *e)
282{
283 Q_D(QSplitterHandle);
284 if (!opaqueResize() && e->button() == Qt::LeftButton) {
285 int pos = d->pick(parentWidget()->mapFromGlobal(e->globalPos()))
286 - d->mouseOffset;
287 d->s->setRubberBand(-1);
288 moveSplitter(pos);
289 }
290 if (e->button() == Qt::LeftButton) {
291 d->pressed = false;
292 update();
293 }
294}
295
296/*!
297 \reimp
298*/
299void QSplitterHandle::paintEvent(QPaintEvent *)
300{
301 Q_D(QSplitterHandle);
302 QPainter p(this);
303 QStyleOption opt(0);
304 opt.rect = rect();
305 opt.palette = palette();
306 if (orientation() == Qt::Horizontal)
307 opt.state = QStyle::State_Horizontal;
308 else
309 opt.state = QStyle::State_None;
310 if (d->hover)
311 opt.state |= QStyle::State_MouseOver;
312 if (d->pressed)
313 opt.state |= QStyle::State_Sunken;
314 if (isEnabled())
315 opt.state |= QStyle::State_Enabled;
316 parentWidget()->style()->drawControl(QStyle::CE_Splitter, &opt, &p, d->s);
317}
318
319
320int QSplitterLayoutStruct::getWidgetSize(Qt::Orientation orient)
321{
322 if (sizer == -1) {
323 QSize s = widget->sizeHint();
324 const int presizer = pick(s, orient);
325 const int realsize = pick(widget->size(), orient);
326 if (!s.isValid() || (widget->testAttribute(Qt::WA_Resized) && (realsize > presizer))) {
327 sizer = pick(widget->size(), orient);
328 } else {
329 sizer = presizer;
330 }
331 QSizePolicy p = widget->sizePolicy();
332 int sf = (orient == Qt::Horizontal) ? p.horizontalStretch() : p.verticalStretch();
333 if (sf > 1)
334 sizer *= sf;
335 }
336 return sizer;
337}
338
339int QSplitterLayoutStruct::getHandleSize(Qt::Orientation orient)
340{
341 return pick(handle->sizeHint(), orient);
342}
343
344void QSplitterPrivate::init()
345{
346 Q_Q(QSplitter);
347 QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Preferred);
348 if (orient == Qt::Vertical)
349 sp.transpose();
350 q->setSizePolicy(sp);
351 q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
352}
353
354void QSplitterPrivate::recalc(bool update)
355{
356 Q_Q(QSplitter);
357 int n = list.count();
358 /*
359 Splitter handles before the first visible widget or right
360 before a hidden widget must be hidden.
361 */
362 bool first = true;
363 bool allInvisible = n != 0;
364 for (int i = 0; i < n ; ++i) {
365 QSplitterLayoutStruct *s = list.at(i);
366 bool widgetHidden = s->widget->isHidden();
367 if (allInvisible && !widgetHidden && !s->collapsed)
368 allInvisible = false;
369 s->handle->setHidden(first || widgetHidden);
370 if (!widgetHidden)
371 first = false;
372 }
373
374 if (allInvisible)
375 for (int i = 0; i < n ; ++i) {
376 QSplitterLayoutStruct *s = list.at(i);
377 if (!s->widget->isHidden()) {
378 s->collapsed = false;
379 break;
380 }
381 }
382
383 int fi = 2 * q->frameWidth();
384 int maxl = fi;
385 int minl = fi;
386 int maxt = QWIDGETSIZE_MAX;
387 int mint = fi;
388 /*
389 calculate min/max sizes for the whole splitter
390 */
391 bool empty = true;
392 for (int j = 0; j < n; j++) {
393 QSplitterLayoutStruct *s = list.at(j);
394
395 if (!s->widget->isHidden()) {
396 empty = false;
397 if (!s->handle->isHidden()) {
398 minl += s->getHandleSize(orient);
399 maxl += s->getHandleSize(orient);
400 }
401
402 QSize minS = qSmartMinSize(s->widget);
403 minl += pick(minS);
404 maxl += pick(s->widget->maximumSize());
405 mint = qMax(mint, trans(minS));
406 int tm = trans(s->widget->maximumSize());
407 if (tm > 0)
408 maxt = qMin(maxt, tm);
409 }
410 }
411
412 if (empty) {
413 if (qobject_cast<QSplitter *>(parent)) {
414 // nested splitters; be nice
415 maxl = maxt = 0;
416 } else {
417 // QSplitter with no children yet
418 maxl = QWIDGETSIZE_MAX;
419 }
420 } else {
421 maxl = qMin<int>(maxl, QWIDGETSIZE_MAX);
422 }
423 if (maxt < mint)
424 maxt = mint;
425
426 if (update) {
427 if (orient == Qt::Horizontal) {
428 q->setMaximumSize(maxl, maxt);
429 if (q->isWindow())
430 q->setMinimumSize(minl,mint);
431 } else {
432 q->setMaximumSize(maxt, maxl);
433 if (q->isWindow())
434 q->setMinimumSize(mint,minl);
435 }
436 doResize();
437 q->updateGeometry();
438 } else {
439 firstShow = true;
440 }
441}
442
443void QSplitterPrivate::doResize()
444{
445 Q_Q(QSplitter);
446 QRect r = q->contentsRect();
447 int n = list.count();
448 QVector<QLayoutStruct> a(n*2);
449 int i;
450
451 bool noStretchFactorsSet = true;
452 for (i = 0; i < n; ++i) {
453 QSizePolicy p = list.at(i)->widget->sizePolicy();
454 int sf = orient == Qt::Horizontal ? p.horizontalStretch() : p.verticalStretch();
455 if (sf != 0) {
456 noStretchFactorsSet = false;
457 break;
458 }
459 }
460
461 int j=0;
462 for (i = 0; i < n; ++i) {
463 QSplitterLayoutStruct *s = list.at(i);
464#ifdef QSPLITTER_DEBUG
465 qDebug("widget %d hidden: %d collapsed: %d handle hidden: %d", i, s->widget->isHidden(),
466 s->collapsed, s->handle->isHidden());
467#endif
468
469 a[j].init();
470 if (s->handle->isHidden()) {
471 a[j].maximumSize = 0;
472 } else {
473 a[j].sizeHint = a[j].minimumSize = a[j].maximumSize = s->getHandleSize(orient);
474 a[j].empty = false;
475 }
476 ++j;
477
478 a[j].init();
479 if (s->widget->isHidden() || s->collapsed) {
480 a[j].maximumSize = 0;
481 } else {
482 a[j].minimumSize = pick(qSmartMinSize(s->widget));
483 a[j].maximumSize = pick(s->widget->maximumSize());
484 a[j].empty = false;
485
486 bool stretch = noStretchFactorsSet;
487 if (!stretch) {
488 QSizePolicy p = s->widget->sizePolicy();
489 int sf = orient == Qt::Horizontal ? p.horizontalStretch() : p.verticalStretch();
490 stretch = (sf != 0);
491 }
492 if (stretch) {
493 a[j].stretch = s->getWidgetSize(orient);
494 a[j].sizeHint = a[j].minimumSize;
495 a[j].expansive = true;
496 } else {
497 a[j].sizeHint = qMax(s->getWidgetSize(orient), a[j].minimumSize);
498 }
499 }
500 ++j;
501 }
502
503 qGeomCalc(a, 0, n*2, pick(r.topLeft()), pick(r.size()), 0);
504
505#ifdef QSPLITTER_DEBUG
506 for (i = 0; i < n*2; ++i) {
507 qDebug("%*s%d: stretch %d, sh %d, minS %d, maxS %d, exp %d, emp %d -> %d, %d",
508 i, "", i,
509 a[i].stretch,
510 a[i].sizeHint,
511 a[i].minimumSize,
512 a[i].maximumSize,
513 a[i].expansive,
514 a[i].empty,
515 a[i].pos,
516 a[i].size);
517 }
518#endif
519
520 for (i = 0; i < n; ++i) {
521 QSplitterLayoutStruct *s = list.at(i);
522 setGeo(s, a[i*2+1].pos, a[i*2+1].size, false);
523 }
524}
525
526void QSplitterPrivate::storeSizes()
527{
528 for (int i = 0; i < list.size(); ++i) {
529 QSplitterLayoutStruct *sls = list.at(i);
530 sls->sizer = pick(sls->rect.size());
531 }
532}
533
534void QSplitterPrivate::addContribution(int index, int *min, int *max, bool mayCollapse) const
535{
536 QSplitterLayoutStruct *s = list.at(index);
537 if (!s->widget->isHidden()) {
538 if (!s->handle->isHidden()) {
539 *min += s->getHandleSize(orient);
540 *max += s->getHandleSize(orient);
541 }
542 if (mayCollapse || !s->collapsed)
543 *min += pick(qSmartMinSize(s->widget));
544
545 *max += pick(s->widget->maximumSize());
546 }
547}
548
549int QSplitterPrivate::findWidgetJustBeforeOrJustAfter(int index, int delta, int &collapsibleSize) const
550{
551 if (delta < 0)
552 index += delta;
553 do {
554 QWidget *w = list.at(index)->widget;
555 if (!w->isHidden()) {
556 if (collapsible(list.at(index)))
557 collapsibleSize = pick(qSmartMinSize(w));
558 return index;
559 }
560 index += delta;
561 } while (index >= 0 && index < list.count());
562
563 return -1;
564}
565
566/*
567 For the splitter handle with index \a index, \a min and \a max give the range without collapsing any widgets,
568 and \a farMin and farMax give the range with collapsing included.
569*/
570void QSplitterPrivate::getRange(int index, int *farMin, int *min, int *max, int *farMax) const
571{
572 Q_Q(const QSplitter);
573 int n = list.count();
574 if (index <= 0 || index >= n)
575 return;
576
577 int collapsibleSizeBefore = 0;
578 int idJustBefore = findWidgetJustBeforeOrJustAfter(index, -1, collapsibleSizeBefore);
579
580 int collapsibleSizeAfter = 0;
581 int idJustAfter = findWidgetJustBeforeOrJustAfter(index, +1, collapsibleSizeAfter);
582
583 int minBefore = 0;
584 int minAfter = 0;
585 int maxBefore = 0;
586 int maxAfter = 0;
587 int i;
588
589 for (i = 0; i < index; ++i)
590 addContribution(i, &minBefore, &maxBefore, i == idJustBefore);
591 for (i = index; i < n; ++i)
592 addContribution(i, &minAfter, &maxAfter, i == idJustAfter);
593
594 QRect r = q->contentsRect();
595 int farMinVal;
596 int minVal;
597 int maxVal;
598 int farMaxVal;
599
600 int smartMinBefore = qMax(minBefore, pick(r.size()) - maxAfter);
601 int smartMaxBefore = qMin(maxBefore, pick(r.size()) - minAfter);
602
603 minVal = pick(r.topLeft()) + smartMinBefore;
604 maxVal = pick(r.topLeft()) + smartMaxBefore;
605
606 farMinVal = minVal;
607 if (minBefore - collapsibleSizeBefore >= pick(r.size()) - maxAfter)
608 farMinVal -= collapsibleSizeBefore;
609 farMaxVal = maxVal;
610 if (pick(r.size()) - (minAfter - collapsibleSizeAfter) <= maxBefore)
611 farMaxVal += collapsibleSizeAfter;
612
613 if (farMin)
614 *farMin = farMinVal;
615 if (min)
616 *min = minVal;
617 if (max)
618 *max = maxVal;
619 if (farMax)
620 *farMax = farMaxVal;
621}
622
623int QSplitterPrivate::adjustPos(int pos, int index, int *farMin, int *min, int *max, int *farMax) const
624{
625 const int Threshold = 40;
626
627 getRange(index, farMin, min, max, farMax);
628
629 if (pos >= *min) {
630 if (pos <= *max) {
631 return pos;
632 } else {
633 int delta = pos - *max;
634 int width = *farMax - *max;
635
636 if (delta > width / 2 && delta >= qMin(Threshold, width)) {
637 return *farMax;
638 } else {
639 return *max;
640 }
641 }
642 } else {
643 int delta = *min - pos;
644 int width = *min - *farMin;
645
646 if (delta > width / 2 && delta >= qMin(Threshold, width)) {
647 return *farMin;
648 } else {
649 return *min;
650 }
651 }
652}
653
654bool QSplitterPrivate::collapsible(QSplitterLayoutStruct *s) const
655{
656 if (s->collapsible != Default) {
657 return (bool)s->collapsible;
658 } else {
659 return childrenCollapsible;
660 }
661}
662
663void QSplitterPrivate::updateHandles()
664{
665 Q_Q(QSplitter);
666 recalc(q->isVisible());
667}
668
669void QSplitterPrivate::setSizes_helper(const QList<int> &sizes, bool clampNegativeSize)
670{
671 int j = 0;
672
673 for (int i = 0; i < list.size(); ++i) {
674 QSplitterLayoutStruct *s = list.at(i);
675
676 s->collapsed = false;
677 s->sizer = sizes.value(j++);
678 if (clampNegativeSize && s->sizer < 0)
679 s->sizer = 0;
680 int smartMinSize = pick(qSmartMinSize(s->widget));
681
682 // Make sure that we reset the collapsed state.
683 if (s->sizer == 0) {
684 if (collapsible(s) && smartMinSize > 0) {
685 s->collapsed = true;
686 } else {
687 s->sizer = smartMinSize;
688 }
689 } else {
690 if (s->sizer < smartMinSize)
691 s->sizer = smartMinSize;
692 }
693 }
694 doResize();
695}
696
697void QSplitterPrivate::setGeo(QSplitterLayoutStruct *sls, int p, int s, bool allowCollapse)
698{
699 Q_Q(QSplitter);
700 QWidget *w = sls->widget;
701 QRect r;
702 QRect contents = q->contentsRect();
703 if (orient == Qt::Horizontal) {
704 r.setRect(p, contents.y(), s, contents.height());
705 } else {
706 r.setRect(contents.x(), p, contents.width(), s);
707 }
708 sls->rect = r;
709
710 int minSize = pick(qSmartMinSize(w));
711
712 if (orient == Qt::Horizontal && q->isRightToLeft())
713 r.moveRight(contents.width() - r.left());
714
715 if (allowCollapse)
716 sls->collapsed = s <= 0 && minSize > 0 && !w->isHidden();
717
718 // Hide the child widget, but without calling hide() so that
719 // the splitter handle is still shown.
720 if (sls->collapsed)
721 r.moveTopLeft(QPoint(-r.width()-1, -r.height()-1));
722
723 w->setGeometry(r);
724
725 if (!sls->handle->isHidden()) {
726 QSplitterHandle *h = sls->handle;
727 QSize hs = h->sizeHint();
728 int left, top, right, bottom;
729 h->getContentsMargins(&left, &top, &right, &bottom);
730 if (orient==Qt::Horizontal) {
731 if (q->isRightToLeft())
732 p = contents.width() - p + hs.width();
733 h->setGeometry(p-hs.width() - left, contents.y(), hs.width() + left + right, contents.height());
734 } else {
735 h->setGeometry(contents.x(), p-hs.height() - top, contents.width(), hs.height() + top + bottom);
736 }
737 }
738}
739
740void QSplitterPrivate::doMove(bool backwards, int hPos, int index, int delta, bool mayCollapse,
741 int *positions, int *widths)
742{
743 if (index < 0 || index >= list.count())
744 return;
745
746#ifdef QSPLITTER_DEBUG
747 qDebug() << "QSplitterPrivate::doMove" << backwards << hPos << index << delta << mayCollapse;
748#endif
749
750 QSplitterLayoutStruct *s = list.at(index);
751 QWidget *w = s->widget;
752
753 int nextId = backwards ? index - delta : index + delta;
754
755 if (w->isHidden()) {
756 doMove(backwards, hPos, nextId, delta, collapsible(nextId), positions, widths);
757 } else {
758 int hs =s->handle->isHidden() ? 0 : s->getHandleSize(orient);
759
760 int ws = backwards ? hPos - pick(s->rect.topLeft())
761 : pick(s->rect.bottomRight()) - hPos -hs + 1;
762 if (ws > 0 || (!s->collapsed && !mayCollapse)) {
763 ws = qMin(ws, pick(w->maximumSize()));
764 ws = qMax(ws, pick(qSmartMinSize(w)));
765 } else {
766 ws = 0;
767 }
768 positions[index] = backwards ? hPos - ws : hPos + hs;
769 widths[index] = ws;
770 doMove(backwards, backwards ? hPos - ws - hs : hPos + hs + ws, nextId, delta,
771 collapsible(nextId), positions, widths);
772 }
773
774}
775
776QSplitterLayoutStruct *QSplitterPrivate::findWidget(QWidget *w) const
777{
778 for (int i = 0; i < list.size(); ++i) {
779 if (list.at(i)->widget == w)
780 return list.at(i);
781 }
782 return 0;
783}
784
785#ifdef QT3_SUPPORT
786static void setStretch(QWidget *w, int sf)
787{
788 QSizePolicy sp = w->sizePolicy();
789 sp.setHorizontalStretch(sf);
790 sp.setVerticalStretch(sf);
791 w->setSizePolicy(sp);
792}
793
794static int getStretch(const QWidget *w)
795{
796 QSizePolicy sp = w->sizePolicy();
797 return qMax(sp.horizontalStretch(), sp.verticalStretch());
798}
799
800void QSplitter::setResizeMode(QWidget *w, ResizeMode mode)
801{
802 /*
803 Internal comment:
804
805 This function tries to simulate the Qt 3.x ResizeMode
806 behavior using QSizePolicy stretch factors. This isn't easy,
807 because the default \l ResizeMode was \l Stretch, not \l
808 KeepSize, whereas the default stetch factor is 0.
809
810 So what we do is this: When the user calls setResizeMode()
811 the first time, we iterate through all the child widgets and
812 set their stretch factors to 1. Later on, if children are
813 added (using addWidget()), their stretch factors are also set
814 to 1.
815
816 There is just one problem left: Often, setResizeMode() is
817 called \e{before} addWidget(), because addWidget() is called
818 from the event loop. In that case, we use a special value,
819 243, instead of 0 to prevent 0 from being overwritten with 1
820 in addWidget(). This is a wicked hack, but fortunately it
821 only occurs as a result of calling a \c QT3_SUPPORT function.
822 */
823
824 Q_D(QSplitter);
825 bool metWidget = false;
826 if (!d->compatMode) {
827 d->compatMode = true;
828 for (int i = 0; i < d->list.size(); ++i) {
829 QSplitterLayoutStruct *s = d->list.at(i);
830 if (s->widget == w)
831 metWidget = true;
832 if (getStretch(s->widget) == 0)
833 setStretch(s->widget, 1);
834 }
835 }
836 int sf;
837 if (mode == KeepSize)
838 sf = metWidget ? 0 : 243;
839 else
840 sf = 1;
841 setStretch(w, sf);
842}
843
844/*!
845 Use one of the constructors that doesn't take the \a name
846 argument and then use setObjectName() instead.
847*/
848QSplitter::QSplitter(QWidget *parent, const char *name)
849 : QFrame(*new QSplitterPrivate, parent)
850{
851 Q_D(QSplitter);
852 setObjectName(QString::fromAscii(name));
853 d->orient = Qt::Horizontal;
854 d->init();
855}
856
857
858/*!
859 Use one of the constructors that don't take the \a name argument
860 and then use setObjectName() instead.
861*/
862QSplitter::QSplitter(Qt::Orientation orientation, QWidget *parent, const char *name)
863 : QFrame(*new QSplitterPrivate, parent)
864{
865 Q_D(QSplitter);
866 setObjectName(QString::fromAscii(name));
867 d->orient = orientation;
868 d->init();
869}
870#endif
871
872/*!
873 \internal
874*/
875void QSplitterPrivate::insertWidget_helper(int index, QWidget *widget, bool show)
876{
877 Q_Q(QSplitter);
878 QBoolBlocker b(blockChildAdd);
879 bool needShow = show && q->isVisible() &&
880 !(widget->isHidden() && widget->testAttribute(Qt::WA_WState_ExplicitShowHide));
881 if (widget->parentWidget() != q)
882 widget->setParent(q);
883 if (needShow)
884 widget->show();
885 insertWidget(index, widget);
886 recalc(q->isVisible());
887}
888
889/*
890 Inserts the widget \a w at position \a index in the splitter's list of widgets.
891
892 If \a w is already in the splitter, it will be moved to the new position.
893*/
894
895QSplitterLayoutStruct *QSplitterPrivate::insertWidget(int index, QWidget *w)
896{
897 Q_Q(QSplitter);
898 QSplitterLayoutStruct *sls = 0;
899 int i;
900 int last = list.count();
901 for (i = 0; i < list.size(); ++i) {
902 QSplitterLayoutStruct *s = list.at(i);
903 if (s->widget == w) {
904 sls = s;
905 --last;
906 break;
907 }
908 }
909 if (index < 0 || index > last)
910 index = last;
911
912 if (sls) {
913 list.move(i,index);
914 } else {
915 QSplitterHandle *newHandle = 0;
916 sls = new QSplitterLayoutStruct;
917 QString tmp = QLatin1String("qt_splithandle_");
918 tmp += w->objectName();
919 newHandle = q->createHandle();
920 newHandle->setObjectName(tmp);
921 sls->handle = newHandle;
922 sls->widget = w;
923 w->lower();
924 list.insert(index,sls);
925
926 if (newHandle && q->isVisible())
927 newHandle->show(); // will trigger sending of post events
928
929#ifdef QT3_SUPPORT
930 if (compatMode) {
931 int sf = getStretch(sls->widget);
932 if (sf == 243)
933 setStretch(sls->widget, 0);
934 else if (sf == 0)
935 setStretch(sls->widget, 1);
936 }
937#endif
938 }
939 return sls;
940}
941
942/*!
943 \class QSplitter
944 \brief The QSplitter class implements a splitter widget.
945
946 \ingroup organizers
947
948
949 A splitter lets the user control the size of child widgets by dragging the
950 boundary between the children. Any number of widgets may be controlled by a
951 single splitter. The typical use of a QSplitter is to create several
952 widgets and add them using insertWidget() or addWidget().
953
954 The following example will show a QListView, QTreeView, and
955 QTextEdit side by side, with two splitter handles:
956
957 \snippet doc/src/snippets/splitter/splitter.cpp 0
958
959 If a widget is already inside a QSplitter when insertWidget() or
960 addWidget() is called, it will move to the new position. This can be used
961 to reorder widgets in the splitter later. You can use indexOf(),
962 widget(), and count() to get access to the widgets inside the splitter.
963
964 A default QSplitter lays out its children horizontally (side by side); you
965 can use setOrientation(Qt::Vertical) to lay its
966 children out vertically.
967
968 By default, all widgets can be as large or as small as the user
969 wishes, between the \l minimumSizeHint() (or \l minimumSize())
970 and \l maximumSize() of the widgets.
971
972 QSplitter resizes its children dynamically by default. If you
973 would rather have QSplitter resize the children only at the end of
974 a resize operation, call setOpaqueResize(false).
975
976 The initial distribution of size between the widgets is determined by
977 multiplying the initial size with the stretch factor.
978 You can also use setSizes() to set the sizes
979 of all the widgets. The function sizes() returns the sizes set by the user.
980 Alternatively, you can save and restore the sizes of the widgets from a
981 QByteArray using saveState() and restoreState() respectively.
982
983 When you hide() a child its space will be distributed among the
984 other children. It will be reinstated when you show() it again.
985
986 \sa QSplitterHandle, QHBoxLayout, QVBoxLayout, QTabWidget
987*/
988
989
990/*!
991 Constructs a horizontal splitter with the \a parent
992 arguments is passed on to the QFrame constructor.
993
994 \sa setOrientation()
995*/
996QSplitter::QSplitter(QWidget *parent)
997 : QFrame(*new QSplitterPrivate, parent)
998{
999 Q_D(QSplitter);
1000 d->orient = Qt::Horizontal;
1001 d->init();
1002}
1003
1004
1005/*!
1006 Constructs a splitter with the given \a orientation and \a parent.
1007
1008 \sa setOrientation()
1009*/
1010QSplitter::QSplitter(Qt::Orientation orientation, QWidget *parent)
1011 : QFrame(*new QSplitterPrivate, parent)
1012{
1013 Q_D(QSplitter);
1014 d->orient = orientation;
1015 d->init();
1016}
1017
1018
1019/*!
1020 Destroys the splitter. All children are deleted.
1021*/
1022
1023QSplitter::~QSplitter()
1024{
1025 Q_D(QSplitter);
1026 delete d->rubberBand;
1027 while (!d->list.isEmpty())
1028 delete d->list.takeFirst();
1029}
1030
1031/*!
1032 Updates the splitter's state. You should not need to call this
1033 function.
1034*/
1035void QSplitter::refresh()
1036{
1037 Q_D(QSplitter);
1038 d->recalc(true);
1039}
1040
1041/*!
1042 \property QSplitter::orientation
1043 \brief the orientation of the splitter
1044
1045 By default the orientation is horizontal (i.e., the widgets are
1046 laid out side by side). The possible orientations are
1047 Qt::Horizontal and Qt::Vertical.
1048
1049 \sa QSplitterHandle::orientation()
1050*/
1051
1052void QSplitter::setOrientation(Qt::Orientation orientation)
1053{
1054 Q_D(QSplitter);
1055 if (d->orient == orientation)
1056 return;
1057
1058 if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) {
1059 QSizePolicy sp = sizePolicy();
1060 sp.transpose();
1061 setSizePolicy(sp);
1062 setAttribute(Qt::WA_WState_OwnSizePolicy, false);
1063 }
1064
1065 d->orient = orientation;
1066
1067 for (int i = 0; i < d->list.size(); ++i) {
1068 QSplitterLayoutStruct *s = d->list.at(i);
1069 s->handle->setOrientation(orientation);
1070 }
1071 d->recalc(isVisible());
1072}
1073
1074Qt::Orientation QSplitter::orientation() const
1075{
1076 Q_D(const QSplitter);
1077 return d->orient;
1078}
1079
1080/*!
1081 \property QSplitter::childrenCollapsible
1082 \brief whether child widgets can be resized down to size 0 by the user
1083
1084 By default, children are collapsible. It is possible to enable
1085 and disable the collapsing of individual children using
1086 setCollapsible().
1087
1088 \sa setCollapsible()
1089*/
1090
1091void QSplitter::setChildrenCollapsible(bool collapse)
1092{
1093 Q_D(QSplitter);
1094 d->childrenCollapsible = collapse;
1095}
1096
1097bool QSplitter::childrenCollapsible() const
1098{
1099 Q_D(const QSplitter);
1100 return d->childrenCollapsible;
1101}
1102
1103/*!
1104 Sets whether the child widget at index \a index is collapsible to \a collapse.
1105
1106 By default, children are collapsible, meaning that the user can
1107 resize them down to size 0, even if they have a non-zero
1108 minimumSize() or minimumSizeHint(). This behavior can be changed
1109 on a per-widget basis by calling this function, or globally for
1110 all the widgets in the splitter by setting the \l
1111 childrenCollapsible property.
1112
1113 \sa childrenCollapsible
1114*/
1115
1116void QSplitter::setCollapsible(int index, bool collapse)
1117{
1118 Q_D(QSplitter);
1119
1120 if (index < 0 || index >= d->list.size()) {
1121 qWarning("QSplitter::setCollapsible: Index %d out of range", index);
1122 return;
1123 }
1124 d->list.at(index)->collapsible = collapse ? 1 : 0;
1125}
1126
1127/*!
1128 Returns true if the widget at \a index is collapsible, otherwise returns false
1129*/
1130bool QSplitter::isCollapsible(int index) const
1131{
1132 Q_D(const QSplitter);
1133 if (index < 0 || index >= d->list.size()) {
1134 qWarning("QSplitter::isCollapsible: Index %d out of range", index);
1135 return false;
1136 }
1137 return d->list.at(index)->collapsible;
1138}
1139
1140/*!
1141 \reimp
1142*/
1143void QSplitter::resizeEvent(QResizeEvent *)
1144{
1145 Q_D(QSplitter);
1146 d->doResize();
1147}
1148
1149/*!
1150 Adds the given \a widget to the splitter's layout after all the other
1151 items.
1152
1153 If \a widget is already in the splitter, it will be moved to the new position.
1154
1155 \sa insertWidget() widget() indexOf()
1156*/
1157void QSplitter::addWidget(QWidget *widget)
1158{
1159 Q_D(QSplitter);
1160 insertWidget(d->list.count(), widget);
1161}
1162
1163/*!
1164 Inserts the \a widget specified into the splitter's layout at the
1165 given \a index.
1166
1167 If \a widget is already in the splitter, it will be moved to the new position.
1168
1169 if \a index is an invalid index, then the widget will be inserted at the end.
1170
1171 \sa addWidget() indexOf() widget()
1172*/
1173void QSplitter::insertWidget(int index, QWidget *widget)
1174{
1175 Q_D(QSplitter);
1176 d->insertWidget_helper(index, widget, true);
1177}
1178
1179/*!
1180 \fn int QSplitter::indexOf(QWidget *widget) const
1181
1182 Returns the index in the splitter's layout of the specified \a widget. This
1183 also works for handles.
1184
1185 Handles are numbered from 0. There are as many handles as there
1186 are child widgets, but the handle at position 0 is always hidden.
1187
1188
1189 \sa count(), widget()
1190*/
1191int QSplitter::indexOf(QWidget *w) const
1192{
1193 Q_D(const QSplitter);
1194 for (int i = 0; i < d->list.size(); ++i) {
1195 QSplitterLayoutStruct *s = d->list.at(i);
1196 if (s->widget == w || s->handle == w)
1197 return i;
1198 }
1199 return -1;
1200}
1201
1202/*!
1203 Returns a new splitter handle as a child widget of this splitter.
1204 This function can be reimplemented in subclasses to provide support
1205 for custom handles.
1206
1207 \sa handle(), indexOf()
1208*/
1209QSplitterHandle *QSplitter::createHandle()
1210{
1211 Q_D(QSplitter);
1212 return new QSplitterHandle(d->orient, this);
1213}
1214
1215/*!
1216 Returns the handle to the left (or above) for the item in the
1217 splitter's layout at the given \a index. The handle at index 0 is
1218 always hidden.
1219
1220 For right-to-left languages such as Arabic and Hebrew, the layout
1221 of horizontal splitters is reversed. The handle will be to the
1222 right of the widget at \a index.
1223
1224 \sa count(), widget(), indexOf(), createHandle(), setHandleWidth()
1225*/
1226QSplitterHandle *QSplitter::handle(int index) const
1227{
1228 Q_D(const QSplitter);
1229 if (index < 0 || index >= d->list.size())
1230 return 0;
1231 return d->list.at(index)->handle;
1232}
1233
1234/*!
1235 Returns the widget at the given \a index in the splitter's layout.
1236
1237 \sa count(), handle(), indexOf(), insertWidget()
1238*/
1239QWidget *QSplitter::widget(int index) const
1240{
1241 Q_D(const QSplitter);
1242 if (index < 0 || index >= d->list.size())
1243 return 0;
1244 return d->list.at(index)->widget;
1245}
1246
1247/*!
1248 Returns the number of widgets contained in the splitter's layout.
1249
1250 \sa widget(), handle()
1251*/
1252int QSplitter::count() const
1253{
1254 Q_D(const QSplitter);
1255 return d->list.count();
1256}
1257
1258/*!
1259 \reimp
1260
1261 Tells the splitter that the child widget described by \a c has been
1262 inserted or removed.
1263
1264 This method is also used to handle the situation where a widget is created
1265 with the splitter as a parent but not explicitly added with insertWidget()
1266 or addWidget(). This is for compatibility and not the recommended way of
1267 putting widgets into a splitter in new code. Please use insertWidget() or
1268 addWidget() in new code.
1269
1270 \sa addWidget() insertWidget()
1271*/
1272
1273void QSplitter::childEvent(QChildEvent *c)
1274{
1275 Q_D(QSplitter);
1276 if (!c->child()->isWidgetType())
1277 return;
1278 QWidget *w = static_cast<QWidget*>(c->child());
1279
1280 if (c->added() && !d->blockChildAdd && !w->isWindow() && !d->findWidget(w)) {
1281 d->insertWidget_helper(d->list.count(), w, false);
1282 } else if (c->polished() && !d->blockChildAdd) {
1283 if (isVisible() && !(w->isHidden() && w->testAttribute(Qt::WA_WState_ExplicitShowHide)))
1284 w->show();
1285 } else if (c->type() == QEvent::ChildRemoved) {
1286 for (int i = 0; i < d->list.size(); ++i) {
1287 QSplitterLayoutStruct *s = d->list.at(i);
1288 if (s->widget == w) {
1289 d->list.removeAt(i);
1290 delete s;
1291 d->recalc(isVisible());
1292 return;
1293 }
1294 }
1295 }
1296}
1297
1298
1299/*!
1300 Displays a rubber band at position \a pos. If \a pos is negative, the
1301 rubber band is removed.
1302*/
1303
1304void QSplitter::setRubberBand(int pos)
1305{
1306 Q_D(QSplitter);
1307 if (pos < 0) {
1308 if (d->rubberBand)
1309 QTimer::singleShot(0, d->rubberBand, SLOT(deleteLater()));
1310 return;
1311 }
1312 QRect r = contentsRect();
1313 const int rBord = 3; // customizable?
1314 int hw = handleWidth();
1315 if (!d->rubberBand) {
1316 d->rubberBand = new QRubberBand(QRubberBand::Line);
1317 // For accessibility to identify this special widget.
1318 d->rubberBand->setObjectName(QLatin1String("qt_rubberband"));
1319 }
1320 if (d->orient == Qt::Horizontal)
1321 d->rubberBand->setGeometry(QRect(QPoint(pos + hw / 2 - rBord, r.y()),
1322 QSize(2 * rBord, r.height())).translated(mapToGlobal(QPoint())));
1323 else
1324 d->rubberBand->setGeometry(QRect(QPoint(r.x(), pos + hw / 2 - rBord),
1325 QSize(r.width(), 2 * rBord)).translated(mapToGlobal(QPoint())));
1326 if (!d->rubberBand->isVisible())
1327 d->rubberBand->show();
1328}
1329
1330/*!
1331 \reimp
1332*/
1333
1334bool QSplitter::event(QEvent *e)
1335{
1336 Q_D(QSplitter);
1337 switch (e->type()) {
1338 case QEvent::Hide:
1339 // Reset firstShow to false here since things can be done to the splitter in between
1340 if (!d->firstShow)
1341 d->firstShow = true;
1342 break;
1343 case QEvent::Show:
1344 if (!d->firstShow)
1345 break;
1346 d->firstShow = false;
1347 // fall through
1348 case QEvent::HideToParent:
1349 case QEvent::ShowToParent:
1350 case QEvent::LayoutRequest:
1351#ifdef QT3_SUPPORT
1352 case QEvent::LayoutHint:
1353#endif
1354 d->recalc(isVisible());
1355 break;
1356 default:
1357 ;
1358 }
1359 return QWidget::event(e);
1360}
1361
1362/*!
1363 \fn QSplitter::splitterMoved(int pos, int index)
1364
1365 This signal is emitted when the splitter handle at a particular \a
1366 index has been moved to position \a pos.
1367
1368 For right-to-left languages such as Arabic and Hebrew, the layout
1369 of horizontal splitters is reversed. \a pos is then the
1370 distance from the right edge of the widget.
1371
1372 \sa moveSplitter()
1373*/
1374
1375/*!
1376 Moves the left or top edge of the splitter handle at \a index as
1377 close as possible to position \a pos, which is the distance from the
1378 left or top edge of the widget.
1379
1380 For right-to-left languages such as Arabic and Hebrew, the layout
1381 of horizontal splitters is reversed. \a pos is then the distance
1382 from the right edge of the widget.
1383
1384 \sa splitterMoved(), closestLegalPosition(), getRange()
1385*/
1386void QSplitter::moveSplitter(int pos, int index)
1387{
1388 Q_D(QSplitter);
1389 QSplitterLayoutStruct *s = d->list.at(index);
1390 int farMin;
1391 int min;
1392 int max;
1393 int farMax;
1394
1395#ifdef QSPLITTER_DEBUG
1396 int debugp = pos;
1397#endif
1398
1399 pos = d->adjustPos(pos, index, &farMin, &min, &max, &farMax);
1400 int oldP = d->pick(s->rect.topLeft());
1401#ifdef QSPLITTER_DEBUG
1402 qDebug() << "QSplitter::moveSplitter" << debugp << index << "adjusted" << pos << "oldP" << oldP;
1403#endif
1404
1405 QVarLengthArray<int, 32> poss(d->list.count());
1406 QVarLengthArray<int, 32> ws(d->list.count());
1407 bool upLeft;
1408
1409 d->doMove(false, pos, index, +1, (d->collapsible(s) && (pos > max)), poss.data(), ws.data());
1410 d->doMove(true, pos, index - 1, +1, (d->collapsible(index - 1) && (pos < min)), poss.data(), ws.data());
1411 upLeft = (pos < oldP);
1412
1413 int wid, delta, count = d->list.count();
1414 if (upLeft) {
1415 wid = 0;
1416 delta = 1;
1417 } else {
1418 wid = count - 1;
1419 delta = -1;
1420 }
1421 for (; wid >= 0 && wid < count; wid += delta) {
1422 QSplitterLayoutStruct *sls = d->list.at( wid );
1423 if (!sls->widget->isHidden())
1424 d->setGeo(sls, poss[wid], ws[wid], true);
1425 }
1426 d->storeSizes();
1427
1428 emit splitterMoved(pos, index);
1429}
1430
1431
1432/*!
1433 Returns the valid range of the splitter with index \a index in
1434 *\a{min} and *\a{max} if \a min and \a max are not 0.
1435*/
1436
1437void QSplitter::getRange(int index, int *min, int *max) const
1438{
1439 Q_D(const QSplitter);
1440 d->getRange(index, min, 0, 0, max);
1441}
1442
1443
1444/*!
1445 Returns the closest legal position to \a pos of the widget with index
1446 \a index.
1447
1448 For right-to-left languages such as Arabic and Hebrew, the layout
1449 of horizontal splitters is reversed. Positions are then measured
1450 from the right edge of the widget.
1451
1452 \sa getRange()
1453*/
1454
1455int QSplitter::closestLegalPosition(int pos, int index)
1456{
1457 Q_D(QSplitter);
1458 int x, i, n, u;
1459 return d->adjustPos(pos, index, &u, &n, &i, &x);
1460}
1461
1462/*!
1463 \property QSplitter::opaqueResize
1464 \brief whether resizing is opaque
1465
1466 Opaque resizing is on by default.
1467*/
1468
1469bool QSplitter::opaqueResize() const
1470{
1471 Q_D(const QSplitter);
1472 return d->opaque;
1473}
1474
1475
1476void QSplitter::setOpaqueResize(bool on)
1477{
1478 Q_D(QSplitter);
1479 d->opaque = on;
1480}
1481
1482#ifdef QT3_SUPPORT
1483/*!
1484 \fn void QSplitter::moveToFirst(QWidget *widget)
1485
1486 Use insertWidget(0, \a widget) instead.
1487*/
1488
1489
1490/*!
1491 \fn void QSplitter::moveToLast(QWidget *widget)
1492
1493 Use addWidget(\a widget) instead.
1494*/
1495
1496/*!
1497 \fn void QSplitter::setResizeMode(QWidget *widget, ResizeMode mode)
1498
1499 Use setStretchFactor() instead.
1500
1501 \oldcode
1502 splitter->setResizeMode(firstChild, QSplitter::KeepSize);
1503 splitter->setResizeMode(secondChild, QSplitter::Stretch);
1504 \newcode
1505 splitter->setStretchFactor(splitter->indexOf(firstChild), 0);
1506 splitter->setStretchFactor(splitter->indexOf(secondChild), 1);
1507 \endcode
1508*/
1509
1510/*!
1511 \enum QSplitter::ResizeMode
1512 \compat
1513
1514 This enum describes the different resizing behaviors child
1515 widgets can have:
1516
1517 \value Auto The widget will be resized according to the stretch factors set in its sizePolicy().
1518 \value Stretch The widget will be resized when the splitter itself is resized.
1519 \value KeepSize QSplitter will try to keep the widget's size unchanged.
1520 \value FollowSizeHint QSplitter will resize the widget when the widget's size hint changes.
1521
1522 Use setStretchFactor() instead.
1523*/
1524
1525/*!
1526 \fn void QSplitter::setCollapsible(QWidget *widget, bool collapsible)
1527
1528 Use setCollapsible(indexOf(\a widget, \a collapsible)) instead.
1529*/
1530
1531/*!
1532 \fn void QSplitter::setMargin(int margin)
1533 Sets the width of the margin around the contents of the widget to \a margin.
1534
1535 Use QWidget::setContentsMargins() instead.
1536 \sa margin(), QWidget::setContentsMargins()
1537*/
1538
1539/*!
1540 \fn int QSplitter::margin() const
1541 Returns the width of the margin around the contents of the widget.
1542
1543 Use QWidget::getContentsMargins() instead.
1544 \sa setMargin(), QWidget::getContentsMargins()
1545*/
1546
1547#endif
1548
1549/*!
1550 \reimp
1551*/
1552QSize QSplitter::sizeHint() const
1553{
1554 Q_D(const QSplitter);
1555 ensurePolished();
1556 int l = 0;
1557 int t = 0;
1558 QObjectList childList = children();
1559 for (int i = 0; i < childList.size(); ++i) {
1560 if (QWidget *w = qobject_cast<QWidget *>(childList.at(i))) {
1561 if (w->isHidden())
1562 continue;
1563 QSize s = w->sizeHint();
1564 if (s.isValid()) {
1565 l += d->pick(s);
1566 t = qMax(t, d->trans(s));
1567 }
1568 }
1569 }
1570 return orientation() == Qt::Horizontal ? QSize(l, t) : QSize(t, l);
1571}
1572
1573
1574/*!
1575 \reimp
1576*/
1577
1578QSize QSplitter::minimumSizeHint() const
1579{
1580 Q_D(const QSplitter);
1581 ensurePolished();
1582 int l = 0;
1583 int t = 0;
1584
1585 for (int i = 0; i < d->list.size(); ++i) {
1586 QSplitterLayoutStruct *s = d->list.at(i);
1587 if (!s || !s->widget)
1588 continue;
1589 if (s->widget->isHidden())
1590 continue;
1591 QSize widgetSize = qSmartMinSize(s->widget);
1592 if (widgetSize.isValid()) {
1593 l += d->pick(widgetSize);
1594 t = qMax(t, d->trans(widgetSize));
1595 }
1596 if (!s->handle || s->handle->isHidden())
1597 continue;
1598 QSize splitterSize = s->handle->sizeHint();
1599 if (splitterSize.isValid()) {
1600 l += d->pick(splitterSize);
1601 t = qMax(t, d->trans(splitterSize));
1602 }
1603 }
1604 return orientation() == Qt::Horizontal ? QSize(l, t) : QSize(t, l);
1605}
1606
1607
1608/*!
1609 Returns a list of the size parameters of all the widgets in this splitter.
1610
1611 If the splitter's orientation is horizontal, the list contains the
1612 widgets width in pixels, from left to right; if the orientation is
1613 vertical, the list contains the widgets height in pixels,
1614 from top to bottom.
1615
1616 Giving the values to another splitter's setSizes() function will
1617 produce a splitter with the same layout as this one.
1618
1619 Note that invisible widgets have a size of 0.
1620
1621 \sa setSizes()
1622*/
1623
1624QList<int> QSplitter::sizes() const
1625{
1626 Q_D(const QSplitter);
1627 ensurePolished();
1628
1629 QList<int> list;
1630 for (int i = 0; i < d->list.size(); ++i) {
1631 QSplitterLayoutStruct *s = d->list.at(i);
1632 list.append(d->pick(s->rect.size()));
1633 }
1634 return list;
1635}
1636
1637/*!
1638 Sets the child widgets respective sizes to the values given in the \a list.
1639
1640 If the splitter is horizontal, the values set the widths of each
1641 widget in pixels, from left to right. If the splitter is vertical, the
1642 heights of each widget is set, from top to bottom.
1643
1644 Extra values in the \a list are ignored. If \a list contains too few
1645 values, the result is undefined but the program will still be well-behaved.
1646
1647 The overall size of the splitter widget is not affected.
1648 Instead, any additional/missing space is distributed amongst the
1649 widgets according to the relative weight of the sizes.
1650
1651 If you specify a size of 0, the widget will be invisible. The size policies
1652 of the widgets are preserved. That is, a value smaller then the minimal size
1653 hint of the respective widget will be replaced by the value of the hint.
1654
1655 \sa sizes()
1656*/
1657
1658void QSplitter::setSizes(const QList<int> &list)
1659{
1660 Q_D(QSplitter);
1661 d->setSizes_helper(list, true);
1662}
1663
1664/*!
1665 \property QSplitter::handleWidth
1666 \brief the width of the splitter handles
1667
1668 By default, this property contains a value that depends on the user's platform
1669 and style preferences.
1670*/
1671
1672int QSplitter::handleWidth() const
1673{
1674 Q_D(const QSplitter);
1675 if (d->handleWidth > 0) {
1676 return d->handleWidth;
1677 } else {
1678 return style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this);
1679 }
1680}
1681
1682void QSplitter::setHandleWidth(int width)
1683{
1684 Q_D(QSplitter);
1685 d->handleWidth = width;
1686 d->updateHandles();
1687}
1688
1689/*!
1690 \reimp
1691*/
1692void QSplitter::changeEvent(QEvent *ev)
1693{
1694 Q_D(QSplitter);
1695 if(ev->type() == QEvent::StyleChange)
1696 d->updateHandles();
1697 QFrame::changeEvent(ev);
1698}
1699
1700static const qint32 SplitterMagic = 0xff;
1701
1702/*!
1703 Saves the state of the splitter's layout.
1704
1705 Typically this is used in conjunction with QSettings to remember the size
1706 for a future session. A version number is stored as part of the data.
1707 Here is an example:
1708
1709 \snippet doc/src/snippets/splitter/splitter.cpp 1
1710
1711 \sa restoreState()
1712*/
1713QByteArray QSplitter::saveState() const
1714{
1715 Q_D(const QSplitter);
1716 int version = 0;
1717 QByteArray data;
1718 QDataStream stream(&data, QIODevice::WriteOnly);
1719
1720 stream << qint32(SplitterMagic);
1721 stream << qint32(version);
1722 QList<int> list;
1723 for (int i = 0; i < d->list.size(); ++i) {
1724 QSplitterLayoutStruct *s = d->list.at(i);
1725 list.append(s->sizer);
1726 }
1727 stream << list;
1728 stream << childrenCollapsible();
1729 stream << qint32(handleWidth());
1730 stream << opaqueResize();
1731 stream << qint32(orientation());
1732 return data;
1733}
1734
1735/*!
1736 Restores the splitter's layout to the \a state specified.
1737 Returns true if the state is restored; otherwise returns false.
1738
1739 Typically this is used in conjunction with QSettings to restore the size
1740 from a past session. Here is an example:
1741
1742 Restore the splitters's state:
1743
1744 \snippet doc/src/snippets/splitter/splitter.cpp 2
1745
1746 A failure to restore the splitter's layout may result from either
1747 invalid or out-of-date data in the supplied byte array.
1748
1749 \sa saveState()
1750*/
1751bool QSplitter::restoreState(const QByteArray &state)
1752{
1753 Q_D(QSplitter);
1754 int version = 0;
1755 QByteArray sd = state;
1756 QDataStream stream(&sd, QIODevice::ReadOnly);
1757 QList<int> list;
1758 bool b;
1759 qint32 i;
1760 qint32 marker;
1761 qint32 v;
1762
1763 stream >> marker;
1764 stream >> v;
1765 if (marker != SplitterMagic || v != version)
1766 return false;
1767
1768 stream >> list;
1769 d->setSizes_helper(list, false);
1770
1771 stream >> b;
1772 setChildrenCollapsible(b);
1773
1774 stream >> i;
1775 setHandleWidth(i);
1776
1777 stream >> b;
1778 setOpaqueResize(b);
1779
1780 stream >> i;
1781 setOrientation(Qt::Orientation(i));
1782 d->doResize();
1783
1784 return true;
1785}
1786
1787/*!
1788 Updates the size policy of the widget at position \a index to
1789 have a stretch factor of \a stretch.
1790
1791 \a stretch is not the effective stretch factor; the effective
1792 stretch factor is calculated by taking the initial size of the
1793 widget and multiplying it with \a stretch.
1794
1795 This function is provided for convenience. It is equivalent to
1796
1797 \snippet doc/src/snippets/code/src_gui_widgets_qsplitter.cpp 0
1798
1799 \sa setSizes(), widget()
1800*/
1801void QSplitter::setStretchFactor(int index, int stretch)
1802{
1803 Q_D(QSplitter);
1804 if (index <= -1 || index >= d->list.count())
1805 return;
1806
1807 QWidget *widget = d->list.at(index)->widget;
1808 QSizePolicy sp = widget->sizePolicy();
1809 sp.setHorizontalStretch(stretch);
1810 sp.setVerticalStretch(stretch);
1811 widget->setSizePolicy(sp);
1812}
1813
1814
1815//#ifdef QT3_SUPPORT
1816#ifndef QT_NO_TEXTSTREAM
1817/*!
1818 \relates QSplitter
1819 \obsolete
1820
1821 Use \a ts << \a{splitter}.saveState() instead.
1822*/
1823
1824QTextStream& operator<<(QTextStream& ts, const QSplitter& splitter)
1825{
1826 ts << splitter.saveState() << endl;
1827 return ts;
1828}
1829
1830/*!
1831 \relates QSplitter
1832 \obsolete
1833
1834 Use \a ts >> \a{splitter}.restoreState() instead.
1835*/
1836
1837QTextStream& operator>>(QTextStream& ts, QSplitter& splitter)
1838{
1839 QString line = ts.readLine();
1840 line = line.simplified();
1841 line.replace(QLatin1Char(' '), QString());
1842 line = line.toUpper();
1843
1844 splitter.restoreState(line.toAscii());
1845 return ts;
1846}
1847#endif // QT_NO_TEXTSTREAM
1848//#endif // QT3_SUPPORT
1849
1850QT_END_NAMESPACE
1851
1852#endif // QT_NO_SPLITTER
Note: See TracBrowser for help on using the repository browser.