src/gui/kernel/qlayoutengine.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 "qlayout.h"
00025 #include "private/qlayoutengine_p.h"
00026 
00027 #include "qvector.h"
00028 #include "qwidget.h"
00029 
00030 #include <qlist.h>
00031 #include <qalgorithms.h>
00032 
00033 #include <qdebug.h>
00034 //#define QLAYOUT_EXTRA_DEBUG
00035 
00036 typedef qint64 Fixed;
00037 static inline Fixed toFixed(int i) { return (Fixed)i * 256; }
00038 static inline int fRound(Fixed i) {
00039     return (i % 256 < 128) ? i / 256 : 1 + i / 256;
00040 }
00041 
00042 /*
00043   This is the main workhorse of the QGridLayout. It portions out
00044   available space to the chain's children.
00045 
00046   The calculation is done in fixed point: "fixed" variables are
00047   scaled by a factor of 256.
00048 
00049   If the layout runs "backwards" (i.e. RightToLeft or Up) the layout
00050   is computed mirror-reversed, and it's the caller's responsibility
00051   do reverse the values before use.
00052 
00053   chain contains input and output parameters describing the geometry.
00054   count is the count of items in the chain; pos and space give the
00055   interval (relative to parentWidget topLeft).
00056 */
00057 void qGeomCalc(QVector<QLayoutStruct> &chain, int start, int count,
00058                          int pos, int space, int spacer)
00059 {
00060     int cHint = 0;
00061     int cMin = 0;
00062     int cMax = 0;
00063     int sumStretch = 0;
00064     int spacerCount = 0;
00065 
00066     bool wannaGrow = false; // anyone who really wants to grow?
00067     //    bool canShrink = false; // anyone who could be persuaded to shrink?
00068 
00069     bool allEmptyNonstretch = true;
00070     int i;
00071     for (i = start; i < start + count; i++) {
00072         chain[i].done = false;
00073         cHint += chain[i].smartSizeHint();
00074         cMin += chain[i].minimumSize;
00075         cMax += chain[i].maximumSize;
00076         sumStretch += chain[i].stretch;
00077         if (!chain[i].empty)
00078             spacerCount++;
00079         wannaGrow = wannaGrow || chain[i].expansive || chain[i].stretch > 0;
00080         allEmptyNonstretch = allEmptyNonstretch && !wannaGrow && chain[i].empty;
00081     }
00082 
00083     int extraspace = 0;
00084     if (spacerCount)
00085         spacerCount--; // only spacers between things
00086 
00087     if (space < cMin + spacerCount * spacer) {
00088         /*
00089           Less space than minimumSize; take from the biggest first
00090         */
00091 
00092         int minSize =  cMin + spacerCount * spacer;
00093 
00094         //shrink the spacers proportionally
00095         spacer = minSize > 0 ? (spacer * space) / minSize : 0;
00096 
00097         QList<int> list;
00098 
00099         for (i = start; i < start+count; i++) {
00100             list << chain[i].minimumSize;
00101         }
00102 
00103         qSort(list);
00104 
00105         int space_left = space - spacerCount*spacer;
00106 
00107         int sum = 0;
00108         int idx = 0;
00109         int space_used=0;
00110         int current = 0;
00111         while (idx < count && space_used < space_left) {
00112             current = list.at(idx);
00113             space_used = sum + current * (count - idx);
00114             sum += current;
00115             ++idx;
00116         }
00117         --idx;
00118         int deficit = space_used - space_left;
00119 
00120         int items = count - idx;
00121         int maxval = current - deficit/items;
00122 
00123         for (i = start; i < start+count; i++) {
00124             chain[i].size = qMin(chain[i].minimumSize, maxval);
00125             chain[i].done = true;
00126         }
00127     } else if (space < cHint + spacerCount*spacer) {
00128         /*
00129           Less space than smartSizeHint(), but more than minimumSize.
00130           Currently take space equally from each, as in Qt 2.x.
00131           Commented-out lines will give more space to stretchier
00132           items.
00133         */
00134         int n = count;
00135         int space_left = space - spacerCount*spacer;
00136         int overdraft = cHint - space_left;
00137 
00138         // first give to the fixed ones:
00139         for (i = start; i < start + count; i++) {
00140             if (!chain[i].done
00141                  && chain[i].minimumSize >= chain[i].smartSizeHint()) {
00142                 chain[i].size = chain[i].smartSizeHint();
00143                 chain[i].done = true;
00144                 space_left -= chain[i].smartSizeHint();
00145                 // sumStretch -= chain[i].stretch;
00146                 n--;
00147             }
00148         }
00149         bool finished = n == 0;
00150         while (!finished) {
00151             finished = true;
00152             Fixed fp_over = toFixed(overdraft);
00153             Fixed fp_w = 0;
00154 
00155             for (i = start; i < start+count; i++) {
00156                 if (chain[i].done)
00157                     continue;
00158                 // if (sumStretch <= 0)
00159                 fp_w += fp_over / n;
00160                 // else
00161                 //    fp_w += (fp_over * chain[i].stretch) / sumStretch;
00162                 int w = fRound(fp_w);
00163                 chain[i].size = chain[i].smartSizeHint() - w;
00164                 fp_w -= toFixed(w); // give the difference to the next
00165                 if (chain[i].size < chain[i].minimumSize) {
00166                     chain[i].done = true;
00167                     chain[i].size = chain[i].minimumSize;
00168                     finished = false;
00169                     overdraft -= (chain[i].smartSizeHint()
00170                                    - chain[i].minimumSize);
00171                     // sumStretch -= chain[i].stretch;
00172                     n--;
00173                     break;
00174                 }
00175             }
00176         }
00177     } else { // extra space
00178         int n = count;
00179         int space_left = space - spacerCount*spacer;
00180         // first give to the fixed ones, and handle non-expansiveness
00181         for (i = start; i < start + count; i++) {
00182             if (!chain[i].done
00183                 && (chain[i].maximumSize <= chain[i].smartSizeHint()
00184                     || (wannaGrow && !chain[i].expansive && chain[i].stretch == 0)
00185                     || (!allEmptyNonstretch && chain[i].empty &&
00186                         !chain[i].expansive && chain[i].stretch == 0))) {
00187                 chain[i].size = chain[i].smartSizeHint();
00188                 chain[i].done = true;
00189                 space_left -= chain[i].size;
00190                 sumStretch -= chain[i].stretch;
00191                 n--;
00192             }
00193         }
00194         extraspace = space_left;
00195 
00196         /*
00197           Do a trial distribution and calculate how much it is off.
00198           If there are more deficit pixels than surplus pixels, give
00199           the minimum size items what they need, and repeat.
00200           Otherwise give to the maximum size items, and repeat.
00201 
00202           Paul Olav Tvete has a wonderful mathematical proof of the
00203           correctness of this principle, but unfortunately this
00204           comment is too small to contain it.
00205         */
00206         int surplus, deficit;
00207         do {
00208             surplus = deficit = 0;
00209             Fixed fp_space = toFixed(space_left);
00210             Fixed fp_w = 0;
00211             for (i = start; i < start+count; i++) {
00212                 if (chain[i].done)
00213                     continue;
00214                 extraspace = 0;
00215                 if (sumStretch <= 0)
00216                     fp_w += fp_space / n;
00217                 else
00218                     fp_w += (fp_space * chain[i].stretch) / sumStretch;
00219                 int w = fRound(fp_w);
00220                 chain[i].size = w;
00221                 fp_w -= toFixed(w); // give the difference to the next
00222                 if (w < chain[i].smartSizeHint()) {
00223                     deficit +=  chain[i].smartSizeHint() - w;
00224                 } else if (w > chain[i].maximumSize) {
00225                     surplus += w - chain[i].maximumSize;
00226                 }
00227             }
00228             if (deficit > 0 && surplus <= deficit) {
00229                 // give to the ones that have too little
00230                 for (i = start; i < start+count; i++) {
00231                     if (!chain[i].done &&
00232                          chain[i].size < chain[i].smartSizeHint()) {
00233                         chain[i].size = chain[i].smartSizeHint();
00234                         chain[i].done = true;
00235                         space_left -= chain[i].smartSizeHint();
00236                         sumStretch -= chain[i].stretch;
00237                         n--;
00238                     }
00239                 }
00240             }
00241             if (surplus > 0 && surplus >= deficit) {
00242                 // take from the ones that have too much
00243                 for (i = start; i < start+count; i++) {
00244                     if (!chain[i].done &&
00245                          chain[i].size > chain[i].maximumSize) {
00246                         chain[i].size = chain[i].maximumSize;
00247                         chain[i].done = true;
00248                         space_left -= chain[i].maximumSize;
00249                         sumStretch -= chain[i].stretch;
00250                         n--;
00251                     }
00252                 }
00253             }
00254         } while (n > 0 && surplus != deficit);
00255         if (n == 0)
00256             extraspace = space_left;
00257     }
00258 
00259     /*
00260       As a last resort, we distribute the unwanted space equally
00261       among the spacers (counting the start and end of the chain). We
00262       could, but don't, attempt a sub-pixel allocation of the extra
00263       space.
00264     */
00265     int extra = extraspace / (spacerCount + 2);
00266     int p = pos + extra;
00267     for (i = start; i < start+count; i++) {
00268         chain[i].pos = p;
00269         p = p + chain[i].size;
00270         if (!chain[i].empty)
00271             p += spacer+extra;
00272     }
00273 
00274 
00275 #ifdef QLAYOUT_EXTRA_DEBUG
00276     qDebug() << "qGeomCalc" << "start" << start <<  "count" << count <<  "pos" << pos <<  "space" << space <<  "spacer" << spacer;
00277     for (i = start; i < start + count; i++) {
00278         qDebug() << i << ":" << chain[i].minimumSize << chain[i].smartSizeHint() << chain[i].maximumSize
00279                  << "stretch" << chain[i].stretch << "empty" << chain[i].empty << "expansive" << chain[i].expansive;
00280         qDebug() << "result pos" << chain[i].pos << "size" << chain[i].size;
00281     }
00282 #endif
00283 }
00284 
00285 Q_GUI_EXPORT QSize qSmartMinSize(const QWidgetItem *i)
00286 {
00287     QWidget *w = ((QWidgetItem *)i)->widget();
00288 
00289     QSize s(0, 0);
00290     QSize msh(w->minimumSizeHint());
00291     QSize sh(w->sizeHint());
00292 
00293     if (w->sizePolicy().horizontalPolicy() != QSizePolicy::Ignored) {
00294         if (w->sizePolicy().horizontalPolicy() & QSizePolicy::ShrinkFlag)
00295             s.setWidth(msh.width());
00296         else
00297             s.setWidth(qMax(sh.width(), msh.width()));
00298     }
00299 
00300     if (w->sizePolicy().verticalPolicy() != QSizePolicy::Ignored) {
00301         if (w->sizePolicy().verticalPolicy() & QSizePolicy::ShrinkFlag) {
00302             s.setHeight(msh.height());
00303         } else {
00304             s.setHeight(qMax(sh.height(), msh.height()));
00305         }
00306     }
00307 
00308     s = s.boundedTo(w->maximumSize());
00309     QSize min = w->minimumSize();
00310     if (min.width() > 0)
00311         s.setWidth(min.width());
00312     if (min.height() > 0)
00313         s.setHeight(min.height());
00314 
00315     return s.expandedTo(QSize(0,0));
00316 }
00317 
00318 Q_GUI_EXPORT QSize qSmartMinSize(const QWidget *w)
00319 {
00320     QWidgetItem item(const_cast<QWidget *>(w));
00321     return qSmartMinSize(&item);
00322 }
00323 
00324 Q_GUI_EXPORT QSize qSmartMaxSize(const QWidgetItem *i, Qt::Alignment align)
00325 {
00326     QWidget *w = ((QWidgetItem*)i)->widget();
00327     if (align & Qt::AlignHorizontal_Mask && align & Qt::AlignVertical_Mask)
00328         return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX);
00329     QSize s = w->maximumSize();
00330     if (s.width() == QWIDGETSIZE_MAX && !(align & Qt::AlignHorizontal_Mask))
00331         if (!(w->sizePolicy().horizontalPolicy() & QSizePolicy::GrowFlag))
00332             s.setWidth(w->sizeHint().width());
00333 
00334     if (s.height() == QWIDGETSIZE_MAX && !(align & Qt::AlignVertical_Mask))
00335         if (!(w->sizePolicy().verticalPolicy() & QSizePolicy::GrowFlag))
00336             s.setHeight(w->sizeHint().height());
00337 
00338     s = s.expandedTo(w->minimumSize());
00339 
00340     if (align & Qt::AlignHorizontal_Mask)
00341         s.setWidth(QLAYOUTSIZE_MAX);
00342     if (align & Qt::AlignVertical_Mask)
00343         s.setHeight(QLAYOUTSIZE_MAX);
00344     return s;
00345 }
00346 
00347 Q_GUI_EXPORT QSize qSmartMaxSize(const QWidget *w, Qt::Alignment align)
00348 {
00349     QWidgetItem item(const_cast<QWidget *>(w));
00350     return qSmartMaxSize(&item, align);
00351 }
00352 

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