~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/gui/kernel/qlayoutengine.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the gui module of the Qt Toolkit.
 
6
**
 
7
** This file may be distributed under the terms of the Q Public License
 
8
** as defined by Trolltech AS of Norway and appearing in the file
 
9
** LICENSE.QPL included in the packaging of this file.
 
10
**
 
11
** This file may be distributed and/or modified under the terms of the
 
12
** GNU General Public License version 2 as published by the Free Software
 
13
** Foundation and appearing in the file LICENSE.GPL included in the
 
14
** packaging of this file.
 
15
**
 
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
17
**   information about Qt Commercial License Agreements.
 
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
#include "qlayout.h"
 
30
#include "private/qlayoutengine_p.h"
 
31
 
 
32
#include "qvector.h"
 
33
#include "qwidget.h"
 
34
 
 
35
#include <qlist.h>
 
36
#include <qalgorithms.h>
 
37
 
 
38
#ifndef QT_NO_LAYOUT
 
39
 
 
40
typedef qint64 Fixed;
 
41
static inline Fixed toFixed(int i) { return (Fixed)i * 256; }
 
42
static inline int fRound(Fixed i) {
 
43
    return (i % 256 < 128) ? i / 256 : 1 + i / 256;
 
44
}
 
45
 
 
46
/*
 
47
  This is the main workhorse of the QGridLayout. It portions out
 
48
  available space to the chain's children.
 
49
 
 
50
  The calculation is done in fixed point: "fixed" variables are
 
51
  scaled by a factor of 256.
 
52
 
 
53
  If the layout runs "backwards" (i.e. RightToLeft or Up) the layout
 
54
  is computed mirror-reversed, and it's the caller's responsibility
 
55
  do reverse the values before use.
 
56
 
 
57
  chain contains input and output parameters describing the geometry.
 
58
  count is the count of items in the chain; pos and space give the
 
59
  interval (relative to parentWidget topLeft).
 
60
*/
 
61
void qGeomCalc(QVector<QLayoutStruct> &chain, int start, int count,
 
62
                         int pos, int space, int spacer)
 
63
{
 
64
    int cHint = 0;
 
65
    int cMin = 0;
 
66
    int cMax = 0;
 
67
    int sumStretch = 0;
 
68
    int spacerCount = 0;
 
69
 
 
70
    bool wannaGrow = false; // anyone who really wants to grow?
 
71
    //    bool canShrink = false; // anyone who could be persuaded to shrink?
 
72
 
 
73
    int i;
 
74
    for (i = start; i < start + count; i++) {
 
75
        chain[i].done = false;
 
76
        cHint += chain[i].smartSizeHint();
 
77
        cMin += chain[i].minimumSize;
 
78
        cMax += chain[i].maximumSize;
 
79
        sumStretch += chain[i].stretch;
 
80
        if (!chain[i].empty)
 
81
            spacerCount++;
 
82
        wannaGrow = wannaGrow || chain[i].expansive || chain[i].stretch > 0;
 
83
    }
 
84
 
 
85
    int extraspace = 0;
 
86
    if (spacerCount)
 
87
        spacerCount--; // only spacers between things
 
88
 
 
89
    if (space < cMin + spacerCount * spacer) {
 
90
        /*
 
91
          Less space than minimumSize; take from the biggest first
 
92
        */
 
93
 
 
94
        int minSize =  cMin + spacerCount * spacer;
 
95
 
 
96
        //shrink the spacers proportionally
 
97
        spacer = minSize > 0 ? (spacer * space) / minSize : 0;
 
98
 
 
99
        QList<int> list;
 
100
 
 
101
        for (i = start; i < start+count; i++) {
 
102
            list << chain[i].minimumSize;
 
103
        }
 
104
 
 
105
        qSort(list);
 
106
 
 
107
        int space_left = space - spacerCount*spacer;
 
108
 
 
109
        int sum = 0;
 
110
        int idx = 0;
 
111
        int space_used=0;
 
112
        int current = 0;
 
113
        while (idx < count && space_used < space_left) {
 
114
            current = list.at(idx);
 
115
            space_used = sum + current * (count - idx);
 
116
            sum += current;
 
117
            ++idx;
 
118
        }
 
119
        --idx;
 
120
        int deficit = space_used - space_left;
 
121
 
 
122
        int items = count - idx;
 
123
        int maxval = current - deficit/items;
 
124
 
 
125
        for (i = start; i < start+count; i++) {
 
126
            chain[i].size = qMin(chain[i].minimumSize, maxval);
 
127
            chain[i].done = true;
 
128
        }
 
129
    } else if (space < cHint + spacerCount*spacer) {
 
130
        /*
 
131
          Less space than smartSizeHint(), but more than minimumSize.
 
132
          Currently take space equally from each, as in Qt 2.x.
 
133
          Commented-out lines will give more space to stretchier
 
134
          items.
 
135
        */
 
136
        int n = count;
 
137
        int space_left = space - spacerCount*spacer;
 
138
        int overdraft = cHint - space_left;
 
139
 
 
140
        // first give to the fixed ones:
 
141
        for (i = start; i < start + count; i++) {
 
142
            if (!chain[i].done
 
143
                 && chain[i].minimumSize >= chain[i].smartSizeHint()) {
 
144
                chain[i].size = chain[i].smartSizeHint();
 
145
                chain[i].done = true;
 
146
                space_left -= chain[i].smartSizeHint();
 
147
                // sumStretch -= chain[i].stretch;
 
148
                n--;
 
149
            }
 
150
        }
 
151
        bool finished = n == 0;
 
152
        while (!finished) {
 
153
            finished = true;
 
154
            Fixed fp_over = toFixed(overdraft);
 
155
            Fixed fp_w = 0;
 
156
 
 
157
            for (i = start; i < start+count; i++) {
 
158
                if (chain[i].done)
 
159
                    continue;
 
160
                // if (sumStretch <= 0)
 
161
                fp_w += fp_over / n;
 
162
                // else
 
163
                //    fp_w += (fp_over * chain[i].stretch) / sumStretch;
 
164
                int w = fRound(fp_w);
 
165
                chain[i].size = chain[i].smartSizeHint() - w;
 
166
                fp_w -= toFixed(w); // give the difference to the next
 
167
                if (chain[i].size < chain[i].minimumSize) {
 
168
                    chain[i].done = true;
 
169
                    chain[i].size = chain[i].minimumSize;
 
170
                    finished = false;
 
171
                    overdraft -= (chain[i].smartSizeHint()
 
172
                                   - chain[i].minimumSize);
 
173
                    // sumStretch -= chain[i].stretch;
 
174
                    n--;
 
175
                    break;
 
176
                }
 
177
            }
 
178
        }
 
179
    } else { // extra space
 
180
        int n = count;
 
181
        int space_left = space - spacerCount*spacer;
 
182
        // first give to the fixed ones, and handle non-expansiveness
 
183
        for (i = start; i < start + count; i++) {
 
184
            if (!chain[i].done
 
185
                 && (chain[i].maximumSize <= chain[i].smartSizeHint()
 
186
                     || (wannaGrow && !chain[i].expansive && chain[i].stretch == 0))) {
 
187
                chain[i].size = chain[i].smartSizeHint();
 
188
                chain[i].done = true;
 
189
                space_left -= chain[i].smartSizeHint();
 
190
                sumStretch -= chain[i].stretch;
 
191
                n--;
 
192
            }
 
193
        }
 
194
        extraspace = space_left;
 
195
 
 
196
        /*
 
197
          Do a trial distribution and calculate how much it is off.
 
198
          If there are more deficit pixels than surplus pixels, give
 
199
          the minimum size items what they need, and repeat.
 
200
          Otherwise give to the maximum size items, and repeat.
 
201
 
 
202
          Paul Olav Tvete has a wonderful mathematical proof of the
 
203
          correctness of this principle, but unfortunately this
 
204
          comment is too small to contain it.
 
205
        */
 
206
        int surplus, deficit;
 
207
        do {
 
208
            surplus = deficit = 0;
 
209
            Fixed fp_space = toFixed(space_left);
 
210
            Fixed fp_w = 0;
 
211
            for (i = start; i < start+count; i++) {
 
212
                if (chain[i].done)
 
213
                    continue;
 
214
                extraspace = 0;
 
215
                if (sumStretch <= 0)
 
216
                    fp_w += fp_space / n;
 
217
                else
 
218
                    fp_w += (fp_space * chain[i].stretch) / sumStretch;
 
219
                int w = fRound(fp_w);
 
220
                chain[i].size = w;
 
221
                fp_w -= toFixed(w); // give the difference to the next
 
222
                if (w < chain[i].smartSizeHint()) {
 
223
                    deficit +=  chain[i].smartSizeHint() - w;
 
224
                } else if (w > chain[i].maximumSize) {
 
225
                    surplus += w - chain[i].maximumSize;
 
226
                }
 
227
            }
 
228
            if (deficit > 0 && surplus <= deficit) {
 
229
                // give to the ones that have too little
 
230
                for (i = start; i < start+count; i++) {
 
231
                    if (!chain[i].done &&
 
232
                         chain[i].size < chain[i].smartSizeHint()) {
 
233
                        chain[i].size = chain[i].smartSizeHint();
 
234
                        chain[i].done = true;
 
235
                        space_left -= chain[i].smartSizeHint();
 
236
                        sumStretch -= chain[i].stretch;
 
237
                        n--;
 
238
                    }
 
239
                }
 
240
            }
 
241
            if (surplus > 0 && surplus >= deficit) {
 
242
                // take from the ones that have too much
 
243
                for (i = start; i < start+count; i++) {
 
244
                    if (!chain[i].done &&
 
245
                         chain[i].size > chain[i].maximumSize) {
 
246
                        chain[i].size = chain[i].maximumSize;
 
247
                        chain[i].done = true;
 
248
                        space_left -= chain[i].maximumSize;
 
249
                        sumStretch -= chain[i].stretch;
 
250
                        n--;
 
251
                    }
 
252
                }
 
253
            }
 
254
        } while (n > 0 && surplus != deficit);
 
255
        if (n == 0)
 
256
            extraspace = space_left;
 
257
    }
 
258
 
 
259
    /*
 
260
      As a last resort, we distribute the unwanted space equally
 
261
      among the spacers (counting the start and end of the chain). We
 
262
      could, but don't, attempt a sub-pixel allocation of the extra
 
263
      space.
 
264
    */
 
265
    int extra = extraspace / (spacerCount + 2);
 
266
    int p = pos + extra;
 
267
    for (i = start; i < start+count; i++) {
 
268
        chain[i].pos = p;
 
269
        p = p + chain[i].size;
 
270
        if (!chain[i].empty)
 
271
            p += spacer+extra;
 
272
    }
 
273
}
 
274
 
 
275
Q_GUI_EXPORT QSize qSmartMinSize(const QWidgetItem *i)
 
276
{
 
277
    QWidget *w = ((QWidgetItem *)i)->widget();
 
278
 
 
279
    QSize s(0, 0);
 
280
    QSize msh(w->minimumSizeHint());
 
281
    QSize sh(w->sizeHint());
 
282
 
 
283
    if (w->sizePolicy().horizontalPolicy() != QSizePolicy::Ignored) {
 
284
        if (w->sizePolicy().horizontalPolicy() & QSizePolicy::ShrinkFlag)
 
285
            s.setWidth(msh.width());
 
286
        else
 
287
            s.setWidth(qMax(sh.width(), msh.width()));
 
288
    }
 
289
 
 
290
    if (w->sizePolicy().verticalPolicy() != QSizePolicy::Ignored) {
 
291
        if (w->sizePolicy().verticalPolicy() & QSizePolicy::ShrinkFlag) {
 
292
            s.setHeight(msh.height());
 
293
        } else {
 
294
            s.setHeight(qMax(sh.height(), msh.height()));
 
295
        }
 
296
    }
 
297
 
 
298
    s = s.boundedTo(w->maximumSize());
 
299
    QSize min = w->minimumSize();
 
300
    if (min.width() > 0)
 
301
        s.setWidth(min.width());
 
302
    if (min.height() > 0)
 
303
        s.setHeight(min.height());
 
304
    else if (i->hasHeightForWidth() && s.width() > 0)
 
305
        s = s.expandedTo(QSize(0, i->heightForWidth(s.width())));
 
306
 
 
307
    return s.expandedTo(QSize(0,0));
 
308
}
 
309
 
 
310
Q_GUI_EXPORT QSize qSmartMinSize(const QWidget *w)
 
311
{
 
312
    QWidgetItem item(const_cast<QWidget *>(w));
 
313
    return qSmartMinSize(&item);
 
314
}
 
315
 
 
316
Q_GUI_EXPORT QSize qSmartMaxSize(const QWidgetItem *i, Qt::Alignment align)
 
317
{
 
318
    QWidget *w = ((QWidgetItem*)i)->widget();
 
319
    if (align & Qt::AlignHorizontal_Mask && align & Qt::AlignVertical_Mask)
 
320
        return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX);
 
321
    QSize s = w->maximumSize();
 
322
    if (s.width() == QWIDGETSIZE_MAX && !(align & Qt::AlignHorizontal_Mask))
 
323
        if (!(w->sizePolicy().horizontalPolicy() & QSizePolicy::GrowFlag))
 
324
            s.setWidth(w->sizeHint().width());
 
325
 
 
326
    if (s.height() == QWIDGETSIZE_MAX && !(align & Qt::AlignVertical_Mask))
 
327
        if (!(w->sizePolicy().verticalPolicy() & QSizePolicy::GrowFlag))
 
328
            s.setHeight(w->sizeHint().height());
 
329
 
 
330
    s = s.expandedTo(w->minimumSize());
 
331
 
 
332
    if (align & Qt::AlignHorizontal_Mask)
 
333
        s.setWidth(QLAYOUTSIZE_MAX);
 
334
    if (align & Qt::AlignVertical_Mask)
 
335
        s.setHeight(QLAYOUTSIZE_MAX);
 
336
    return s;
 
337
}
 
338
 
 
339
Q_GUI_EXPORT QSize qSmartMaxSize(const QWidget *w, Qt::Alignment align)
 
340
{
 
341
    QWidgetItem item(const_cast<QWidget *>(w));
 
342
    return qSmartMaxSize(&item, align);
 
343
}
 
344
 
 
345
#endif // QT_NO_LAYOUT