~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to plasma/generic/applets/systemtray/ui/compactlayout.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   compactlayout.cpp                                                     *
 
3
 *                                                                         *
 
4
 *   Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com>              *
 
5
 *                                                                         *
 
6
 *   This program is free software; you can redistribute it and/or modify  *
 
7
 *   it under the terms of the GNU General Public License as published by  *
 
8
 *   the Free Software Foundation; either version 2 of the License, or     *
 
9
 *   (at your option) any later version.                                   *
 
10
 *                                                                         *
 
11
 *   This program is distributed in the hope that it will be useful,       *
 
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 
14
 *   GNU General Public License for more details.                          *
 
15
 *                                                                         *
 
16
 *   You should have received a copy of the GNU General Public License     *
 
17
 *   along with this program; if not, write to the                         *
 
18
 *   Free Software Foundation, Inc.,                                       *
 
19
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
 
20
 ***************************************************************************/
 
21
 
 
22
#include "compactlayout.h"
 
23
 
 
24
#include <QtCore/QHash>
 
25
 
 
26
#include <QtGui/QGraphicsWidget>
 
27
#include <kdebug.h>
 
28
 
 
29
namespace SystemTray
 
30
{
 
31
 
 
32
 
 
33
class CompactLayout::Private
 
34
{
 
35
public:
 
36
    Private(CompactLayout *q)
 
37
        : q(q),
 
38
          spacing(4.0)
 
39
    {
 
40
    }
 
41
 
 
42
    QHash<QGraphicsLayoutItem*, QRectF> calculateGeometries(const QRectF &rect,
 
43
                                                            Qt::SizeHint which,
 
44
                                                            const QSizeF &constraint) const;
 
45
    void addPadding(QHash<QGraphicsLayoutItem*, QRectF> &geometries,
 
46
                    const QRectF &constraint);
 
47
    QSizeF hackedConstraint(const QSizeF &constraint) const;
 
48
    void updateParentWidget(QGraphicsWidget *item);
 
49
    QRectF boundingRect(const QList<QRectF> &rects) const;
 
50
 
 
51
    CompactLayout *q;
 
52
    qreal spacing;
 
53
    QList<QGraphicsLayoutItem*> items;
 
54
};
 
55
 
 
56
 
 
57
CompactLayout::CompactLayout(QGraphicsLayoutItem *parent)
 
58
    : QGraphicsLayout(parent),
 
59
      d(new Private(this))
 
60
{
 
61
}
 
62
 
 
63
 
 
64
CompactLayout::~CompactLayout()
 
65
{
 
66
    foreach (QGraphicsLayoutItem* item, d->items) {
 
67
        removeItem(item);
 
68
    }
 
69
    delete d;
 
70
}
 
71
 
 
72
 
 
73
qreal CompactLayout::spacing() const
 
74
{
 
75
    return d->spacing;
 
76
}
 
77
 
 
78
 
 
79
void CompactLayout::setSpacing(qreal spacing)
 
80
{
 
81
    d->spacing = spacing;
 
82
}
 
83
 
 
84
 
 
85
void CompactLayout::insertItem(int index, QGraphicsLayoutItem *item)
 
86
{
 
87
    index = qBound(0, index, d->items.count());
 
88
 
 
89
    item->setParentLayoutItem(this);
 
90
 
 
91
    QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget *>(item);
 
92
    if (widget) {
 
93
        d->updateParentWidget(widget);
 
94
    }
 
95
 
 
96
    if (index == d->items.count()) {
 
97
        d->items.append(item);
 
98
    } else {
 
99
        d->items.insert(index, item);
 
100
    }
 
101
 
 
102
    updateGeometry();
 
103
    activate();
 
104
}
 
105
 
 
106
void CompactLayout::addItem(QGraphicsLayoutItem *item)
 
107
{
 
108
    insertItem(d->items.count(), item);
 
109
}
 
110
 
 
111
void CompactLayout::Private::updateParentWidget(QGraphicsWidget *item)
 
112
{
 
113
    QGraphicsLayoutItem *parentItem = q->parentLayoutItem();
 
114
    while (parentItem && parentItem->isLayout()) {
 
115
        parentItem = parentItem->parentLayoutItem();
 
116
    }
 
117
 
 
118
    if (parentItem) {
 
119
        item->setParentItem(static_cast<QGraphicsWidget*>(parentItem));
 
120
    }
 
121
}
 
122
 
 
123
 
 
124
void CompactLayout::removeItem(QGraphicsLayoutItem *item)
 
125
{
 
126
    d->items.removeAll(item);
 
127
    item->setParentLayoutItem(0);
 
128
    updateGeometry();
 
129
    activate();
 
130
}
 
131
 
 
132
 
 
133
bool CompactLayout::containsItem(QGraphicsLayoutItem *item) const
 
134
{
 
135
    return d->items.contains(item);
 
136
}
 
137
 
 
138
 
 
139
int CompactLayout::count() const
 
140
{
 
141
    return d->items.count();
 
142
}
 
143
 
 
144
 
 
145
void CompactLayout::setGeometry(const QRectF &rect)
 
146
{
 
147
    QGraphicsLayout::setGeometry(rect);
 
148
    //kDebug() << rect;
 
149
    QHash<QGraphicsLayoutItem*, QRectF> geometries;
 
150
    geometries = d->calculateGeometries(rect, Qt::PreferredSize, rect.size());
 
151
 
 
152
    d->addPadding(geometries, rect);
 
153
 
 
154
    QHashIterator<QGraphicsLayoutItem*, QRectF> i(geometries);
 
155
    while (i.hasNext()) {
 
156
        i.next();
 
157
        QGraphicsLayoutItem *item = i.key();
 
158
        item->setGeometry(i.value());
 
159
    }
 
160
}
 
161
 
 
162
 
 
163
void CompactLayout::Private::addPadding(QHash<QGraphicsLayoutItem*, QRectF> &geometries, const QRectF &constraint)
 
164
{
 
165
    QRectF bRect = boundingRect(geometries.values());
 
166
    QSizeF size = bRect.size();
 
167
 
 
168
    qreal xAdjustment = (constraint.width() - size.width()) / 2.0;
 
169
    qreal yAdjustment = (constraint.height() - size.height()) / 2.0;
 
170
 
 
171
    bRect.moveTopLeft(bRect.topLeft() + QPointF(xAdjustment, yAdjustment));
 
172
    if (bRect.left() < constraint.left()) {
 
173
        xAdjustment = 0;
 
174
    }
 
175
    if (bRect.top() < constraint.top()) {
 
176
        yAdjustment = 0;
 
177
    }
 
178
 
 
179
    if (xAdjustment || yAdjustment) {
 
180
        foreach (QGraphicsLayoutItem *item, items) {
 
181
            QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget *>(item);
 
182
            if (widget && !widget->isVisible()) {
 
183
                continue;
 
184
            }
 
185
 
 
186
            geometries[item].moveLeft(geometries[item].left() + xAdjustment);
 
187
            geometries[item].moveTop(geometries[item].top() + yAdjustment);
 
188
        }
 
189
    }
 
190
}
 
191
 
 
192
 
 
193
QGraphicsLayoutItem* CompactLayout::itemAt(int index) const
 
194
{
 
195
    if (index > d->items.count()) {
 
196
        return 0;
 
197
    }
 
198
 
 
199
    return d->items.at(index);
 
200
}
 
201
 
 
202
 
 
203
void CompactLayout::removeAt(int index)
 
204
{
 
205
    QGraphicsLayoutItem* item = itemAt(index);
 
206
    if (item) {
 
207
        item->setParentLayoutItem(0);
 
208
        d->items.removeAt(index);
 
209
    }
 
210
}
 
211
 
 
212
QSizeF CompactLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
 
213
{
 
214
    if (which == Qt::PreferredSize) {
 
215
        QHash<QGraphicsLayoutItem*, QRectF> geometries =
 
216
            d->calculateGeometries(geometry(), which, d->hackedConstraint(constraint));
 
217
        return d->boundingRect(geometries.values()).size();
 
218
    } else if (which == Qt::MinimumSize) {
 
219
        QSizeF min;
 
220
 
 
221
        foreach (QGraphicsLayoutItem *item, d->items) {
 
222
            QSizeF itemMin = item->minimumSize();
 
223
 
 
224
            if (itemMin.height() > min.height()) {
 
225
                min.setHeight(itemMin.height());
 
226
            }
 
227
 
 
228
            if (itemMin.width() > min.width()) {
 
229
                min.setWidth(itemMin.width());
 
230
            }
 
231
        }
 
232
 
 
233
        //kDebug() << "porporting" << min;
 
234
        return min;
 
235
    } else {
 
236
        return QSizeF();
 
237
    }
 
238
}
 
239
 
 
240
 
 
241
QRectF CompactLayout::Private::boundingRect(const QList<QRectF> &rects) const
 
242
{
 
243
    QRectF boundingRect;
 
244
 
 
245
    foreach (const QRectF &rect, rects) {
 
246
        if (boundingRect.isNull()) {
 
247
            boundingRect = rect;
 
248
        } else {
 
249
            boundingRect = boundingRect.united(rect);
 
250
        }
 
251
    }
 
252
 
 
253
    return boundingRect;
 
254
}
 
255
 
 
256
 
 
257
QHash<QGraphicsLayoutItem*, QRectF> CompactLayout::Private::calculateGeometries(const QRectF &geom, Qt::SizeHint which, const QSizeF &constraint) const
 
258
{
 
259
    QSizePolicy sizePolicy;
 
260
    //FIXME: not really pretty: try to fetch it from the grandparent (assumption on how taskarea is done) otherwise from the parent
 
261
    if (q->parentLayoutItem()->parentLayoutItem()) {
 
262
        sizePolicy = q->parentLayoutItem()->parentLayoutItem()->sizePolicy();
 
263
    } else if (q->parentLayoutItem()) {
 
264
        sizePolicy = q->parentLayoutItem()->sizePolicy();
 
265
    } else {
 
266
        sizePolicy = q->sizePolicy();
 
267
    }
 
268
 
 
269
    QHash<QGraphicsLayoutItem*, QRectF> geometries;
 
270
    QList<qreal> xPositions;
 
271
    QList<qreal> yPositions;
 
272
 
 
273
    xPositions << geom.left();
 
274
    yPositions << geom.top();
 
275
 
 
276
    foreach (QGraphicsLayoutItem *item, items) {
 
277
        QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget *>(item);
 
278
        if (widget && !widget->isVisible()) {
 
279
            continue;
 
280
        }
 
281
 
 
282
        QRectF rect;
 
283
        rect.setSize(item->effectiveSizeHint(which));
 
284
 
 
285
        rect.setWidth(qBound(item->minimumWidth(), rect.width(), constraint.width()));
 
286
        rect.setHeight(qBound(item->minimumHeight(), rect.height(), constraint.height()));
 
287
 
 
288
        // Try to find an empty space for the item within the bounds
 
289
        // of the already positioned out items
 
290
        foreach (qreal x, xPositions) {
 
291
            rect.moveLeft(x);
 
292
            if (rect.right() >= xPositions.last()) {
 
293
                continue;
 
294
            }
 
295
 
 
296
            foreach (qreal y, yPositions) {
 
297
                rect.moveTop(y);
 
298
                if (rect.bottom() >= yPositions.last()) {
 
299
                    continue;
 
300
                }
 
301
 
 
302
                bool overlapping = false;
 
303
                foreach (const QRectF &existingRect, geometries) {
 
304
                    if (existingRect.intersects(rect)) {
 
305
                        overlapping = true;
 
306
                    }
 
307
                }
 
308
 
 
309
                if (!overlapping) {
 
310
                    goto positioning_done;
 
311
                }
 
312
            }
 
313
        }
 
314
 
 
315
        // It didn't fit anywhere, so the current bounds will need to
 
316
        // be extended.
 
317
        Qt::Orientation direction;
 
318
 
 
319
        {
 
320
            const int yDelta = yPositions.last() + rect.height() - constraint.height();
 
321
            const int xDelta = xPositions.last() + rect.width() - constraint.width();
 
322
            //a layout without elements will have height==0 when vertical width == 0 when horizontal
 
323
            if (int(constraint.height()) == 0 && constraint.width() > 0) {
 
324
                direction = Qt::Vertical;
 
325
            } else if (int(constraint.width()) == 0 && constraint.height() > 0) {
 
326
                direction = Qt::Horizontal;
 
327
            // Extend based on constraints and prevent expanding if possible
 
328
            } else if ((sizePolicy.verticalPolicy() != QSizePolicy::Expanding) && xDelta < 0) {
 
329
                direction = Qt::Horizontal;
 
330
            } else if ((sizePolicy.horizontalPolicy() != QSizePolicy::Expanding) && yDelta < 0) {
 
331
                direction = Qt::Vertical;
 
332
            // Then extend based on expanding policy
 
333
            } else if (sizePolicy.horizontalPolicy() != QSizePolicy::Expanding) {
 
334
                direction = Qt::Horizontal;
 
335
            } else if (sizePolicy.verticalPolicy() != QSizePolicy::Expanding) {
 
336
                direction = Qt::Vertical;
 
337
            // Otherwise try to keep the shape of a square
 
338
            } else if (yPositions.last() >= xPositions.last()) {
 
339
                direction = Qt::Horizontal;
 
340
            } else {
 
341
                direction = Qt::Vertical;
 
342
            }
 
343
        }
 
344
 
 
345
        if (direction == Qt::Horizontal) {
 
346
            rect.moveTop(yPositions.first());
 
347
            rect.moveLeft(xPositions.last());
 
348
        } else {
 
349
            rect.moveLeft(xPositions.first());
 
350
            rect.moveTop(yPositions.last());
 
351
        }
 
352
 
 
353
    positioning_done:
 
354
        if (!xPositions.contains(rect.right() + spacing)) {
 
355
            xPositions.append(rect.right() + spacing);
 
356
            qSort(xPositions);
 
357
        }
 
358
 
 
359
        if (!yPositions.contains(rect.bottom() + spacing)) {
 
360
            yPositions.append(rect.bottom() + spacing);
 
361
            qSort(yPositions);
 
362
        }
 
363
 
 
364
        geometries[item] = rect;
 
365
    }
 
366
 
 
367
    return geometries;
 
368
}
 
369
 
 
370
 
 
371
QSizeF CompactLayout::Private::hackedConstraint(const QSizeF &constraint) const
 
372
{
 
373
    // Qt doesn't seem to ever specify constraints to sizeHint()
 
374
    // but the layout needs to know what the constraints are.
 
375
    // This function returns a new constraint with the size of
 
376
    // the containing view when Qt hasn't passed a constraint.
 
377
 
 
378
    if (constraint.width() != -1 || constraint.height() != -1) {
 
379
        return constraint;
 
380
    }
 
381
 
 
382
    const QGraphicsWidget *widget = 0;
 
383
    const QGraphicsLayoutItem *item = q;
 
384
 
 
385
    while (item && !widget) {
 
386
        item = item->parentLayoutItem();
 
387
        if (!item->isLayout()) {
 
388
            widget = static_cast<const QGraphicsWidget*>(item);
 
389
        }
 
390
    }
 
391
 
 
392
    if (!widget) {
 
393
        return constraint;
 
394
    }
 
395
 
 
396
    QSizeF parentSize;
 
397
    qreal xMargins = 0.0, yMargins = 0.0;
 
398
 
 
399
    while (widget) {
 
400
        parentSize = widget->size();
 
401
 
 
402
        qreal left, top, right, bottom;
 
403
 
 
404
        if (widget->layout()) {
 
405
            widget->layout()->getContentsMargins(&left, &top, &right, &bottom);
 
406
            xMargins += left + right;
 
407
            yMargins += top + bottom;
 
408
        }
 
409
 
 
410
        widget->getContentsMargins(&left, &top, &right, &bottom);
 
411
        xMargins += left + right;
 
412
        yMargins += top + bottom;
 
413
 
 
414
        widget = widget->parentWidget();
 
415
    }
 
416
 
 
417
    return parentSize - QSizeF(xMargins, yMargins);
 
418
}
 
419
 
 
420
 
 
421
}