|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (developer.feedback@nokia.com) |
|
6 ** |
|
7 ** This file is part of the HbWidgets module of the UI Extensions for Mobile. |
|
8 ** |
|
9 ** GNU Lesser General Public License Usage |
|
10 ** This file may be used under the terms of the GNU Lesser General Public |
|
11 ** License version 2.1 as published by the Free Software Foundation and |
|
12 ** appearing in the file LICENSE.LGPL included in the packaging of this file. |
|
13 ** Please review the following information to ensure the GNU Lesser General |
|
14 ** Public License version 2.1 requirements will be met: |
|
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
16 ** |
|
17 ** In addition, as a special exception, Nokia gives you certain additional |
|
18 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
20 ** |
|
21 ** If you have questions regarding the use of this file, please contact |
|
22 ** Nokia at developer.feedback@nokia.com. |
|
23 ** |
|
24 ****************************************************************************/ |
|
25 |
|
26 #include "hbtreelayout_p.h" |
|
27 #include "hblayoututils_p.h" |
|
28 |
|
29 #include "hbabstractitemcontainer.h" |
|
30 #include "hbapplication.h" |
|
31 |
|
32 #include <QWidget> // for QWIDGETSIZE_MAX |
|
33 |
|
34 namespace |
|
35 { |
|
36 static const qreal INVALID_ITEM_HEIGHT = -1.0; |
|
37 } |
|
38 |
|
39 |
|
40 /* |
|
41 \private |
|
42 \class HbTreeLayout |
|
43 \brief HbTreeLayout manages geometries of hierarchical tree view contents. |
|
44 */ |
|
45 |
|
46 class HbTreeLayoutPrivate |
|
47 { |
|
48 public: |
|
49 |
|
50 struct TreeItem |
|
51 { |
|
52 QGraphicsLayoutItem* mItem; |
|
53 int mLevel; |
|
54 }; |
|
55 |
|
56 HbTreeLayoutPrivate(HbTreeLayout *q_ptr); |
|
57 bool uniformSizedItems() const; |
|
58 qreal calculateSmallestItemHeight() const; |
|
59 |
|
60 QList<TreeItem> mItems; |
|
61 HbTreeLayout *q; |
|
62 qreal mIndentation; |
|
63 qreal mSmallestItemHeight; |
|
64 }; |
|
65 |
|
66 HbTreeLayoutPrivate::HbTreeLayoutPrivate(HbTreeLayout *q_ptr) : |
|
67 q(q_ptr), |
|
68 mIndentation(15.0), |
|
69 mSmallestItemHeight(INVALID_ITEM_HEIGHT) |
|
70 { |
|
71 } |
|
72 |
|
73 bool HbTreeLayoutPrivate::uniformSizedItems() const |
|
74 { |
|
75 if (q->parentLayoutItem() && (static_cast<HbAbstractItemContainer *>(q->parentLayoutItem()))->uniformItemSizes() ) { |
|
76 return true; |
|
77 } else { |
|
78 return false; |
|
79 } |
|
80 } |
|
81 |
|
82 /*! |
|
83 Calculates the smallest item height from all items. |
|
84 */ |
|
85 qreal HbTreeLayoutPrivate::calculateSmallestItemHeight() const |
|
86 { |
|
87 qreal smallestHeight(0); |
|
88 if (uniformSizedItems()) { |
|
89 QGraphicsLayoutItem *firstItem = mItems.value(0).mItem; |
|
90 if (firstItem) { |
|
91 smallestHeight = firstItem->preferredHeight(); |
|
92 } |
|
93 } else { |
|
94 int itemCount = mItems.count(); |
|
95 if (itemCount > 0) { |
|
96 smallestHeight = mItems.first().mItem->preferredHeight(); |
|
97 } |
|
98 for (int i = 1; i < itemCount; ++i) { |
|
99 smallestHeight = qMin(smallestHeight, mItems.at(i).mItem->preferredHeight()); |
|
100 } |
|
101 } |
|
102 return smallestHeight; |
|
103 } |
|
104 |
|
105 |
|
106 /*! |
|
107 Constructor. |
|
108 \param parent parent layout item. |
|
109 */ |
|
110 HbTreeLayout::HbTreeLayout(QGraphicsLayoutItem *parent) |
|
111 : QGraphicsLayout(parent), d(new HbTreeLayoutPrivate(this)) |
|
112 { |
|
113 } |
|
114 |
|
115 /*! |
|
116 Destructor. |
|
117 Does not clear the parentLayoutItem of it's sub items. |
|
118 If you want to reuse the sub items first call removeAt() |
|
119 for each of them. |
|
120 */ |
|
121 HbTreeLayout::~HbTreeLayout() |
|
122 { |
|
123 delete d; |
|
124 } |
|
125 |
|
126 /*! |
|
127 \brief Appends a layout item to the list layout. |
|
128 |
|
129 This is convenience function which will internally call the \c insertItem method. |
|
130 |
|
131 \param item layout item to be added to list. |
|
132 */ |
|
133 void HbTreeLayout::addItem(QGraphicsLayoutItem *item, int level) |
|
134 { |
|
135 insertItem( -1, item, level ); |
|
136 } |
|
137 |
|
138 /*! |
|
139 \brief Inserts a layout item to the list layout. |
|
140 |
|
141 Inserts the given \a item to given position \a index. |
|
142 Does not take ownership. |
|
143 |
|
144 If index is out of range, the item is appended to the end of the list. |
|
145 |
|
146 \param index position where to insert the layout. |
|
147 \param item layout item to be inserted to stack. |
|
148 */ |
|
149 void HbTreeLayout::insertItem(int index, QGraphicsLayoutItem *item, int level) |
|
150 { |
|
151 index = qMin(index, d->mItems.count()); |
|
152 if (index < 0) { |
|
153 index = d->mItems.count(); |
|
154 } |
|
155 HbLayoutUtils::addChildItem(this, item); |
|
156 |
|
157 HbTreeLayoutPrivate::TreeItem listItem; |
|
158 listItem.mItem = item; |
|
159 listItem.mLevel = level; |
|
160 |
|
161 d->mItems.insert( index, listItem); |
|
162 invalidate(); |
|
163 } |
|
164 |
|
165 /*! |
|
166 \brief Returns position of layout item in the list layout. |
|
167 \param item item to look for. |
|
168 \return position of layout item, or -1 if not found. |
|
169 */ |
|
170 int HbTreeLayout::indexOf( QGraphicsLayoutItem *item ) const |
|
171 { |
|
172 for ( int i = 0; i < count(); ++i ) { |
|
173 if ( itemAt( i ) == item ) { |
|
174 return i; |
|
175 } |
|
176 } |
|
177 return -1; |
|
178 } |
|
179 |
|
180 /*! |
|
181 \brief Removes layout item from the list layout. |
|
182 |
|
183 Equivalent of calling removeAt(indexOf(item)). |
|
184 |
|
185 \param item item to be removed. |
|
186 \sa removeAt |
|
187 */ |
|
188 void HbTreeLayout::removeItem( QGraphicsLayoutItem *item ) |
|
189 { |
|
190 removeAt(indexOf(item)); |
|
191 } |
|
192 |
|
193 /*! |
|
194 From QGraphicsLayout. |
|
195 */ |
|
196 int HbTreeLayout::count() const |
|
197 { |
|
198 return d->mItems.count(); |
|
199 } |
|
200 |
|
201 /*! |
|
202 From QGraphicsLayout. |
|
203 */ |
|
204 QGraphicsLayoutItem *HbTreeLayout::itemAt(int i) const |
|
205 { |
|
206 return d->mItems.value(i).mItem; |
|
207 } |
|
208 |
|
209 /*! |
|
210 From QGraphicsLayout. |
|
211 Clears the parentLayoutItem of removed item. |
|
212 */ |
|
213 void HbTreeLayout::removeAt(int index) |
|
214 { |
|
215 QGraphicsLayoutItem *item = itemAt(index); |
|
216 if ( item ) { |
|
217 d->mItems.removeAt(index); |
|
218 item->setParentLayoutItem(0); |
|
219 invalidate(); |
|
220 } |
|
221 } |
|
222 |
|
223 /*! |
|
224 From QGraphicsLayout. |
|
225 */ |
|
226 void HbTreeLayout::setGeometry( const QRectF &rect ) |
|
227 { |
|
228 QGraphicsLayout::setGeometry(rect); |
|
229 QRectF effectiveRect = geometry(); |
|
230 // TODO: Apply margins? |
|
231 //qreal left, top, right, bottom; |
|
232 //getContentsMargins(&left, &top, &right, &bottom); |
|
233 //effectiveRect.adjust(+left, +top, -right, -bottom); |
|
234 |
|
235 bool mirrorLayout = false; |
|
236 if (HbApplication::layoutDirection() == Qt::RightToLeft) { |
|
237 mirrorLayout = true; |
|
238 } |
|
239 |
|
240 qreal y = effectiveRect.y(); |
|
241 int itemCount = count(); |
|
242 for (int i = 0; i < itemCount; ++i) { |
|
243 HbTreeLayoutPrivate::TreeItem listItem = d->mItems.at(i); |
|
244 |
|
245 qreal itemHeight = listItem.mItem->preferredHeight(); |
|
246 qreal itemWidth = listItem.mItem->preferredWidth(); |
|
247 |
|
248 qreal viewWidth = minimumWidth(); |
|
249 if (viewWidth > 0.0) { |
|
250 if (d->mIndentation * listItem.mLevel + itemWidth < viewWidth) { |
|
251 itemWidth = viewWidth - (d->mIndentation * listItem.mLevel); |
|
252 } else if (itemWidth > viewWidth) { |
|
253 itemWidth = viewWidth; |
|
254 } |
|
255 } |
|
256 |
|
257 qreal x; |
|
258 if (mirrorLayout) { |
|
259 x = effectiveRect.right() - d->mIndentation * listItem.mLevel - itemWidth; |
|
260 } else { |
|
261 x = d->mIndentation * listItem.mLevel + effectiveRect.left(); |
|
262 } |
|
263 |
|
264 listItem.mItem->setGeometry( QRectF(x, y, itemWidth, itemHeight ) ); |
|
265 y += itemHeight; |
|
266 } |
|
267 } |
|
268 |
|
269 /*! |
|
270 From QGraphicsLayout. |
|
271 |
|
272 The minimum size hint's width and height are the smallest ones of the |
|
273 child layout items. |
|
274 |
|
275 The preferred size hint is the sum of the preferred size hints of the |
|
276 child layout items.. |
|
277 |
|
278 The maximum size hint "unlimited", i.e. the child layout items do not |
|
279 affect the maximum size hint. |
|
280 */ |
|
281 QSizeF HbTreeLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const |
|
282 { |
|
283 QSizeF sizeHint(0, 0); |
|
284 |
|
285 if (which == Qt::MaximumSize) { |
|
286 // Sub items do not affect the maximum size hint. |
|
287 sizeHint = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); |
|
288 } else if (which == Qt::PreferredSize) { |
|
289 QGraphicsWidget *view = 0; |
|
290 if ( parentLayoutItem() |
|
291 && !parentLayoutItem()->isLayout() |
|
292 && parentLayoutItem()->graphicsItem()) { |
|
293 view = parentLayoutItem()->graphicsItem()->parentWidget(); |
|
294 } |
|
295 qreal viewWidth = view ? view->size().width() : 0.0; |
|
296 |
|
297 int itemCount = count(); |
|
298 for (int i = 0; i < itemCount; ++i) { |
|
299 QSizeF itemSize(0, 0); |
|
300 HbTreeLayoutPrivate::TreeItem listItem = d->mItems.at(i); |
|
301 |
|
302 itemSize = listItem.mItem->effectiveSizeHint(which); |
|
303 if (viewWidth > 0.0) { |
|
304 if (d->mIndentation * listItem.mLevel + itemSize.width() < viewWidth) { |
|
305 // if indentation + item width fits into view, expand item so that |
|
306 // indentation + item width == view width |
|
307 itemSize.setWidth(viewWidth - (d->mIndentation * listItem.mLevel)); |
|
308 } else if (itemSize.width() > viewWidth) { |
|
309 itemSize.setWidth(viewWidth); |
|
310 } |
|
311 } |
|
312 |
|
313 if (itemSize.isValid()) { |
|
314 sizeHint.rwidth() = qMax((d->mIndentation * listItem.mLevel + itemSize.width()), sizeHint.width()); |
|
315 sizeHint.rheight() = sizeHint.height() + itemSize.height(); |
|
316 } |
|
317 } |
|
318 } else if (which == Qt::MinimumSize) { |
|
319 if(itemAt(0)) { |
|
320 sizeHint = itemAt(0)->effectiveSizeHint(Qt::PreferredSize); |
|
321 } |
|
322 if (d->uniformSizedItems()) { |
|
323 return sizeHint; //No need to goto loop below to check the sizes of every item |
|
324 } |
|
325 |
|
326 int itemCount = count(); |
|
327 for( int i = 1; i < itemCount; ++i ) { |
|
328 QSizeF itemSize(0, 0); |
|
329 HbTreeLayoutPrivate::TreeItem listItem = d->mItems.at(i); |
|
330 |
|
331 itemSize = listItem.mItem->effectiveSizeHint(Qt::PreferredSize); |
|
332 |
|
333 if ( itemSize.isValid() ) { |
|
334 sizeHint.rwidth() = qMin( itemSize.width(), sizeHint.width() ); |
|
335 sizeHint.rheight() = qMin( sizeHint.height(), itemSize.height() ); |
|
336 } |
|
337 } |
|
338 } else { |
|
339 Q_ASSERT_X(false, "HbTreeLayout::sizeHint()", "Unhandled Qt::SizeHint type"); |
|
340 return sizeHint; |
|
341 } |
|
342 |
|
343 if (constraint.height() >= 0) { |
|
344 sizeHint.setHeight(qMin(sizeHint.height(), constraint.height())); |
|
345 } |
|
346 |
|
347 if (constraint.width() >= 0) { |
|
348 sizeHint.setWidth(qMin(sizeHint.width(), constraint.width())); |
|
349 } |
|
350 |
|
351 return sizeHint; |
|
352 |
|
353 } |
|
354 |
|
355 void HbTreeLayout::setItemLevel(QGraphicsLayoutItem *item, int level) |
|
356 { |
|
357 for (int current = 0; current < d->mItems.count(); ++current) { |
|
358 if (d->mItems.at(current).mItem == item) { |
|
359 d->mItems[current].mLevel = level; |
|
360 invalidate(); |
|
361 break; |
|
362 } |
|
363 } |
|
364 } |
|
365 |
|
366 qreal HbTreeLayout::indentation() const |
|
367 { |
|
368 return d->mIndentation; |
|
369 } |
|
370 |
|
371 void HbTreeLayout::setIndentation(qreal indentation) |
|
372 { |
|
373 if (indentation != d->mIndentation) { |
|
374 d->mIndentation = indentation; |
|
375 invalidate(); |
|
376 } |
|
377 } |
|
378 |
|
379 /*! |
|
380 Returns the smallest item height. |
|
381 */ |
|
382 qreal HbTreeLayout::smallestItemHeight() const |
|
383 { |
|
384 if (d->mSmallestItemHeight == INVALID_ITEM_HEIGHT) { |
|
385 d->mSmallestItemHeight = d->calculateSmallestItemHeight(); |
|
386 } |
|
387 return d->mSmallestItemHeight; |
|
388 } |
|
389 |
|
390 /*! |
|
391 \reimp |
|
392 |
|
393 Invalidates smallest item height cache. |
|
394 */ |
|
395 void HbTreeLayout::invalidate() |
|
396 { |
|
397 QGraphicsLayout::invalidate(); |
|
398 |
|
399 d->mSmallestItemHeight = INVALID_ITEM_HEIGHT; |
|
400 } |
|
401 |
|
402 |