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

Last change on this file since 913 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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