|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtDeclarative module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "private/qdeclarativerepeater_p.h" |
|
43 #include "private/qdeclarativerepeater_p_p.h" |
|
44 |
|
45 #include "private/qdeclarativevisualitemmodel_p.h" |
|
46 #include <private/qdeclarativeglobal_p.h> |
|
47 #include <qdeclarativelistaccessor_p.h> |
|
48 |
|
49 #include <qlistmodelinterface_p.h> |
|
50 |
|
51 QT_BEGIN_NAMESPACE |
|
52 QDeclarativeRepeaterPrivate::QDeclarativeRepeaterPrivate() |
|
53 : model(0), ownModel(false) |
|
54 { |
|
55 } |
|
56 |
|
57 QDeclarativeRepeaterPrivate::~QDeclarativeRepeaterPrivate() |
|
58 { |
|
59 if (ownModel) |
|
60 delete model; |
|
61 } |
|
62 |
|
63 /*! |
|
64 \qmlclass Repeater QDeclarativeRepeater |
|
65 \since 4.7 |
|
66 \inherits Item |
|
67 |
|
68 \brief The Repeater item allows you to repeat an Item-based component using a model. |
|
69 |
|
70 The Repeater item is used to create a large number of |
|
71 similar items. For each entry in the model, an item is instantiated |
|
72 in a context seeded with data from the model. If the repeater will |
|
73 be instantiating a large number of instances, it may be more efficient to |
|
74 use one of Qt Declarative's \l {xmlViews}{view items}. |
|
75 |
|
76 The model may be either an object list, a string list, a number or a Qt model. |
|
77 In each case, the data element and the index is exposed to each instantiated |
|
78 component. |
|
79 |
|
80 The index is always exposed as an accessible \c index property. |
|
81 In the case of an object or string list, the data element (of type string |
|
82 or object) is available as the \c modelData property. In the case of a Qt model, |
|
83 all roles are available as named properties just like in the view classes. The |
|
84 following example shows how to use the index property inside the instantiated |
|
85 items. |
|
86 |
|
87 \snippet doc/src/snippets/declarative/repeater-index.qml 0 |
|
88 |
|
89 \image repeater-index.png |
|
90 |
|
91 Items instantiated by the Repeater are inserted, in order, as |
|
92 children of the Repeater's parent. The insertion starts immediately after |
|
93 the repeater's position in its parent stacking list. This is to allow |
|
94 you to use a Repeater inside a layout. The following QML example shows how |
|
95 the instantiated items would visually appear stacked between the red and |
|
96 blue rectangles. |
|
97 |
|
98 \snippet doc/src/snippets/declarative/repeater.qml 0 |
|
99 |
|
100 \image repeater.png |
|
101 |
|
102 The repeater instance continues to own all items it instantiates, even |
|
103 if they are otherwise manipulated. It is illegal to manually remove an item |
|
104 created by the Repeater. |
|
105 |
|
106 \note Repeater is Item-based, and cannot be used to repeat non-Item-derived objects. |
|
107 For example, it cannot be used to repeat QtObjects. |
|
108 \badcode |
|
109 Item { |
|
110 //XXX illegal. Can't repeat QtObject as it doesn't derive from Item. |
|
111 Repeater { |
|
112 model: 10 |
|
113 QtObject {} |
|
114 } |
|
115 } |
|
116 \endcode |
|
117 */ |
|
118 |
|
119 /*! |
|
120 \internal |
|
121 \class QDeclarativeRepeater |
|
122 \qmlclass Repeater |
|
123 */ |
|
124 |
|
125 /*! |
|
126 Create a new QDeclarativeRepeater instance. |
|
127 */ |
|
128 QDeclarativeRepeater::QDeclarativeRepeater(QDeclarativeItem *parent) |
|
129 : QDeclarativeItem(*(new QDeclarativeRepeaterPrivate), parent) |
|
130 { |
|
131 } |
|
132 |
|
133 /*! |
|
134 Destroy the repeater instance. All items it instantiated are also |
|
135 destroyed. |
|
136 */ |
|
137 QDeclarativeRepeater::~QDeclarativeRepeater() |
|
138 { |
|
139 } |
|
140 |
|
141 /*! |
|
142 \qmlproperty any Repeater::model |
|
143 |
|
144 The model providing data for the repeater. |
|
145 |
|
146 The model may be either an object list, a string list, a number or a Qt model. |
|
147 In each case, the data element and the index is exposed to each instantiated |
|
148 component. The index is always exposed as an accessible \c index property. |
|
149 In the case of an object or string list, the data element (of type string |
|
150 or object) is available as the \c modelData property. In the case of a Qt model, |
|
151 all roles are available as named properties just like in the view classes. |
|
152 |
|
153 As a special case the model can also be merely a number. In this case it will |
|
154 create that many instances of the component. They will also be assigned an index |
|
155 based on the order they are created. |
|
156 |
|
157 Models can also be created directly in QML, using a \l{ListModel} or \l{XmlListModel}. |
|
158 |
|
159 \sa {qmlmodels}{Data Models} |
|
160 */ |
|
161 QVariant QDeclarativeRepeater::model() const |
|
162 { |
|
163 Q_D(const QDeclarativeRepeater); |
|
164 return d->dataSource; |
|
165 } |
|
166 |
|
167 void QDeclarativeRepeater::setModel(const QVariant &model) |
|
168 { |
|
169 Q_D(QDeclarativeRepeater); |
|
170 if (d->dataSource == model) |
|
171 return; |
|
172 |
|
173 clear(); |
|
174 if (d->model) { |
|
175 disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); |
|
176 disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); |
|
177 disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); |
|
178 disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); |
|
179 /* |
|
180 disconnect(d->model, SIGNAL(createdItem(int, QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); |
|
181 disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); |
|
182 */ |
|
183 } |
|
184 d->dataSource = model; |
|
185 emit modelChanged(); |
|
186 QObject *object = qvariant_cast<QObject*>(model); |
|
187 QDeclarativeVisualModel *vim = 0; |
|
188 if (object && (vim = qobject_cast<QDeclarativeVisualModel *>(object))) { |
|
189 if (d->ownModel) { |
|
190 delete d->model; |
|
191 d->ownModel = false; |
|
192 } |
|
193 d->model = vim; |
|
194 } else { |
|
195 if (!d->ownModel) { |
|
196 d->model = new QDeclarativeVisualDataModel(qmlContext(this), this); |
|
197 d->ownModel = true; |
|
198 } |
|
199 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) |
|
200 dataModel->setModel(model); |
|
201 } |
|
202 if (d->model) { |
|
203 connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); |
|
204 connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); |
|
205 connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); |
|
206 connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); |
|
207 /* |
|
208 connect(d->model, SIGNAL(createdItem(int, QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); |
|
209 connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); |
|
210 */ |
|
211 regenerate(); |
|
212 emit countChanged(); |
|
213 } |
|
214 } |
|
215 |
|
216 /*! |
|
217 \qmlproperty Component Repeater::delegate |
|
218 \default |
|
219 |
|
220 The delegate provides a template defining each item instantiated by the repeater. |
|
221 The index is exposed as an accessible \c index property. Properties of the |
|
222 model are also available depending upon the type of \l {qmlmodels}{Data Model}. |
|
223 */ |
|
224 QDeclarativeComponent *QDeclarativeRepeater::delegate() const |
|
225 { |
|
226 Q_D(const QDeclarativeRepeater); |
|
227 if (d->model) { |
|
228 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) |
|
229 return dataModel->delegate(); |
|
230 } |
|
231 |
|
232 return 0; |
|
233 } |
|
234 |
|
235 void QDeclarativeRepeater::setDelegate(QDeclarativeComponent *delegate) |
|
236 { |
|
237 Q_D(QDeclarativeRepeater); |
|
238 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) |
|
239 if (delegate == dataModel->delegate()) |
|
240 return; |
|
241 |
|
242 if (!d->ownModel) { |
|
243 d->model = new QDeclarativeVisualDataModel(qmlContext(this)); |
|
244 d->ownModel = true; |
|
245 } |
|
246 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) { |
|
247 dataModel->setDelegate(delegate); |
|
248 regenerate(); |
|
249 emit delegateChanged(); |
|
250 } |
|
251 } |
|
252 |
|
253 /*! |
|
254 \qmlproperty int Repeater::count |
|
255 |
|
256 This property holds the number of items in the repeater. |
|
257 */ |
|
258 int QDeclarativeRepeater::count() const |
|
259 { |
|
260 Q_D(const QDeclarativeRepeater); |
|
261 if (d->model) |
|
262 return d->model->count(); |
|
263 return 0; |
|
264 } |
|
265 |
|
266 |
|
267 /*! |
|
268 \internal |
|
269 */ |
|
270 void QDeclarativeRepeater::componentComplete() |
|
271 { |
|
272 QDeclarativeItem::componentComplete(); |
|
273 regenerate(); |
|
274 } |
|
275 |
|
276 /*! |
|
277 \internal |
|
278 */ |
|
279 QVariant QDeclarativeRepeater::itemChange(GraphicsItemChange change, |
|
280 const QVariant &value) |
|
281 { |
|
282 QVariant rv = QDeclarativeItem::itemChange(change, value); |
|
283 if (change == ItemParentHasChanged) { |
|
284 regenerate(); |
|
285 } |
|
286 |
|
287 return rv; |
|
288 } |
|
289 |
|
290 void QDeclarativeRepeater::clear() |
|
291 { |
|
292 Q_D(QDeclarativeRepeater); |
|
293 if (d->model) { |
|
294 foreach (QDeclarativeItem *item, d->deletables) { |
|
295 d->model->release(item); |
|
296 } |
|
297 } |
|
298 d->deletables.clear(); |
|
299 } |
|
300 |
|
301 /*! |
|
302 \internal |
|
303 */ |
|
304 void QDeclarativeRepeater::regenerate() |
|
305 { |
|
306 Q_D(QDeclarativeRepeater); |
|
307 if (!isComponentComplete()) |
|
308 return; |
|
309 |
|
310 clear(); |
|
311 |
|
312 if (!d->model || !d->model->count() || !d->model->isValid() || !parentItem() || !isComponentComplete()) |
|
313 return; |
|
314 |
|
315 for (int ii = 0; ii < count(); ++ii) { |
|
316 QDeclarativeItem *item = d->model->item(ii); |
|
317 if (item) { |
|
318 QDeclarative_setParent_noEvent(item, parentItem()); |
|
319 item->setParentItem(parentItem()); |
|
320 item->stackBefore(this); |
|
321 d->deletables << item; |
|
322 } |
|
323 } |
|
324 } |
|
325 |
|
326 void QDeclarativeRepeater::itemsInserted(int index, int count) |
|
327 { |
|
328 Q_D(QDeclarativeRepeater); |
|
329 if (!isComponentComplete()) |
|
330 return; |
|
331 for (int i = 0; i < count; ++i) { |
|
332 int modelIndex = index + i; |
|
333 QDeclarativeItem *item = d->model->item(modelIndex); |
|
334 if (item) { |
|
335 QDeclarative_setParent_noEvent(item, parentItem()); |
|
336 item->setParentItem(parentItem()); |
|
337 if (modelIndex < d->deletables.count()) |
|
338 item->stackBefore(d->deletables.at(modelIndex)); |
|
339 else |
|
340 item->stackBefore(this); |
|
341 d->deletables.insert(modelIndex, item); |
|
342 } |
|
343 } |
|
344 } |
|
345 |
|
346 void QDeclarativeRepeater::itemsRemoved(int index, int count) |
|
347 { |
|
348 Q_D(QDeclarativeRepeater); |
|
349 if (!isComponentComplete() || count <= 0) |
|
350 return; |
|
351 while (count--) { |
|
352 QDeclarativeItem *item = d->deletables.takeAt(index); |
|
353 if (item) |
|
354 d->model->release(item); |
|
355 else |
|
356 break; |
|
357 } |
|
358 } |
|
359 |
|
360 void QDeclarativeRepeater::itemsMoved(int from, int to, int count) |
|
361 { |
|
362 Q_D(QDeclarativeRepeater); |
|
363 if (!isComponentComplete() || count <= 0) |
|
364 return; |
|
365 if (from + count > d->deletables.count()) { |
|
366 regenerate(); |
|
367 return; |
|
368 } |
|
369 QList<QDeclarativeItem*> removed; |
|
370 int removedCount = count; |
|
371 while (removedCount--) |
|
372 removed << d->deletables.takeAt(from); |
|
373 for (int i = 0; i < count; ++i) |
|
374 d->deletables.insert(to + i, removed.at(i)); |
|
375 d->deletables.last()->stackBefore(this); |
|
376 for (int i = d->model->count()-1; i > 0; --i) { |
|
377 QDeclarativeItem *item = d->deletables.at(i-1); |
|
378 item->stackBefore(d->deletables.at(i)); |
|
379 } |
|
380 } |
|
381 |
|
382 void QDeclarativeRepeater::modelReset() |
|
383 { |
|
384 if (!isComponentComplete()) |
|
385 return; |
|
386 regenerate(); |
|
387 } |
|
388 |
|
389 QT_END_NAMESPACE |