Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickrepeater.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General 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 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickrepeater_p.h"
43 #include "qquickrepeater_p_p.h"
44 #include "qquickvisualdatamodel_p.h"
45
46 #include <private/qdeclarativeglobal_p.h>
47 #include <private/qdeclarativelistaccessor_p.h>
48 #include <private/qlistmodelinterface_p.h>
49 #include <private/qdeclarativechangeset_p.h>
50
51 QT_BEGIN_NAMESPACE
52
53 QQuickRepeaterPrivate::QQuickRepeaterPrivate()
54     : model(0), ownModel(false), inRequest(false), itemCount(0), createFrom(-1)
55 {
56 }
57
58 QQuickRepeaterPrivate::~QQuickRepeaterPrivate()
59 {
60     if (ownModel)
61         delete model;
62 }
63
64 /*!
65     \qmlclass Repeater QQuickRepeater
66     \inqmlmodule QtQuick 2
67     \ingroup qml-utility-elements
68     \inherits Item
69
70     \brief The Repeater element allows you to repeat an Item-based component using a model.
71
72     The Repeater element is used to create a large number of
73     similar items. Like other view elements, a Repeater has a \l model and a \l delegate:
74     for each entry in the model, the delegate is instantiated
75     in a context seeded with data from the model. A Repeater item is usually
76     enclosed in a positioner element such as \l Row or \l Column to visually
77     position the multiple delegate items created by the Repeater.
78
79     The following Repeater creates three instances of a \l Rectangle item within
80     a \l Row:
81
82     \snippet doc/src/snippets/declarative/repeaters/repeater.qml import
83     \codeline
84     \snippet doc/src/snippets/declarative/repeaters/repeater.qml simple
85
86     \image repeater-simple.png
87
88     A Repeater's \l model can be any of the supported \l {qmlmodels}{data models}.
89     Additionally, like delegates for other views, a Repeater delegate can access
90     its index within the repeater, as well as the model data relevant to the
91     delegate. See the \l delegate property documentation for details.
92
93     Items instantiated by the Repeater are inserted, in order, as
94     children of the Repeater's parent.  The insertion starts immediately after
95     the repeater's position in its parent stacking list.  This allows
96     a Repeater to be used inside a layout. For example, the following Repeater's
97     items are stacked between a red rectangle and a blue rectangle:
98
99     \snippet doc/src/snippets/declarative/repeaters/repeater.qml layout
100
101     \image repeater.png
102
103
104     \note A Repeater item owns all items it instantiates. Removing or dynamically destroying
105     an item created by a Repeater results in unpredictable behavior.
106
107
108     \section2 Considerations when using Repeater
109
110     The Repeater element creates all of its delegate items when the repeater is first
111     created. This can be inefficient if there are a large number of delegate items and
112     not all of the items are required to be visible at the same time. If this is the case,
113     consider using other view elements like ListView (which only creates delegate items
114     when they are scrolled into view) or use the \l {Dynamic Object Creation} methods to
115     create items as they are required.
116
117     Also, note that Repeater is \l {Item}-based, and can only repeat \l {Item}-derived objects.
118     For example, it cannot be used to repeat QtObjects:
119     \badcode
120     Item {
121         //XXX does not work! Can't repeat QtObject as it doesn't derive from Item.
122         Repeater {
123             model: 10
124             QtObject {}
125         }
126     }
127     \endcode
128  */
129
130 /*!
131     \qmlsignal QtQuick2::Repeater::onItemAdded(int index, Item item)
132
133     This handler is called when an item is added to the repeater. The \a index
134     parameter holds the index at which the item has been inserted within the
135     repeater, and the \a item parameter holds the \l Item that has been added.
136 */
137
138 /*!
139     \qmlsignal QtQuick2::Repeater::onItemRemoved(int index, Item item)
140
141     This handler is called when an item is removed from the repeater. The \a index
142     parameter holds the index at which the item was removed from the repeater,
143     and the \a item parameter holds the \l Item that was removed.
144
145     Do not keep a reference to \a item if it was created by this repeater, as
146     in these cases it will be deleted shortly after the handler is called.
147 */
148 QQuickRepeater::QQuickRepeater(QQuickItem *parent)
149   : QQuickItem(*(new QQuickRepeaterPrivate), parent)
150 {
151 }
152
153 QQuickRepeater::~QQuickRepeater()
154 {
155 }
156
157 /*!
158     \qmlproperty any QtQuick2::Repeater::model
159
160     The model providing data for the repeater.
161
162     This property can be set to any of the supported \l {qmlmodels}{data models}:
163
164     \list
165     \o A number that indicates the number of delegates to be created by the repeater
166     \o A model (e.g. a ListModel item, or a QAbstractItemModel subclass)
167     \o A string list
168     \o An object list
169     \endlist
170
171     The type of model affects the properties that are exposed to the \l delegate.
172
173     \sa {qmlmodels}{Data Models}
174 */
175 QVariant QQuickRepeater::model() const
176 {
177     Q_D(const QQuickRepeater);
178     return d->dataSource;
179 }
180
181 void QQuickRepeater::setModel(const QVariant &model)
182 {
183     Q_D(QQuickRepeater);
184     if (d->dataSource == model)
185         return;
186
187     clear();
188     if (d->model) {
189         disconnect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)),
190                 this, SLOT(modelUpdated(QDeclarativeChangeSet,bool)));
191         disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
192         disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
193 //        disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
194     }
195     d->dataSource = model;
196     QObject *object = qvariant_cast<QObject*>(model);
197     QQuickVisualModel *vim = 0;
198     if (object && (vim = qobject_cast<QQuickVisualModel *>(object))) {
199         if (d->ownModel) {
200             delete d->model;
201             d->ownModel = false;
202         }
203         d->model = vim;
204     } else {
205         if (!d->ownModel) {
206             d->model = new QQuickVisualDataModel(qmlContext(this));
207             d->ownModel = true;
208             if (isComponentComplete())
209                 static_cast<QQuickVisualDataModel *>(d->model)->componentComplete();
210         }
211         if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
212             dataModel->setModel(model);
213     }
214     if (d->model) {
215         connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)),
216                 this, SLOT(modelUpdated(QDeclarativeChangeSet,bool)));
217         connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
218         connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
219 //        connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
220         regenerate();
221     }
222     emit modelChanged();
223     emit countChanged();
224 }
225
226 /*!
227     \qmlproperty Component QtQuick2::Repeater::delegate
228     \default
229
230     The delegate provides a template defining each item instantiated by the repeater.
231
232     Delegates are exposed to a read-only \c index property that indicates the index
233     of the delegate within the repeater. For example, the following \l Text delegate
234     displays the index of each repeated item:
235
236     \table
237     \row
238     \o \snippet doc/src/snippets/declarative/repeaters/repeater.qml index
239     \o \image repeater-index.png
240     \endtable
241
242     If the \l model is a \l{QStringList-based model}{string list} or
243     \l{QObjectList-based model}{object list}, the delegate is also exposed to
244     a read-only \c modelData property that holds the string or object data. For
245     example:
246
247     \table
248     \row
249     \o \snippet doc/src/snippets/declarative/repeaters/repeater.qml modeldata
250     \o \image repeater-modeldata.png
251     \endtable
252
253     If the \l model is a model object (such as a \l ListModel) the delegate
254     can access all model roles as named properties, in the same way that delegates
255     do for view classes like ListView.
256
257     \sa {QML Data Models}
258  */
259 QDeclarativeComponent *QQuickRepeater::delegate() const
260 {
261     Q_D(const QQuickRepeater);
262     if (d->model) {
263         if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
264             return dataModel->delegate();
265     }
266
267     return 0;
268 }
269
270 void QQuickRepeater::setDelegate(QDeclarativeComponent *delegate)
271 {
272     Q_D(QQuickRepeater);
273     if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
274        if (delegate == dataModel->delegate())
275            return;
276
277     if (!d->ownModel) {
278         d->model = new QQuickVisualDataModel(qmlContext(this));
279         d->ownModel = true;
280     }
281     if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) {
282         dataModel->setDelegate(delegate);
283         regenerate();
284         emit delegateChanged();
285     }
286 }
287
288 /*!
289     \qmlproperty int QtQuick2::Repeater::count
290
291     This property holds the number of items in the repeater.
292 */
293 int QQuickRepeater::count() const
294 {
295     Q_D(const QQuickRepeater);
296     if (d->model)
297         return d->model->count();
298     return 0;
299 }
300
301 /*!
302     \qmlmethod Item QtQuick2::Repeater::itemAt(index)
303
304     Returns the \l Item that has been created at the given \a index, or \c null
305     if no item exists at \a index.
306 */
307 QQuickItem *QQuickRepeater::itemAt(int index) const
308 {
309     Q_D(const QQuickRepeater);
310     if (index >= 0 && index < d->deletables.count())
311         return d->deletables[index];
312     return 0;
313 }
314
315 void QQuickRepeater::componentComplete()
316 {
317     Q_D(QQuickRepeater);
318     if (d->model && d->ownModel)
319         static_cast<QQuickVisualDataModel *>(d->model)->componentComplete();
320     QQuickItem::componentComplete();
321     regenerate();
322     if (d->model && d->model->count())
323         emit countChanged();
324 }
325
326 void QQuickRepeater::itemChange(ItemChange change, const ItemChangeData &value)
327 {
328     QQuickItem::itemChange(change, value);
329     if (change == ItemParentHasChanged) {
330         regenerate();
331     }
332 }
333
334 void QQuickRepeater::clear()
335 {
336     Q_D(QQuickRepeater);
337     bool complete = isComponentComplete();
338
339     if (d->model) {
340         for (int i = 0; i < d->deletables.count(); ++i) {
341             QQuickItem *item = d->deletables.at(i);
342             if (complete)
343                 emit itemRemoved(i, item);
344             d->model->release(item);
345         }
346     }
347     d->deletables.clear();
348     d->itemCount = 0;
349 }
350
351 void QQuickRepeater::regenerate()
352 {
353     Q_D(QQuickRepeater);
354     if (!isComponentComplete())
355         return;
356
357     clear();
358
359     if (!d->model || !d->model->count() || !d->model->isValid() || !parentItem() || !isComponentComplete())
360         return;
361
362     d->itemCount = count();
363     d->deletables.resize(d->itemCount);
364     d->createFrom = 0;
365     d->createItems();
366 }
367
368 void QQuickRepeaterPrivate::createItems()
369 {
370     Q_Q(QQuickRepeater);
371     if (createFrom == -1)
372         return;
373     inRequest = true;
374     for (int ii = createFrom; ii < itemCount; ++ii) {
375         if (!deletables.at(ii)) {
376             QQuickItem *item = model->item(ii, false);
377             if (!item) {
378                 createFrom = ii;
379                 break;
380             }
381             deletables[ii] = item;
382             QDeclarative_setParent_noEvent(item, q->parentItem());
383             item->setParentItem(q->parentItem());
384             if (ii > 0 && deletables.at(ii-1)) {
385                 item->stackAfter(deletables.at(ii-1));
386             } else {
387                 QQuickItem *after = q;
388                 for (int si = ii+1; si < itemCount; ++si) {
389                     if (deletables.at(si)) {
390                         after = deletables.at(si);
391                         break;
392                     }
393                 }
394                 item->stackBefore(after);
395             }
396             emit q->itemAdded(ii, item);
397         }
398     }
399     inRequest = false;
400 }
401
402 void QQuickRepeater::createdItem(int, QQuickItem *)
403 {
404     Q_D(QQuickRepeater);
405     if (!d->inRequest)
406         d->createItems();
407 }
408
409 void QQuickRepeater::initItem(int, QQuickItem *item)
410 {
411     QDeclarative_setParent_noEvent(item, parentItem());
412     item->setParentItem(parentItem());
413 }
414
415 void QQuickRepeater::modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset)
416 {
417     Q_D(QQuickRepeater);
418
419     if (!isComponentComplete())
420         return;
421
422     if (reset) {
423         regenerate();
424         emit countChanged();
425     }
426
427     int difference = 0;
428     QHash<int, QVector<QPointer<QQuickItem> > > moved;
429     foreach (const QDeclarativeChangeSet::Remove &remove, changeSet.removes()) {
430         int index = qMin(remove.index, d->deletables.count());
431         int count = qMin(remove.index + remove.count, d->deletables.count()) - index;
432         if (remove.isMove()) {
433             moved.insert(remove.moveId, d->deletables.mid(index, count));
434             d->deletables.erase(
435                     d->deletables.begin() + index,
436                     d->deletables.begin() + index + count);
437         } else while (count--) {
438             QQuickItem *item = d->deletables.at(index);
439             d->deletables.remove(index);
440             emit itemRemoved(index, item);
441             if (item)
442                 d->model->release(item);
443             --d->itemCount;
444         }
445
446         difference -= remove.count;
447     }
448
449     d->createFrom = -1;
450     foreach (const QDeclarativeChangeSet::Insert &insert, changeSet.inserts()) {
451         int index = qMin(insert.index, d->deletables.count());
452         if (insert.isMove()) {
453             QVector<QPointer<QQuickItem> > items = moved.value(insert.moveId);
454             d->deletables = d->deletables.mid(0, index) + items + d->deletables.mid(index);
455             QQuickItem *stackBefore = index + items.count() < d->deletables.count()
456                     ? d->deletables.at(index + items.count())
457                     : this;
458             for (int i = index; i < index + items.count(); ++i)
459                 d->deletables.at(i)->stackBefore(stackBefore);
460         } else for (int i = 0; i < insert.count; ++i) {
461             int modelIndex = index + i;
462             ++d->itemCount;
463             d->deletables.insert(modelIndex, 0);
464             if (d->createFrom == -1)
465                 d->createFrom = modelIndex;
466         }
467         difference += insert.count;
468     }
469
470     d->createItems();
471
472     if (difference != 0)
473         emit countChanged();
474 }
475
476 QT_END_NAMESPACE