src/gui/widgets/qsplitter.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the QtGui module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include "qsplitter.h"
00025 #ifndef QT_NO_SPLITTER
00026 
00027 #include "qapplication.h"
00028 #include "qcursor.h"
00029 #include "qdrawutil.h"
00030 #include "qevent.h"
00031 #include "qlayout.h"
00032 #include "qlist.h"
00033 #include "qpainter.h"
00034 #include "qrubberband.h"
00035 #include "qstyle.h"
00036 #include "qstyleoption.h"
00037 #include "qtextstream.h"
00038 #include "qvarlengtharray.h"
00039 #include "qvector.h"
00040 #include "private/qlayoutengine_p.h"
00041 #include "private/qsplitter_p.h"
00042 #include "qdebug.h"
00043 
00044 #include <ctype.h>
00045 
00046 //#define QSPLITTER_DEBUG
00047 
00102 QSplitterHandle::QSplitterHandle(Qt::Orientation orientation, QSplitter *parent)
00103     : QWidget(*new QSplitterHandlePrivate, parent, 0)
00104 {
00105     Q_D(QSplitterHandle);
00106     d->s = parent;
00107     d->hover = false;
00108     setOrientation(orientation);
00109 }
00110 
00117 void QSplitterHandle::setOrientation(Qt::Orientation orientation)
00118 {
00119     Q_D(QSplitterHandle);
00120     d->orient = orientation;
00121 #ifndef QT_NO_CURSOR
00122     setCursor(orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor);
00123 #endif
00124 }
00125 
00131 Qt::Orientation QSplitterHandle::orientation() const
00132 {
00133     Q_D(const QSplitterHandle);
00134     return d->orient;
00135 }
00136 
00137 
00145 bool QSplitterHandle::opaqueResize() const
00146 {
00147     Q_D(const QSplitterHandle);
00148     return d->s->opaqueResize();
00149 }
00150 
00151 
00157 QSplitter *QSplitterHandle::splitter() const
00158 {
00159     return d_func()->s;
00160 }
00161 
00172 void QSplitterHandle::moveSplitter(int pos)
00173 {
00174     Q_D(QSplitterHandle);
00175     if (d->s->isRightToLeft() && d->orient == Qt::Horizontal)
00176         pos = d->s->contentsRect().width() - pos;
00177     d->s->moveSplitter(pos, d->s->indexOf(this));
00178 }
00179 
00188 int QSplitterHandle::closestLegalPosition(int pos)
00189 {
00190     Q_D(QSplitterHandle);
00191     QSplitter *s = d->s;
00192     if (s->isRightToLeft() && d->orient == Qt::Horizontal) {
00193         int w = s->contentsRect().width();
00194         return w - s->closestLegalPosition(w - pos, s->indexOf(this));
00195     }
00196     return s->closestLegalPosition(pos, s->indexOf(this));
00197 }
00198 
00202 QSize QSplitterHandle::sizeHint() const
00203 {
00204     Q_D(const QSplitterHandle);
00205     int hw = d->s->handleWidth();
00206     QStyleOption opt(0);
00207     opt.init(d->s);
00208     opt.state = QStyle::State_None;
00209     return parentWidget()->style()->sizeFromContents(QStyle::CT_Splitter, &opt, QSize(hw, hw), d->s)
00210         .expandedTo(QApplication::globalStrut());
00211 }
00212 
00216 bool QSplitterHandle::event(QEvent *event)
00217 {
00218     Q_D(QSplitterHandle);
00219     switch(event->type()) {
00220     case QEvent::HoverEnter:
00221         d->hover = true;
00222         update();
00223         break;
00224     case QEvent::HoverLeave:
00225         d->hover = false;
00226         update();
00227         break;
00228     default:
00229         break;
00230     }
00231     return QWidget::event(event);
00232 }
00233 
00237 void QSplitterHandle::mouseMoveEvent(QMouseEvent *e)
00238 {
00239     Q_D(QSplitterHandle);
00240     if (!(e->buttons() & Qt::LeftButton))
00241         return;
00242     int pos = d->pick(parentWidget()->mapFromGlobal(e->globalPos()))
00243                  - d->mouseOffset;
00244     if (opaqueResize()) {
00245         moveSplitter(pos);
00246     } else {
00247         d->s->setRubberBand(closestLegalPosition(pos));
00248     }
00249 }
00250 
00254 void QSplitterHandle::mousePressEvent(QMouseEvent *e)
00255 {
00256     Q_D(QSplitterHandle);
00257     if (e->button() == Qt::LeftButton)
00258         d->mouseOffset = d->pick(e->pos());
00259 }
00260 
00264 void QSplitterHandle::mouseReleaseEvent(QMouseEvent *e)
00265 {
00266     Q_D(QSplitterHandle);
00267     if (!opaqueResize() && e->button() == Qt::LeftButton) {
00268         int pos = d->pick(parentWidget()->mapFromGlobal(e->globalPos()))
00269                      - d->mouseOffset;
00270         d->s->setRubberBand(-1);
00271         moveSplitter(pos);
00272     }
00273 }
00274 
00278 void QSplitterHandle::paintEvent(QPaintEvent *)
00279 {
00280     Q_D(QSplitterHandle);
00281     QPainter p(this);
00282     QStyleOption opt(0);
00283     opt.rect = rect();
00284     opt.palette = palette();
00285     if (orientation() == Qt::Horizontal)
00286         opt.state = QStyle::State_Horizontal;
00287     else
00288         opt.state = QStyle::State_None;
00289     if (d->hover)
00290         opt.state |= QStyle::State_MouseOver;
00291     if (isEnabled())
00292         opt.state |= QStyle::State_Enabled;
00293     parentWidget()->style()->drawControl(QStyle::CE_Splitter, &opt, &p, d->s);
00294 }
00295 
00296 
00297 int QSplitterLayoutStruct::getWidgetSize(Qt::Orientation orient)
00298 {
00299     if (sizer == -1) {
00300         QSize s = widget->sizeHint();
00301         const int presizer = pick(s, orient);
00302         const int realsize = pick(widget->size(), orient);
00303         if (!s.isValid() || (widget->testAttribute(Qt::WA_Resized) && (realsize > presizer))) {
00304             sizer = pick(widget->size(), orient);
00305         } else {
00306             sizer = presizer;
00307         }
00308         QSizePolicy p = widget->sizePolicy();
00309         int sf = (orient == Qt::Horizontal) ? p.horizontalStretch() : p.verticalStretch();
00310         if (sf > 1)
00311             sizer *= sf;
00312     }
00313     return sizer;
00314 }
00315 
00316 int QSplitterLayoutStruct::getHandleSize(Qt::Orientation orient)
00317 {
00318     return pick(handle->sizeHint(), orient);
00319 }
00320 
00321 void QSplitterPrivate::init()
00322 {
00323     Q_Q(QSplitter);
00324     QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Preferred);
00325     if (orient == Qt::Vertical)
00326         sp.transpose();
00327     q->setSizePolicy(sp);
00328     q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
00329 }
00330 
00331 void QSplitterPrivate::recalc(bool update)
00332 {
00333     Q_Q(QSplitter);
00334     int n = list.count();
00335     /*
00336       Splitter handles before the first visible widget or right
00337       before a hidden widget must be hidden.
00338     */
00339     bool first = true;
00340     for (int i = 0; i < n ; ++i) {
00341         QSplitterLayoutStruct *s = list.at(i);
00342         s->handle->setHidden(first || s->widget->isHidden());
00343         if (!s->widget->isHidden())
00344             first = false;
00345     }
00346 
00347     int fi = 2 * q->frameWidth();
00348     int maxl = fi;
00349     int minl = fi;
00350     int maxt = QWIDGETSIZE_MAX;
00351     int mint = fi;
00352     /*
00353       calculate min/max sizes for the whole splitter
00354     */
00355     bool empty = true;
00356     for (int j = 0; j < n; j++) {
00357         QSplitterLayoutStruct *s = list.at(j);
00358 
00359         if (!s->widget->isHidden()) {
00360             empty = false;
00361             if (!s->handle->isHidden()) {
00362                 minl += s->getHandleSize(orient);
00363                 maxl += s->getHandleSize(orient);
00364             }
00365 
00366             QSize minS = qSmartMinSize(s->widget);
00367             minl += pick(minS);
00368             maxl += pick(s->widget->maximumSize());
00369             mint = qMax(mint, trans(minS));
00370             int tm = trans(s->widget->maximumSize());
00371             if (tm > 0)
00372                 maxt = qMin(maxt, tm);
00373         }
00374     }
00375 
00376     if (empty) {
00377         if (qobject_cast<QSplitter *>(q->parentWidget())) {
00378             // nested splitters; be nice
00379             maxl = maxt = 0;
00380         } else {
00381             // QSplitter with no children yet
00382             maxl = QWIDGETSIZE_MAX;
00383         }
00384     } else {
00385         maxl = qMin<int>(maxl, QWIDGETSIZE_MAX);
00386     }
00387     if (maxt < mint)
00388         maxt = mint;
00389 
00390     if (update) {
00391         if (orient == Qt::Horizontal) {
00392             q->setMaximumSize(maxl, maxt);
00393             if (q->isWindow())
00394                 q->setMinimumSize(minl,mint);
00395         } else {
00396             q->setMaximumSize(maxt, maxl);
00397             if (q->isWindow())
00398                 q->setMinimumSize(mint,minl);
00399         }
00400         doResize();
00401         q->updateGeometry();
00402     } else {
00403         firstShow = true;
00404     }
00405 }
00406 
00407 void QSplitterPrivate::doResize()
00408 {
00409     Q_Q(QSplitter);
00410     QRect r = q->contentsRect();
00411     int n = list.count();
00412     QVector<QLayoutStruct> a(n*2);
00413     int i;
00414 
00415     bool noStretchFactorsSet = true;
00416     for (i = 0; i < n; ++i) {
00417         QSizePolicy p = list.at(i)->widget->sizePolicy();
00418         int sf = orient == Qt::Horizontal ? p.horizontalStretch() : p.verticalStretch();
00419         if (sf != 0) {
00420             noStretchFactorsSet = false;
00421             break;
00422         }
00423     }
00424 
00425     int j=0;
00426     for (i = 0; i < n; ++i) {
00427         QSplitterLayoutStruct *s = list.at(i);
00428 #ifdef QSPLITTER_DEBUG
00429         qDebug("widget %d hidden: %d collapsed: %d handle hidden: %d", i, s->widget->isHidden(),
00430                s->collapsed, s->handle->isHidden());
00431 #endif
00432 
00433         a[j].init();
00434         if (s->handle->isHidden()) {
00435             a[j].maximumSize = 0;
00436         } else {
00437             a[j].sizeHint = a[j].minimumSize = a[j].maximumSize = s->getHandleSize(orient);
00438             a[j].empty = false;
00439         }
00440         ++j;
00441 
00442         a[j].init();
00443         if (s->widget->isHidden() || s->collapsed) {
00444             a[j].maximumSize = 0;
00445         } else {
00446             a[j].minimumSize = pick(qSmartMinSize(s->widget));
00447             a[j].maximumSize = pick(s->widget->maximumSize());
00448             a[j].empty = false;
00449 
00450             bool stretch = noStretchFactorsSet;
00451             if (!stretch) {
00452                 QSizePolicy p = s->widget->sizePolicy();
00453                 int sf = orient == Qt::Horizontal ? p.horizontalStretch() : p.verticalStretch();
00454                 stretch = (sf != 0);
00455             }
00456             if (stretch) {
00457                 a[j].stretch = s->getWidgetSize(orient);
00458                 a[j].sizeHint = a[j].minimumSize;
00459                 a[j].expansive = true;
00460             } else {
00461                 a[j].sizeHint = s->getWidgetSize(orient);
00462             }
00463         }
00464         ++j;
00465     }
00466 
00467     qGeomCalc(a, 0, n*2, pick(r.topLeft()), pick(r.size()), 0);
00468 
00469 #ifdef QSPLITTER_DEBUG
00470     for (i = 0; i < n*2; ++i) {
00471         qDebug("%*s%d: stretch %d, sh %d, minS %d, maxS %d, exp %d, emp %d -> %d, %d",
00472                i, "", i,
00473                a[i].stretch,
00474                a[i].sizeHint,
00475                a[i].minimumSize,
00476                a[i].maximumSize,
00477                a[i].expansive,
00478                a[i].empty,
00479                a[i].pos,
00480                a[i].size);
00481     }
00482 #endif
00483 
00484     for (i = 0; i < n; ++i) {
00485         QSplitterLayoutStruct *s = list.at(i);
00486         setGeo(s, a[i*2+1].pos, a[i*2+1].size, false);
00487     }
00488 }
00489 
00490 void QSplitterPrivate::storeSizes()
00491 {
00492     for (int i = 0; i < list.size(); ++i) {
00493         QSplitterLayoutStruct *sls = list.at(i);
00494         sls->sizer = pick(sls->rect.size());
00495     }
00496 }
00497 
00498 void QSplitterPrivate::addContribution(int index, int *min, int *max, bool mayCollapse) const
00499 {
00500     QSplitterLayoutStruct *s = list.at(index);
00501     if (!s->widget->isHidden()) {
00502         if (!s->handle->isHidden()) {
00503             *min += s->getHandleSize(orient);
00504             *max += s->getHandleSize(orient);
00505         }
00506         if (mayCollapse || !s->collapsed)
00507             *min += pick(qSmartMinSize(s->widget));
00508 
00509         *max += pick(s->widget->maximumSize());
00510     }
00511 }
00512 
00513 int QSplitterPrivate::findWidgetJustBeforeOrJustAfter(int index, int delta, int &collapsibleSize) const
00514 {
00515     if (delta < 0)
00516         index += delta;
00517     do {
00518         QWidget *w = list.at(index)->widget;
00519         if (!w->isHidden()) {
00520             if (collapsible(list.at(index)))
00521                 collapsibleSize = pick(qSmartMinSize(w));
00522             return index;
00523         }
00524         index += delta;
00525     } while (index >= 0 && index < list.count());
00526 
00527     return -1;
00528 }
00529 
00530 /*
00531   For the splitter handle with index \a index, \a min and \a max give the range without collapsing any widgets,
00532   and \a farMin and farMax give the range with collapsing included.
00533 */
00534 void QSplitterPrivate::getRange(int index, int *farMin, int *min, int *max, int *farMax) const
00535 {
00536     Q_Q(const QSplitter);
00537     int n = list.count();
00538     if (index <= 0 || index >= n)
00539         return;
00540 
00541     int collapsibleSizeBefore = 0;
00542     int idJustBefore = findWidgetJustBeforeOrJustAfter(index, -1, collapsibleSizeBefore);
00543 
00544     int collapsibleSizeAfter = 0;
00545     int idJustAfter = findWidgetJustBeforeOrJustAfter(index, +1, collapsibleSizeAfter);
00546 
00547     int minBefore = 0;
00548     int minAfter = 0;
00549     int maxBefore = 0;
00550     int maxAfter = 0;
00551     int i;
00552 
00553     for (i = 0; i < index; ++i)
00554         addContribution(i, &minBefore, &maxBefore, i == idJustBefore);
00555     for (i = index; i < n; ++i)
00556         addContribution(i, &minAfter, &maxAfter, i == idJustAfter);
00557 
00558     QRect r = q->contentsRect();
00559     int farMinVal;
00560     int minVal;
00561     int maxVal;
00562     int farMaxVal;
00563 
00564     int smartMinBefore = qMax(minBefore, pick(r.size()) - maxAfter);
00565     int smartMaxBefore = qMin(maxBefore, pick(r.size()) - minAfter);
00566 
00567     minVal = pick(r.topLeft()) + smartMinBefore;
00568     maxVal = pick(r.topLeft()) + smartMaxBefore;
00569 
00570     farMinVal = minVal;
00571     if (minBefore - collapsibleSizeBefore >= pick(r.size()) - maxAfter)
00572         farMinVal -= collapsibleSizeBefore;
00573     farMaxVal = maxVal;
00574     if (pick(r.size()) - (minAfter - collapsibleSizeAfter) <= maxBefore)
00575         farMaxVal += collapsibleSizeAfter;
00576 
00577     if (farMin)
00578         *farMin = farMinVal;
00579     if (min)
00580         *min = minVal;
00581     if (max)
00582         *max = maxVal;
00583     if (farMax)
00584         *farMax = farMaxVal;
00585 }
00586 
00587 int QSplitterPrivate::adjustPos(int pos, int index, int *farMin, int *min, int *max, int *farMax) const
00588 {
00589     const int Threshold = 40;
00590 
00591     getRange(index, farMin, min, max, farMax);
00592 
00593     if (pos >= *min) {
00594         if (pos <= *max) {
00595             return pos;
00596         } else {
00597             int delta = pos - *max;
00598             int width = *farMax - *max;
00599 
00600             if (delta > width / 2 && delta >= qMin(Threshold, width)) {
00601                 return *farMax;
00602             } else {
00603                 return *max;
00604             }
00605         }
00606     } else {
00607         int delta = *min - pos;
00608         int width = *min - *farMin;
00609 
00610         if (delta > width / 2 && delta >= qMin(Threshold, width)) {
00611             return *farMin;
00612         } else {
00613             return *min;
00614         }
00615     }
00616 }
00617 
00618 bool QSplitterPrivate::collapsible(QSplitterLayoutStruct *s) const
00619 {
00620     if (s->collapsible != Default) {
00621         return (bool)s->collapsible;
00622     } else {
00623         return childrenCollapsible;
00624     }
00625 }
00626 
00627 void QSplitterPrivate::updateHandles()
00628 {
00629     Q_Q(QSplitter);
00630     recalc(q->isVisible());
00631 }
00632 
00633 void QSplitterPrivate::setGeo(QSplitterLayoutStruct *sls, int p, int s, bool allowCollapse)
00634 {
00635     Q_Q(QSplitter);
00636     QWidget *w = sls->widget;
00637     QRect r;
00638     QRect contents = q->contentsRect();
00639     if (orient == Qt::Horizontal) {
00640         r.setRect(p, contents.y(), s, contents.height());
00641     } else {
00642         r.setRect(contents.x(), p, contents.width(), s);
00643     }
00644     sls->rect = r;
00645 
00646     int minSize = pick(qSmartMinSize(w));
00647 
00648     if (orient == Qt::Horizontal && q->isRightToLeft())
00649         r.moveRight(contents.width() - r.left());
00650 
00651     if (allowCollapse)
00652         sls->collapsed = s <= 0 && minSize > 0 && !w->isHidden();
00653 
00654     //   Hide the child widget, but without calling hide() so that
00655     //   the splitter handle is still shown.
00656     if (sls->collapsed)
00657         r.moveTopLeft(QPoint(-r.width()-1, -r.height()-1));
00658 
00659     w->setGeometry(r);
00660 
00661     if (!sls->handle->isHidden()) {
00662         QSplitterHandle *h = sls->handle;
00663         QSize hs = h->sizeHint();
00664         if (orient==Qt::Horizontal) {
00665             if (q->isRightToLeft())
00666                 p = contents.width() - p + hs.width();
00667             h->setGeometry(p-hs.width(), contents.y(), hs.width(), contents.height());
00668         } else {
00669             h->setGeometry(contents.x(), p-hs.height(), contents.width(), hs.height());
00670         }
00671     }
00672 }
00673 
00674 void QSplitterPrivate::doMove(bool backwards, int hPos, int index, int delta, bool mayCollapse,
00675                               int *positions, int *widths)
00676 {
00677     if (index < 0 || index >= list.count())
00678         return;
00679 
00680 #ifdef QSPLITTER_DEBUG
00681     qDebug() << "QSplitterPrivate::doMove" << backwards << hPos << index << delta << mayCollapse;
00682 #endif
00683 
00684     QSplitterLayoutStruct *s = list.at(index);
00685     QWidget *w = s->widget;
00686 
00687     int nextId = backwards ? index - delta : index + delta;
00688 
00689     if (w->isHidden()) {
00690         doMove(backwards, hPos, nextId, delta, collapsible(nextId), positions, widths);
00691     } else {
00692         int hs =s->handle->isHidden() ? 0 : s->getHandleSize(orient);
00693 
00694         int  ws = backwards ? hPos - pick(s->rect.topLeft())
00695                  : pick(s->rect.bottomRight()) - hPos -hs + 1;
00696         if (ws > 0 || (!s->collapsed && !mayCollapse)) {
00697             ws = qMin(ws, pick(w->maximumSize()));
00698             ws = qMax(ws, pick(qSmartMinSize(w)));
00699         } else {
00700             ws = 0;
00701         }
00702         positions[index] = backwards ? hPos - ws : hPos + hs;
00703         widths[index] = ws;
00704         doMove(backwards, backwards ? hPos - ws - hs : hPos + hs + ws, nextId, delta,
00705                collapsible(nextId), positions, widths);
00706     }
00707 
00708 }
00709 
00710 QSplitterLayoutStruct *QSplitterPrivate::findWidget(QWidget *w) const
00711 {
00712     for (int i = 0; i < list.size(); ++i) {
00713         if (list.at(i)->widget == w)
00714             return list.at(i);
00715     }
00716     return 0;
00717 }
00718 
00719 #ifdef QT3_SUPPORT
00720 static void setStretch(QWidget *w, int sf)
00721 {
00722     QSizePolicy sp = w->sizePolicy();
00723     sp.setHorizontalStretch(sf);
00724     sp.setVerticalStretch(sf);
00725     w->setSizePolicy(sp);
00726 }
00727 
00728 static int getStretch(const QWidget *w)
00729 {
00730     QSizePolicy sp = w->sizePolicy();
00731     return qMax(sp.horizontalStretch(), sp.verticalStretch());
00732 }
00733 
00734 void QSplitter::setResizeMode(QWidget *w, ResizeMode mode)
00735 {
00736     /*
00737         Internal comment:
00738 
00739         This function tries to simulate the Qt 3.x ResizeMode
00740         behavior using QSizePolicy stretch factors. This isn't easy,
00741         because the default \l ResizeMode was \l Stretch, not \l
00742         KeepSize, whereas the default stetch factor is 0.
00743 
00744         So what we do is this: When the user calls setResizeMode()
00745         the first time, we iterate through all the child widgets and
00746         set their stretch factors to 1. Later on, if children are
00747         added (using addWidget()), their stretch factors are also set
00748         to 1.
00749 
00750         There is just one problem left: Often, setResizeMode() is
00751         called \e{before} addWidget(), because addWidget() is called
00752         from the event loop. In that case, we use a special value,
00753         243, instead of 0 to prevent 0 from being overwritten with 1
00754         in addWidget(). This is a wicked hack, but fortunately it
00755         only occurs as a result of calling a \c QT3_SUPPORT function.
00756     */
00757 
00758     Q_D(QSplitter);
00759     bool metWidget = false;
00760     if (!d->compatMode) {
00761         d->compatMode = true;
00762         for (int i = 0; i < d->list.size(); ++i) {
00763             QSplitterLayoutStruct *s = d->list.at(i);
00764             if (s->widget == w)
00765                 metWidget = true;
00766             if (getStretch(s->widget) == 0)
00767                 setStretch(s->widget, 1);
00768         }
00769     }
00770     int sf;
00771     if (mode == KeepSize)
00772         sf = metWidget ? 0 : 243;
00773     else
00774         sf = 1;
00775     setStretch(w, sf);
00776 }
00777 
00782 QSplitter::QSplitter(QWidget *parent, const char *name)
00783     : QFrame(*new QSplitterPrivate, parent)
00784 {
00785     Q_D(QSplitter);
00786     setObjectName(QString::fromAscii(name));
00787     d->orient = Qt::Horizontal;
00788     d->init();
00789 }
00790 
00791 
00796 QSplitter::QSplitter(Qt::Orientation orientation, QWidget *parent, const char *name)
00797     : QFrame(*new QSplitterPrivate, parent)
00798 {
00799     Q_D(QSplitter);
00800     setObjectName(QString::fromAscii(name));
00801     d->orient = orientation;
00802     d->init();
00803 }
00804 #endif
00805 
00806 /*
00807     Inserts the widget \a w at position \a index in the splitter's list of widgets.
00808 
00809     If \a w is already in the splitter, it will be moved to the new position.
00810 */
00811 
00812 QSplitterLayoutStruct *QSplitterPrivate::insertWidget(int index, QWidget *w)
00813 {
00814     Q_Q(QSplitter);
00815     QSplitterLayoutStruct *sls = 0;
00816     int i;
00817     int last = list.count();
00818     for (i = 0; i < list.size(); ++i) {
00819         QSplitterLayoutStruct *s = list.at(i);
00820         if (s->widget == w) {
00821             sls = s;
00822             --last;
00823             break;
00824         }
00825     }
00826     if (index < 0 || index > last)
00827         index = last;
00828 
00829     if (sls) {
00830         list.move(i,index);
00831     } else {
00832         QSplitterHandle *newHandle = 0;
00833         sls = new QSplitterLayoutStruct;
00834         QString tmp = QLatin1String("qt_splithandle_");
00835         tmp += w->objectName();
00836         newHandle = q->createHandle();
00837         newHandle->setObjectName(tmp);
00838         sls->handle = newHandle;
00839         sls->widget = w;
00840         list.insert(index,sls);
00841 
00842         if (newHandle && q->isVisible())
00843             newHandle->show(); // will trigger sending of post events
00844 
00845 #ifdef QT3_SUPPORT
00846         if (compatMode) {
00847             int sf = getStretch(sls->widget);
00848             if (sf == 243)
00849                 setStretch(sls->widget, 0);
00850             else if (sf == 0)
00851                 setStretch(sls->widget, 1);
00852         }
00853 #endif
00854     }
00855     return sls;
00856 }
00857 
00913 QSplitter::QSplitter(QWidget *parent)
00914     : QFrame(*new QSplitterPrivate, parent)
00915 {
00916     Q_D(QSplitter);
00917     d->orient = Qt::Horizontal;
00918     d->init();
00919 }
00920 
00921 
00927 QSplitter::QSplitter(Qt::Orientation orientation, QWidget *parent)
00928     : QFrame(*new QSplitterPrivate, parent)
00929 {
00930     Q_D(QSplitter);
00931     d->orient = orientation;
00932     d->init();
00933 }
00934 
00935 
00940 QSplitter::~QSplitter()
00941 {
00942     Q_D(QSplitter);
00943     while (!d->list.isEmpty())
00944         delete d->list.takeFirst();
00945 }
00946 
00951 void QSplitter::refresh()
00952 {
00953     Q_D(QSplitter);
00954     d->recalc(true);
00955 }
00956 
00968 void QSplitter::setOrientation(Qt::Orientation orientation)
00969 {
00970     Q_D(QSplitter);
00971     if (d->orient == orientation)
00972         return;
00973 
00974     if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) {
00975         QSizePolicy sp = sizePolicy();
00976         sp.transpose();
00977         setSizePolicy(sp);
00978         setAttribute(Qt::WA_WState_OwnSizePolicy, false);
00979     }
00980 
00981     d->orient = orientation;
00982 
00983     for (int i = 0; i < d->list.size(); ++i) {
00984         QSplitterLayoutStruct *s = d->list.at(i);
00985         s->handle->setOrientation(orientation);
00986     }
00987     d->recalc(isVisible());
00988 }
00989 
00990 Qt::Orientation QSplitter::orientation() const
00991 {
00992     Q_D(const QSplitter);
00993     return d->orient;
00994 }
00995 
01007 void QSplitter::setChildrenCollapsible(bool collapse)
01008 {
01009     Q_D(QSplitter);
01010     d->childrenCollapsible = collapse;
01011 }
01012 
01013 bool QSplitter::childrenCollapsible() const
01014 {
01015     Q_D(const QSplitter);
01016     return d->childrenCollapsible;
01017 }
01018 
01032 void QSplitter::setCollapsible(int index, bool collapse)
01033 {
01034     Q_D(QSplitter);
01035 
01036     if (index < 0 || index >= d->list.size()) {
01037         qWarning("QSplitter::setCollapsible: Index %d out of range", index);
01038         return;
01039     }
01040     d->list.at(index)->collapsible = collapse ? 1 : 0;
01041 }
01042 
01046 bool QSplitter::isCollapsible(int index) const
01047 {
01048     Q_D(const QSplitter);
01049     if (index < 0 || index >= d->list.size()) {
01050         qWarning("QSplitter::isCollapsible: Index %d out of range", index);
01051         return false;
01052     }
01053     return d->list.at(index)->collapsible;
01054 }
01055 
01059 void QSplitter::resizeEvent(QResizeEvent *)
01060 {
01061     Q_D(QSplitter);
01062     d->doResize();
01063 }
01064 
01073 void QSplitter::addWidget(QWidget *widget)
01074 {
01075     Q_D(QSplitter);
01076     insertWidget(d->list.count(), widget);
01077 }
01078 
01089 void QSplitter::insertWidget(int index, QWidget *widget)
01090 {
01091     Q_D(QSplitter);
01092     QBoolBlocker b(d->blockChildAdd);
01093     bool needShow = isVisible() &&
01094                     !(widget->isHidden() && widget->testAttribute(Qt::WA_WState_ExplicitShowHide));
01095     if (widget->parentWidget() != this)
01096         widget->setParent(this);
01097     if (needShow)
01098         widget->show();
01099     d->insertWidget(index, widget);
01100     d->recalc(isVisible());
01101 }
01102 
01115 int QSplitter::indexOf(QWidget *w) const
01116 {
01117     Q_D(const QSplitter);
01118     for (int i = 0; i < d->list.size(); ++i) {
01119         QSplitterLayoutStruct *s = d->list.at(i);
01120         if (s->widget == w || s->handle == w)
01121             return i;
01122     }
01123     return -1;
01124 }
01125 
01133 QSplitterHandle *QSplitter::createHandle()
01134 {
01135     Q_D(QSplitter);
01136     return new QSplitterHandle(d->orient, this);
01137 }
01138 
01150 QSplitterHandle *QSplitter::handle(int index) const
01151 {
01152     Q_D(const QSplitter);
01153     if (index < 0 || index >= d->list.size())
01154         return 0;
01155     return d->list.at(index)->handle;
01156 }
01157 
01163 QWidget *QSplitter::widget(int index) const
01164 {
01165     Q_D(const QSplitter);
01166     if (index < 0 || index >= d->list.size())
01167         return 0;
01168     return d->list.at(index)->widget;
01169 }
01170 
01176 int QSplitter::count() const
01177 {
01178     Q_D(const QSplitter);
01179     return d->list.count();
01180 }
01181 
01197 void QSplitter::childEvent(QChildEvent *c)
01198 {
01199     Q_D(QSplitter);
01200     if (!c->child()->isWidgetType())
01201         return;
01202     QWidget *w = static_cast<QWidget*>(c->child());
01203 
01204     if (c->added() && !d->blockChildAdd && !w->isWindow() && !d->findWidget(w)) {
01205         addWidget(w);
01206     } else  if (c->type() == QEvent::ChildRemoved) {
01207         for (int i = 0; i < d->list.size(); ++i) {
01208             QSplitterLayoutStruct *s = d->list.at(i);
01209             if (s->widget == w) {
01210                 d->list.removeAt(i);
01211                 delete s;
01212                 d->recalc(isVisible());
01213                 return;
01214             }
01215         }
01216     }
01217 }
01218 
01219 
01225 void QSplitter::setRubberBand(int pos)
01226 {
01227     Q_D(QSplitter);
01228     if (pos < 0) {
01229         if (d->rubberBand)
01230             d->rubberBand->hide();
01231         return;
01232     }
01233     QRect r = contentsRect();
01234     const int rBord = 3; // customizable?
01235     int hw = handleWidth();
01236     if (!d->rubberBand) {
01237         QBoolBlocker block(d->blockChildAdd);
01238         d->rubberBand = new QRubberBand(QRubberBand::Line, this);
01239     }
01240     if (d->orient == Qt::Horizontal)
01241         d->rubberBand->setGeometry(QRect(QPoint(pos + hw / 2 - rBord, r.y()),
01242                                          QSize(2 * rBord, r.height())));
01243     else
01244         d->rubberBand->setGeometry(QRect(QPoint(r.x(), pos + hw / 2 - rBord),
01245                                    QSize(r.width(), 2 * rBord)));
01246     if (!d->rubberBand->isVisible())
01247         d->rubberBand->show();
01248 }
01249 
01254 bool QSplitter::event(QEvent *e)
01255 {
01256     Q_D(QSplitter);
01257     switch (e->type()) {
01258     case QEvent::Hide:
01259         // Reset firstShow to false here since things can be done to the splitter in between
01260         if (!d->firstShow)
01261             d->firstShow = true;
01262         break;
01263     case QEvent::Show:
01264         if (!d->firstShow)
01265             break;
01266         d->firstShow = false;
01267         // fall through
01268     case QEvent::HideToParent:
01269     case QEvent::ShowToParent:
01270     case QEvent::LayoutRequest:
01271 #ifdef QT3_SUPPORT
01272     case QEvent::LayoutHint:
01273 #endif
01274         d->recalc(isVisible());
01275         break;
01276     default:
01277         ;
01278     }
01279     return QWidget::event(e);
01280 }
01281 
01306 void QSplitter::moveSplitter(int pos, int index)
01307 {
01308     Q_D(QSplitter);
01309     QSplitterLayoutStruct *s = d->list.at(index);
01310     int farMin;
01311     int min;
01312     int max;
01313     int farMax;
01314 
01315 #ifdef QSPLITTER_DEBUG
01316     int debugp = pos;
01317 #endif
01318 
01319     pos = d->adjustPos(pos, index, &farMin, &min, &max, &farMax);
01320     int oldP = d->pick(s->rect.topLeft());
01321 #ifdef QSPLITTER_DEBUG
01322     qDebug() << "QSplitter::moveSplitter" << debugp << index << "adjusted" << pos << "oldP" << oldP;
01323 #endif
01324 
01325     QVarLengthArray<int, 32> poss(d->list.count());
01326     QVarLengthArray<int, 32> ws(d->list.count());
01327     bool upLeft;
01328 
01329     d->doMove(false, pos, index, +1, (d->collapsible(s) && (pos > max)), poss.data(), ws.data());
01330     d->doMove(true, pos, index - 1, +1, (d->collapsible(index - 1) && (pos < min)), poss.data(), ws.data());
01331     upLeft = (pos < oldP);
01332 
01333     int wid, delta, count = d->list.count();
01334     if (upLeft) {
01335         wid = 0;
01336         delta = 1;
01337     } else {
01338         wid = count - 1;
01339         delta = -1;
01340     }
01341     for (; wid >= 0 && wid < count; wid += delta) {
01342         QSplitterLayoutStruct *sls = d->list.at( wid );
01343         if (!sls->widget->isHidden())
01344             d->setGeo(sls, poss[wid], ws[wid], true);
01345     }
01346     d->storeSizes();
01347 
01348     emit splitterMoved(pos, index);
01349 }
01350 
01351 
01357 void QSplitter::getRange(int index, int *min, int *max) const
01358 {
01359     Q_D(const QSplitter);
01360     d->getRange(index, min, 0, 0, max);
01361 }
01362 
01363 
01375 int QSplitter::closestLegalPosition(int pos, int index)
01376 {
01377     Q_D(QSplitter);
01378     int x, i, n, u;
01379     return d->adjustPos(pos, index, &u, &n, &i, &x);
01380 }
01381 
01389 bool QSplitter::opaqueResize() const
01390 {
01391     Q_D(const QSplitter);
01392     return d->opaque;
01393 }
01394 
01395 
01396 void QSplitter::setOpaqueResize(bool on)
01397 {
01398     Q_D(QSplitter);
01399     d->opaque = on;
01400 }
01401 
01402 #ifdef QT3_SUPPORT
01403 
01467 #endif
01468 
01472 QSize QSplitter::sizeHint() const
01473 {
01474     Q_D(const QSplitter);
01475     ensurePolished();
01476     int l = 0;
01477     int t = 0;
01478     QObjectList childList = children();
01479     for (int i = 0; i < childList.size(); ++i) {
01480         if (QWidget *w = qobject_cast<QWidget *>(childList.at(i))) {
01481             if (w->isHidden())
01482                 continue;
01483             QSize s = w->sizeHint();
01484             if (s.isValid()) {
01485                 l += d->pick(s);
01486                 t = qMax(t, d->trans(s));
01487             }
01488         }
01489     }
01490     return orientation() == Qt::Horizontal ? QSize(l, t) : QSize(t, l);
01491 }
01492 
01493 
01498 QSize QSplitter::minimumSizeHint() const
01499 {
01500     Q_D(const QSplitter);
01501     ensurePolished();
01502     int l = 0;
01503     int t = 0;
01504     QObjectList childList = children();
01505     for (int i = 0; i < childList.size(); ++i) {
01506         if (QWidget *w = qobject_cast<QWidget *>(childList.at(i))) {
01507             if (w->isHidden())
01508                 continue;
01509             QSize s = qSmartMinSize(w);
01510             if (s.isValid()) {
01511                 l += d->pick(s);
01512                 t = qMax(t, d->trans(s));
01513             }
01514         }
01515     }
01516     return orientation() == Qt::Horizontal ? QSize(l, t) : QSize(t, l);
01517 }
01518 
01519 
01540 QList<int> QSplitter::sizes() const
01541 {
01542     Q_D(const QSplitter);
01543     ensurePolished();
01544 
01545     QList<int> list;
01546     for (int i = 0; i < d->list.size(); ++i) {
01547         QSplitterLayoutStruct *s = d->list.at(i);
01548         list.append(d->pick(s->rect.size()));
01549     }
01550     return list;
01551 }
01552 
01569 void QSplitter::setSizes(const QList<int> &list)
01570 {
01571     Q_D(QSplitter);
01572     int j = 0;
01573 
01574     for (int i = 0; i < d->list.size(); ++i) {
01575         QSplitterLayoutStruct *s = d->list.at(i);
01576 
01577         s->collapsed = false;
01578         s->sizer = qMax(list.value(j++), 0);
01579         int smartMinSize = d->pick(qSmartMinSize(s->widget));
01580 
01581         // Make sure that we reset the collapsed state.
01582         if (s->sizer == 0) {
01583             if (d->collapsible(s) && smartMinSize > 0) {
01584                 s->collapsed = true;
01585             } else {
01586                 s->sizer = smartMinSize;
01587             }
01588         } else {
01589             if (s->sizer < smartMinSize)
01590                 s->sizer = smartMinSize;
01591         }
01592     }
01593     d->doResize();
01594 }
01595 
01601 int QSplitter::handleWidth() const
01602 {
01603     Q_D(const QSplitter);
01604     if (d->handleWidth > 0) {
01605         return d->handleWidth;
01606     } else {
01607         return style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this);
01608     }
01609 }
01610 
01611 void QSplitter::setHandleWidth(int width)
01612 {
01613     Q_D(QSplitter);
01614     d->handleWidth = width;
01615     d->updateHandles();
01616 }
01617 
01621 void QSplitter::changeEvent(QEvent *ev)
01622 {
01623     Q_D(QSplitter);
01624     if(ev->type() == QEvent::StyleChange)
01625         d->updateHandles();
01626     QFrame::changeEvent(ev);
01627 }
01628 
01629 static const qint32 SplitterMagic = 0xff;
01630 
01645 QByteArray QSplitter::saveState() const
01646 {
01647     int version = 0;
01648     QByteArray data;
01649     QDataStream stream(&data, QIODevice::WriteOnly);
01650 
01651     stream << qint32(SplitterMagic);
01652     stream << qint32(version);
01653     stream << sizes();
01654     stream << childrenCollapsible();
01655     stream << qint32(handleWidth());
01656     stream << opaqueResize();
01657     stream << qint32(orientation());
01658     return data;
01659 }
01660 
01680 bool QSplitter::restoreState(const QByteArray &state)
01681 {
01682     Q_D(QSplitter);
01683     int version = 0;
01684     QByteArray sd = state;
01685     QDataStream stream(&sd, QIODevice::ReadOnly);
01686     QList<int> list;
01687     bool b;
01688     qint32 i;
01689     qint32 marker;
01690     qint32 v;
01691 
01692     stream >> marker;
01693     stream >> v;
01694     if (marker != SplitterMagic || v != version)
01695         return false;
01696 
01697     stream >> list;
01698     setSizes(list);
01699 
01700     stream >> b;
01701     setChildrenCollapsible(b);
01702 
01703     stream >> i;
01704     setHandleWidth(i);
01705 
01706     stream >> b;
01707     setOpaqueResize(b);
01708 
01709     stream >> i;
01710     setOrientation(Qt::Orientation(i));
01711     d->doResize();
01712 
01713     return true;
01714 }
01715 
01732 void QSplitter::setStretchFactor(int index, int stretch)
01733 {
01734     Q_D(QSplitter);
01735     if (index <= -1 || index >= d->list.count())
01736         return;
01737 
01738     QWidget *widget = d->list.at(index)->widget;
01739     QSizePolicy sp = widget->sizePolicy();
01740     sp.setHorizontalStretch(stretch);
01741     sp.setVerticalStretch(stretch);
01742     widget->setSizePolicy(sp);
01743 }
01744 
01745 
01746 //#ifdef QT3_SUPPORT
01747 #ifndef QT_NO_TEXTSTREAM
01748 
01755 QTextStream& operator<<(QTextStream& ts, const QSplitter& splitter)
01756 {
01757     ts << splitter.saveState() << endl;
01758     return ts;
01759 }
01760 
01768 QTextStream& operator>>(QTextStream& ts, QSplitter& splitter)
01769 {
01770     QString line = ts.readLine();
01771     line = line.simplified();
01772     line.replace(QLatin1Char(' '), QString());
01773     line = line.toUpper();
01774 
01775     splitter.restoreState(line.toAscii());
01776     return ts;
01777 }
01778 #endif // QT_NO_TEXTSTREAM
01779 //#endif // QT3_SUPPORT
01780 
01781 #endif // QT_NO_SPLITTER

Generated on Thu Mar 15 11:57:06 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1