Doc: Moving Qt Quick docs for new doc structure
[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 QtQml 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/qqmlglobal_p.h>
47 #include <private/qquicklistaccessor_p.h>
48 #include <private/qlistmodelinterface_p.h>
49 #include <private/qquickchangeset_p.h>
50
51 QT_BEGIN_NAMESPACE
52
53 QQuickRepeaterPrivate::QQuickRepeaterPrivate()
54     : model(0), ownModel(false), inRequest(false), dataSourceIsObject(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/snippets/qml/repeaters/repeater.qml import
83     \codeline
84     \snippet doc/snippets/qml/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/snippets/qml/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     \li A number that indicates the number of delegates to be created by the repeater
166     \li A model (e.g. a ListModel item, or a QAbstractItemModel subclass)
167     \li A string list
168     \li 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
179     if (d->dataSourceIsObject) {
180         QObject *o = d->dataSourceAsObject;
181         return QVariant::fromValue(o);
182     }
183
184     return d->dataSource;
185 }
186
187 void QQuickRepeater::setModel(const QVariant &model)
188 {
189     Q_D(QQuickRepeater);
190     if (d->dataSource == model)
191         return;
192
193     clear();
194     if (d->model) {
195         disconnect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
196                 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
197         disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
198         disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
199 //        disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
200     }
201     d->dataSource = model;
202     QObject *object = qvariant_cast<QObject*>(model);
203     d->dataSourceAsObject = object;
204     d->dataSourceIsObject = object != 0;
205     QQuickVisualModel *vim = 0;
206     if (object && (vim = qobject_cast<QQuickVisualModel *>(object))) {
207         if (d->ownModel) {
208             delete d->model;
209             d->ownModel = false;
210         }
211         d->model = vim;
212     } else {
213         if (!d->ownModel) {
214             d->model = new QQuickVisualDataModel(qmlContext(this));
215             d->ownModel = true;
216             if (isComponentComplete())
217                 static_cast<QQuickVisualDataModel *>(d->model)->componentComplete();
218         }
219         if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
220             dataModel->setModel(model);
221     }
222     if (d->model) {
223         connect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
224                 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
225         connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
226         connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
227 //        connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
228         regenerate();
229     }
230     emit modelChanged();
231     emit countChanged();
232 }
233
234 /*!
235     \qmlproperty Component QtQuick2::Repeater::delegate
236     \default
237
238     The delegate provides a template defining each item instantiated by the repeater.
239
240     Delegates are exposed to a read-only \c index property that indicates the index
241     of the delegate within the repeater. For example, the following \l Text delegate
242     displays the index of each repeated item:
243
244     \table
245     \row
246     \li \snippet doc/snippets/qml/repeaters/repeater.qml index
247     \li \image repeater-index.png
248     \endtable
249
250     If the \l model is a \l{QStringList-based model}{string list} or
251     \l{QObjectList-based model}{object list}, the delegate is also exposed to
252     a read-only \c modelData property that holds the string or object data. For
253     example:
254
255     \table
256     \row
257     \li \snippet doc/snippets/qml/repeaters/repeater.qml modeldata
258     \li \image repeater-modeldata.png
259     \endtable
260
261     If the \l model is a model object (such as a \l ListModel) the delegate
262     can access all model roles as named properties, in the same way that delegates
263     do for view classes like ListView.
264
265     \sa {QML Data Models}
266  */
267 QQmlComponent *QQuickRepeater::delegate() const
268 {
269     Q_D(const QQuickRepeater);
270     if (d->model) {
271         if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
272             return dataModel->delegate();
273     }
274
275     return 0;
276 }
277
278 void QQuickRepeater::setDelegate(QQmlComponent *delegate)
279 {
280     Q_D(QQuickRepeater);
281     if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
282        if (delegate == dataModel->delegate())
283            return;
284
285     if (!d->ownModel) {
286         d->model = new QQuickVisualDataModel(qmlContext(this));
287         d->ownModel = true;
288     }
289     if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) {
290         dataModel->setDelegate(delegate);
291         regenerate();
292         emit delegateChanged();
293     }
294 }
295
296 /*!
297     \qmlproperty int QtQuick2::Repeater::count
298
299     This property holds the number of items in the repeater.
300 */
301 int QQuickRepeater::count() const
302 {
303     Q_D(const QQuickRepeater);
304     if (d->model)
305         return d->model->count();
306     return 0;
307 }
308
309 /*!
310     \qmlmethod Item QtQuick2::Repeater::itemAt(index)
311
312     Returns the \l Item that has been created at the given \a index, or \c null
313     if no item exists at \a index.
314 */
315 QQuickItem *QQuickRepeater::itemAt(int index) const
316 {
317     Q_D(const QQuickRepeater);
318     if (index >= 0 && index < d->deletables.count())
319         return d->deletables[index];
320     return 0;
321 }
322
323 void QQuickRepeater::componentComplete()
324 {
325     Q_D(QQuickRepeater);
326     if (d->model && d->ownModel)
327         static_cast<QQuickVisualDataModel *>(d->model)->componentComplete();
328     QQuickItem::componentComplete();
329     regenerate();
330     if (d->model && d->model->count())
331         emit countChanged();
332 }
333
334 void QQuickRepeater::itemChange(ItemChange change, const ItemChangeData &value)
335 {
336     QQuickItem::itemChange(change, value);
337     if (change == ItemParentHasChanged) {
338         regenerate();
339     }
340 }
341
342 void QQuickRepeater::clear()
343 {
344     Q_D(QQuickRepeater);
345     bool complete = isComponentComplete();
346
347     if (d->model) {
348         for (int i = 0; i < d->deletables.count(); ++i) {
349             QQuickItem *item = d->deletables.at(i);
350             if (complete)
351                 emit itemRemoved(i, item);
352             d->model->release(item);
353         }
354     }
355     d->deletables.clear();
356     d->itemCount = 0;
357 }
358
359 void QQuickRepeater::regenerate()
360 {
361     Q_D(QQuickRepeater);
362     if (!isComponentComplete())
363         return;
364
365     clear();
366
367     if (!d->model || !d->model->count() || !d->model->isValid() || !parentItem() || !isComponentComplete())
368         return;
369
370     d->itemCount = count();
371     d->deletables.resize(d->itemCount);
372     d->createFrom = 0;
373     d->createItems();
374 }
375
376 void QQuickRepeaterPrivate::createItems()
377 {
378     Q_Q(QQuickRepeater);
379     if (createFrom == -1)
380         return;
381     inRequest = true;
382     for (int ii = createFrom; ii < itemCount; ++ii) {
383         if (!deletables.at(ii)) {
384             QQuickItem *item = model->item(ii, false);
385             if (!item) {
386                 createFrom = ii;
387                 break;
388             }
389             deletables[ii] = item;
390             QQml_setParent_noEvent(item, q->parentItem());
391             item->setParentItem(q->parentItem());
392             if (ii > 0 && deletables.at(ii-1)) {
393                 item->stackAfter(deletables.at(ii-1));
394             } else {
395                 QQuickItem *after = q;
396                 for (int si = ii+1; si < itemCount; ++si) {
397                     if (deletables.at(si)) {
398                         after = deletables.at(si);
399                         break;
400                     }
401                 }
402                 item->stackBefore(after);
403             }
404             emit q->itemAdded(ii, item);
405         }
406     }
407     inRequest = false;
408 }
409
410 void QQuickRepeater::createdItem(int, QQuickItem *)
411 {
412     Q_D(QQuickRepeater);
413     if (!d->inRequest)
414         d->createItems();
415 }
416
417 void QQuickRepeater::initItem(int, QQuickItem *item)
418 {
419     QQml_setParent_noEvent(item, parentItem());
420     item->setParentItem(parentItem());
421 }
422
423 void QQuickRepeater::modelUpdated(const QQuickChangeSet &changeSet, bool reset)
424 {
425     Q_D(QQuickRepeater);
426
427     if (!isComponentComplete())
428         return;
429
430     if (reset) {
431         regenerate();
432         emit countChanged();
433     }
434
435     int difference = 0;
436     QHash<int, QVector<QPointer<QQuickItem> > > moved;
437     foreach (const QQuickChangeSet::Remove &remove, changeSet.removes()) {
438         int index = qMin(remove.index, d->deletables.count());
439         int count = qMin(remove.index + remove.count, d->deletables.count()) - index;
440         if (remove.isMove()) {
441             moved.insert(remove.moveId, d->deletables.mid(index, count));
442             d->deletables.erase(
443                     d->deletables.begin() + index,
444                     d->deletables.begin() + index + count);
445         } else while (count--) {
446             QQuickItem *item = d->deletables.at(index);
447             d->deletables.remove(index);
448             emit itemRemoved(index, item);
449             if (item)
450                 d->model->release(item);
451             --d->itemCount;
452         }
453
454         difference -= remove.count;
455     }
456
457     d->createFrom = -1;
458     foreach (const QQuickChangeSet::Insert &insert, changeSet.inserts()) {
459         int index = qMin(insert.index, d->deletables.count());
460         if (insert.isMove()) {
461             QVector<QPointer<QQuickItem> > items = moved.value(insert.moveId);
462             d->deletables = d->deletables.mid(0, index) + items + d->deletables.mid(index);
463             QQuickItem *stackBefore = index + items.count() < d->deletables.count()
464                     ? d->deletables.at(index + items.count())
465                     : this;
466             for (int i = index; i < index + items.count(); ++i)
467                 d->deletables.at(i)->stackBefore(stackBefore);
468         } else for (int i = 0; i < insert.count; ++i) {
469             int modelIndex = index + i;
470             ++d->itemCount;
471             d->deletables.insert(modelIndex, 0);
472             if (d->createFrom == -1)
473                 d->createFrom = modelIndex;
474         }
475         difference += insert.count;
476     }
477
478     d->createItems();
479
480     if (difference != 0)
481         emit countChanged();
482 }
483
484 QT_END_NAMESPACE