source: trunk/src/declarative/util/qdeclarativelistmodel.cpp@ 846

Last change on this file since 846 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 47.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtDeclarative module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qdeclarativelistmodel_p_p.h"
43#include "private/qdeclarativelistmodelworkeragent_p.h"
44#include "private/qdeclarativeopenmetaobject_p.h"
45
46#include <qdeclarativecustomparser_p.h>
47#include <qdeclarativeparser_p.h>
48#include <qdeclarativeengine_p.h>
49#include <qdeclarativecontext.h>
50#include <qdeclarativeinfo.h>
51
52#include <QtCore/qdebug.h>
53#include <QtCore/qstack.h>
54#include <QXmlStreamReader>
55#include <QtScript/qscriptvalueiterator.h>
56
57Q_DECLARE_METATYPE(QListModelInterface *)
58
59QT_BEGIN_NAMESPACE
60
61template<typename T>
62void qdeclarativelistmodel_move(int from, int to, int n, T *items)
63{
64 if (n == 1) {
65 items->move(from, to);
66 } else {
67 T replaced;
68 int i=0;
69 typename T::ConstIterator it=items->begin(); it += from+n;
70 for (; i<to-from; ++i,++it)
71 replaced.append(*it);
72 i=0;
73 it=items->begin(); it += from;
74 for (; i<n; ++i,++it)
75 replaced.append(*it);
76 typename T::ConstIterator f=replaced.begin();
77 typename T::Iterator t=items->begin(); t += from;
78 for (; f != replaced.end(); ++f, ++t)
79 *t = *f;
80 }
81}
82
83QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListModelData::instructions() const
84{
85 return (QDeclarativeListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData));
86}
87
88/*!
89 \qmlclass ListModel QDeclarativeListModel
90 \ingroup qml-working-with-data
91 \since 4.7
92 \brief The ListModel element defines a free-form list data source.
93
94 The ListModel is a simple container of ListElement definitions, each containing data roles.
95 The contents can be defined dynamically, or explicitly in QML.
96
97 The number of elements in the model can be obtained from its \l count property.
98 A number of familiar methods are also provided to manipulate the contents of the
99 model, including append(), insert(), move(), remove() and set(). These methods
100 accept dictionaries as their arguments; these are translated to ListElement objects
101 by the model.
102
103 Elements can be manipulated via the model using the setProperty() method, which
104 allows the roles of the specified element to be set and changed.
105
106 \section1 Example Usage
107
108 The following example shows a ListModel containing three elements, with the roles
109 "name" and "cost".
110
111 \beginfloatright
112 \inlineimage listmodel.png
113 \endfloat
114
115 \snippet doc/src/snippets/declarative/listmodel.qml 0
116
117 \clearfloat
118 Roles (properties) in each element must begin with a lower-case letter and
119 should be common to all elements in a model. The ListElement documentation
120 provides more guidelines for how elements should be defined.
121
122 Since the example model contains an \c id property, it can be referenced
123 by views, such as the ListView in this example:
124
125 \snippet doc/src/snippets/declarative/listmodel-simple.qml 0
126 \dots 8
127 \snippet doc/src/snippets/declarative/listmodel-simple.qml 1
128
129 It is possible for roles to contain list data. In the following example we
130 create a list of fruit attributes:
131
132 \snippet doc/src/snippets/declarative/listmodel-nested.qml model
133
134 The delegate displays all the fruit attributes:
135
136 \beginfloatright
137 \inlineimage listmodel-nested.png
138 \endfloat
139
140 \snippet doc/src/snippets/declarative/listmodel-nested.qml delegate
141
142 \clearfloat
143 \section1 Modifying List Models
144
145 The content of a ListModel may be created and modified using the clear(),
146 append(), set() and setProperty() methods. For example:
147
148 \snippet doc/src/snippets/declarative/listmodel-modify.qml delegate
149
150 Note that when creating content dynamically the set of available properties
151 cannot be changed once set. Whatever properties are first added to the model
152 are the only permitted properties in the model.
153
154 \section1 Using Threaded List Models with WorkerScript
155
156 ListModel can be used together with WorkerScript access a list model
157 from multiple threads. This is useful if list modifications are
158 synchronous and take some time: the list operations can be moved to a
159 different thread to avoid blocking of the main GUI thread.
160
161 Here is an example that uses WorkerScript to periodically append the
162 current time to a list model:
163
164 \snippet examples/declarative/threading/threadedlistmodel/timedisplay.qml 0
165
166 The included file, \tt dataloader.js, looks like this:
167
168 \snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0
169
170 The timer in the main example sends messages to the worker script by calling
171 \l WorkerScript::sendMessage(). When this message is received,
172 \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js,
173 which appends the current time to the list model.
174
175 Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()}
176 handler. You must call sync() or else the changes made to the list from the external
177 thread will not be reflected in the list model in the main thread.
178
179 \section1 Restrictions
180
181 If a list model is to be accessed from a WorkerScript, it cannot
182 contain list-type data. So, the following model cannot be used from a WorkerScript
183 because of the list contained in the "attributes" property:
184
185 \code
186 ListModel {
187 id: fruitModel
188 ListElement {
189 name: "Apple"
190 cost: 2.45
191 attributes: [
192 ListElement { description: "Core" },
193 ListElement { description: "Deciduous" }
194 ]
195 }
196 }
197 \endcode
198
199 In addition, the WorkerScript cannot add list-type data to the model.
200
201 \sa {qmlmodels}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtDeclarative
202*/
203
204
205/*
206 A ListModel internally uses either a NestedListModel or FlatListModel.
207
208 A NestedListModel can contain lists of ListElements (which
209 when retrieved from get() is accessible as a list model within the list
210 model) whereas a FlatListModel cannot.
211
212 ListModel uses a NestedListModel to begin with, and if the model is later
213 used from a WorkerScript, it changes to use a FlatListModel instead. This
214 is because ModelNode (which abstracts the nested list model data) needs
215 access to the declarative engine and script engine, which cannot be
216 safely used from outside of the main thread.
217*/
218
219QDeclarativeListModel::QDeclarativeListModel(QObject *parent)
220: QListModelInterface(parent), m_agent(0), m_nested(new NestedListModel(this)), m_flat(0)
221{
222}
223
224QDeclarativeListModel::QDeclarativeListModel(const QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *parent)
225: QListModelInterface(parent), m_agent(0), m_nested(0), m_flat(0)
226{
227 m_flat = new FlatListModel(this);
228 m_flat->m_parentAgent = parent;
229
230 if (orig->m_flat) {
231 m_flat->m_roles = orig->m_flat->m_roles;
232 m_flat->m_strings = orig->m_flat->m_strings;
233 m_flat->m_values = orig->m_flat->m_values;
234
235 m_flat->m_nodeData.reserve(m_flat->m_values.count());
236 for (int i=0; i<m_flat->m_values.count(); i++)
237 m_flat->m_nodeData << 0;
238 }
239}
240
241QDeclarativeListModel::~QDeclarativeListModel()
242{
243 if (m_agent)
244 m_agent->release();
245
246 delete m_nested;
247 delete m_flat;
248}
249
250bool QDeclarativeListModel::flatten()
251{
252 if (m_flat)
253 return true;
254
255 QList<int> roles = m_nested->roles();
256
257 QList<QHash<int, QVariant> > values;
258 bool hasNested = false;
259 for (int i=0; i<m_nested->count(); i++) {
260 values.append(m_nested->data(i, roles, &hasNested));
261 if (hasNested)
262 return false;
263 }
264
265 FlatListModel *flat = new FlatListModel(this);
266 flat->m_values = values;
267
268 for (int i=0; i<roles.count(); i++) {
269 QString s = m_nested->toString(roles[i]);
270 flat->m_roles.insert(roles[i], s);
271 flat->m_strings.insert(s, roles[i]);
272 }
273
274 flat->m_nodeData.reserve(flat->m_values.count());
275 for (int i=0; i<flat->m_values.count(); i++)
276 flat->m_nodeData << 0;
277
278 m_flat = flat;
279 delete m_nested;
280 m_nested = 0;
281 return true;
282}
283
284bool QDeclarativeListModel::inWorkerThread() const
285{
286 return m_flat && m_flat->m_parentAgent;
287}
288
289QDeclarativeListModelWorkerAgent *QDeclarativeListModel::agent()
290{
291 if (m_agent)
292 return m_agent;
293
294 if (!flatten()) {
295 qmlInfo(this) << "List contains list-type data and cannot be used from a worker script";
296 return 0;
297 }
298
299 m_agent = new QDeclarativeListModelWorkerAgent(this);
300 return m_agent;
301}
302
303QList<int> QDeclarativeListModel::roles() const
304{
305 return m_flat ? m_flat->roles() : m_nested->roles();
306}
307
308QString QDeclarativeListModel::toString(int role) const
309{
310 return m_flat ? m_flat->toString(role) : m_nested->toString(role);
311}
312
313QVariant QDeclarativeListModel::data(int index, int role) const
314{
315 if (index >= count() || index < 0)
316 return QVariant();
317
318 return m_flat ? m_flat->data(index, role) : m_nested->data(index, role);
319}
320
321/*!
322 \qmlproperty int ListModel::count
323 The number of data entries in the model.
324*/
325int QDeclarativeListModel::count() const
326{
327 return m_flat ? m_flat->count() : m_nested->count();
328}
329
330/*!
331 \qmlmethod ListModel::clear()
332
333 Deletes all content from the model.
334
335 \sa append() remove()
336*/
337void QDeclarativeListModel::clear()
338{
339 int cleared = count();
340 if (m_flat)
341 m_flat->clear();
342 else
343 m_nested->clear();
344
345 if (!inWorkerThread()) {
346 emit itemsRemoved(0, cleared);
347 emit countChanged();
348 }
349}
350
351QDeclarativeListModel *ModelNode::model(const NestedListModel *model)
352{
353 if (!modelCache) {
354 modelCache = new QDeclarativeListModel;
355 QDeclarativeEngine::setContextForObject(modelCache,QDeclarativeEngine::contextForObject(model->m_listModel));
356 modelCache->m_nested->_root = this; // ListModel defaults to nestable model
357
358 for (int i=0; i<values.count(); ++i) {
359 ModelNode *subNode = qvariant_cast<ModelNode *>(values.at(i));
360 if (subNode)
361 subNode->m_model = modelCache->m_nested;
362 }
363 }
364 return modelCache;
365}
366
367ModelObject *ModelNode::object(const NestedListModel *model)
368{
369 if (!objectCache) {
370 objectCache = new ModelObject(this,
371 const_cast<NestedListModel*>(model),
372 QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(model->m_listModel)));
373 QHash<QString, ModelNode *>::iterator it;
374 for (it = properties.begin(); it != properties.end(); ++it) {
375 objectCache->setValue(it.key().toUtf8(), model->valueForNode(*it));
376 }
377 objectCache->setNodeUpdatesEnabled(true);
378 }
379 return objectCache;
380}
381
382/*!
383 \qmlmethod ListModel::remove(int index)
384
385 Deletes the content at \a index from the model.
386
387 \sa clear()
388*/
389void QDeclarativeListModel::remove(int index)
390{
391 if (index < 0 || index >= count()) {
392 qmlInfo(this) << tr("remove: index %1 out of range").arg(index);
393 return;
394 }
395
396 if (m_flat)
397 m_flat->remove(index);
398 else
399 m_nested->remove(index);
400
401 if (!inWorkerThread()) {
402 emit itemsRemoved(index, 1);
403 emit countChanged();
404 }
405}
406
407/*!
408 \qmlmethod ListModel::insert(int index, jsobject dict)
409
410 Adds a new item to the list model at position \a index, with the
411 values in \a dict.
412
413 \code
414 fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"})
415 \endcode
416
417 The \a index must be to an existing item in the list, or one past
418 the end of the list (equivalent to append).
419
420 \sa set() append()
421*/
422void QDeclarativeListModel::insert(int index, const QScriptValue& valuemap)
423{
424 if (!valuemap.isObject() || valuemap.isArray()) {
425 qmlInfo(this) << tr("insert: value is not an object");
426 return;
427 }
428
429 if (index < 0 || index > count()) {
430 qmlInfo(this) << tr("insert: index %1 out of range").arg(index);
431 return;
432 }
433
434 bool ok = m_flat ? m_flat->insert(index, valuemap) : m_nested->insert(index, valuemap);
435 if (ok && !inWorkerThread()) {
436 emit itemsInserted(index, 1);
437 emit countChanged();
438 }
439}
440
441/*!
442 \qmlmethod ListModel::move(int from, int to, int n)
443
444 Moves \a n items \a from one position \a to another.
445
446 The from and to ranges must exist; for example, to move the first 3 items
447 to the end of the list:
448
449 \code
450 fruitModel.move(0, fruitModel.count - 3, 3)
451 \endcode
452
453 \sa append()
454*/
455void QDeclarativeListModel::move(int from, int to, int n)
456{
457 if (n==0 || from==to)
458 return;
459 if (!canMove(from, to, n)) {
460 qmlInfo(this) << tr("move: out of range");
461 return;
462 }
463
464 int origfrom = from;
465 int origto = to;
466 int orign = n;
467 if (from > to) {
468 // Only move forwards - flip if backwards moving
469 int tfrom = from;
470 int tto = to;
471 from = tto;
472 to = tto+n;
473 n = tfrom-tto;
474 }
475
476 if (m_flat)
477 m_flat->move(from, to, n);
478 else
479 m_nested->move(from, to, n);
480
481 if (!inWorkerThread())
482 emit itemsMoved(origfrom, origto, orign);
483}
484
485/*!
486 \qmlmethod ListModel::append(jsobject dict)
487
488 Adds a new item to the end of the list model, with the
489 values in \a dict.
490
491 \code
492 fruitModel.append({"cost": 5.95, "name":"Pizza"})
493 \endcode
494
495 \sa set() remove()
496*/
497void QDeclarativeListModel::append(const QScriptValue& valuemap)
498{
499 if (!valuemap.isObject() || valuemap.isArray()) {
500 qmlInfo(this) << tr("append: value is not an object");
501 return;
502 }
503
504 insert(count(), valuemap);
505}
506
507/*!
508 \qmlmethod object ListModel::get(int index)
509
510 Returns the item at \a index in the list model. This allows the item
511 data to be accessed or modified from JavaScript:
512
513 \code
514 Component.onCompleted: {
515 fruitModel.append({"cost": 5.95, "name":"Jackfruit"});
516 console.log(fruitModel.get(0).cost);
517 fruitModel.get(0).cost = 10.95;
518 }
519 \endcode
520
521 The \a index must be an element in the list.
522
523 Note that properties of the returned object that are themselves objects
524 will also be models, and this get() method is used to access elements:
525
526 \code
527 fruitModel.append(..., "attributes":
528 [{"name":"spikes","value":"7mm"},
529 {"name":"color","value":"green"}]);
530 fruitModel.get(0).attributes.get(1).value; // == "green"
531 \endcode
532
533 \warning The returned object is not guaranteed to remain valid. It
534 should not be used in \l{Property Binding}{property bindings}.
535
536 \sa append()
537*/
538QScriptValue QDeclarativeListModel::get(int index) const
539{
540 // the internal flat/nested class checks for bad index
541 return m_flat ? m_flat->get(index) : m_nested->get(index);
542}
543
544/*!
545 \qmlmethod ListModel::set(int index, jsobject dict)
546
547 Changes the item at \a index in the list model with the
548 values in \a dict. Properties not appearing in \a dict
549 are left unchanged.
550
551 \code
552 fruitModel.set(3, {"cost": 5.95, "name":"Pizza"})
553 \endcode
554
555 If \a index is equal to count() then a new item is appended to the
556 list. Otherwise, \a index must be an element in the list.
557
558 \sa append()
559*/
560void QDeclarativeListModel::set(int index, const QScriptValue& valuemap)
561{
562 if (!valuemap.isObject() || valuemap.isArray()) {
563 qmlInfo(this) << tr("set: value is not an object");
564 return;
565 }
566 if (index > count() || index < 0) {
567 qmlInfo(this) << tr("set: index %1 out of range").arg(index);
568 return;
569 }
570
571 if (index == count()) {
572 append(valuemap);
573 } else {
574 QList<int> roles;
575 if (m_flat)
576 m_flat->set(index, valuemap, &roles);
577 else
578 m_nested->set(index, valuemap, &roles);
579
580 if (!inWorkerThread())
581 emit itemsChanged(index, 1, roles);
582 }
583}
584
585/*!
586 \qmlmethod ListModel::setProperty(int index, string property, variant value)
587
588 Changes the \a property of the item at \a index in the list model to \a value.
589
590 \code
591 fruitModel.setProperty(3, "cost", 5.95)
592 \endcode
593
594 The \a index must be an element in the list.
595
596 \sa append()
597*/
598void QDeclarativeListModel::setProperty(int index, const QString& property, const QVariant& value)
599{
600 if (count() == 0 || index >= count() || index < 0) {
601 qmlInfo(this) << tr("set: index %1 out of range").arg(index);
602 return;
603 }
604
605 QList<int> roles;
606 if (m_flat)
607 m_flat->setProperty(index, property, value, &roles);
608 else
609 m_nested->setProperty(index, property, value, &roles);
610
611 if (!inWorkerThread())
612 emit itemsChanged(index, 1, roles);
613}
614
615/*!
616 \qmlmethod ListModel::sync()
617
618 Writes any unsaved changes to the list model after it has been modified
619 from a worker script.
620*/
621void QDeclarativeListModel::sync()
622{
623 // This is just a dummy method to make it look like sync() exists in
624 // ListModel (and not just QDeclarativeListModelWorkerAgent) and to let
625 // us document sync().
626 qmlInfo(this) << "List sync() can only be called from a WorkerScript";
627}
628
629bool QDeclarativeListModelParser::compileProperty(const QDeclarativeCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data)
630{
631 QList<QVariant> values = prop.assignedValues();
632 for(int ii = 0; ii < values.count(); ++ii) {
633 const QVariant &value = values.at(ii);
634
635 if(value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) {
636 QDeclarativeCustomParserNode node =
637 qvariant_cast<QDeclarativeCustomParserNode>(value);
638
639 if (node.name() != listElementTypeName) {
640 const QMetaObject *mo = resolveType(node.name());
641 if (mo != &QDeclarativeListElement::staticMetaObject) {
642 error(node, QDeclarativeListModel::tr("ListElement: cannot contain nested elements"));
643 return false;
644 }
645 listElementTypeName = node.name(); // cache right name for next time
646 }
647
648 {
649 ListInstruction li;
650 li.type = ListInstruction::Push;
651 li.dataIdx = -1;
652 instr << li;
653 }
654
655 QList<QDeclarativeCustomParserProperty> props = node.properties();
656 for(int jj = 0; jj < props.count(); ++jj) {
657 const QDeclarativeCustomParserProperty &nodeProp = props.at(jj);
658 if (nodeProp.name().isEmpty()) {
659 error(nodeProp, QDeclarativeListModel::tr("ListElement: cannot contain nested elements"));
660 return false;
661 }
662 if (nodeProp.name() == "id") {
663 error(nodeProp, QDeclarativeListModel::tr("ListElement: cannot use reserved \"id\" property"));
664 return false;
665 }
666
667 ListInstruction li;
668 int ref = data.count();
669 data.append(nodeProp.name());
670 data.append('\0');
671 li.type = ListInstruction::Set;
672 li.dataIdx = ref;
673 instr << li;
674
675 if(!compileProperty(nodeProp, instr, data))
676 return false;
677
678 li.type = ListInstruction::Pop;
679 li.dataIdx = -1;
680 instr << li;
681 }
682
683 {
684 ListInstruction li;
685 li.type = ListInstruction::Pop;
686 li.dataIdx = -1;
687 instr << li;
688 }
689
690 } else {
691
692 QDeclarativeParser::Variant variant =
693 qvariant_cast<QDeclarativeParser::Variant>(value);
694
695 int ref = data.count();
696
697 QByteArray d;
698 d += char(variant.type()); // type tag
699 if (variant.isString()) {
700 d += variant.asString().toUtf8();
701 } else if (variant.isNumber()) {
702 d += QByteArray::number(variant.asNumber(),'g',20);
703 } else if (variant.isBoolean()) {
704 d += char(variant.asBoolean());
705 } else if (variant.isScript()) {
706 if (definesEmptyList(variant.asScript())) {
707 d[0] = char(QDeclarativeParser::Variant::Invalid); // marks empty list
708 } else {
709 QByteArray script = variant.asScript().toUtf8();
710 int v = evaluateEnum(script);
711 if (v<0) {
712 if (script.startsWith("QT_TR_NOOP(\"") && script.endsWith("\")")) {
713 d[0] = char(QDeclarativeParser::Variant::String);
714 d += script.mid(12,script.length()-14);
715 } else {
716 error(prop, QDeclarativeListModel::tr("ListElement: cannot use script for property value"));
717 return false;
718 }
719 } else {
720 d[0] = char(QDeclarativeParser::Variant::Number);
721 d += QByteArray::number(v);
722 }
723 }
724 }
725 d.append('\0');
726 data.append(d);
727
728 ListInstruction li;
729 li.type = ListInstruction::Value;
730 li.dataIdx = ref;
731 instr << li;
732 }
733 }
734
735 return true;
736}
737
738QByteArray QDeclarativeListModelParser::compile(const QList<QDeclarativeCustomParserProperty> &customProps)
739{
740 QList<ListInstruction> instr;
741 QByteArray data;
742 listElementTypeName = QByteArray(); // unknown
743
744 for(int ii = 0; ii < customProps.count(); ++ii) {
745 const QDeclarativeCustomParserProperty &prop = customProps.at(ii);
746 if(!prop.name().isEmpty()) { // isn't default property
747 error(prop, QDeclarativeListModel::tr("ListModel: undefined property '%1'").arg(QString::fromUtf8(prop.name())));
748 return QByteArray();
749 }
750
751 if(!compileProperty(prop, instr, data)) {
752 return QByteArray();
753 }
754 }
755
756 int size = sizeof(ListModelData) +
757 instr.count() * sizeof(ListInstruction) +
758 data.count();
759
760 QByteArray rv;
761 rv.resize(size);
762
763 ListModelData *lmd = (ListModelData *)rv.data();
764 lmd->dataOffset = sizeof(ListModelData) +
765 instr.count() * sizeof(ListInstruction);
766 lmd->instrCount = instr.count();
767 for (int ii = 0; ii < instr.count(); ++ii)
768 lmd->instructions()[ii] = instr.at(ii);
769 ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count());
770
771 return rv;
772}
773
774void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray &d)
775{
776 QDeclarativeListModel *rv = static_cast<QDeclarativeListModel *>(obj);
777
778 ModelNode *root = new ModelNode(rv->m_nested);
779 rv->m_nested->_root = root;
780 QStack<ModelNode *> nodes;
781 nodes << root;
782
783 bool processingSet = false;
784
785 const ListModelData *lmd = (const ListModelData *)d.constData();
786 const char *data = ((const char *)lmd) + lmd->dataOffset;
787
788 for (int ii = 0; ii < lmd->instrCount; ++ii) {
789 const ListInstruction &instr = lmd->instructions()[ii];
790
791 switch(instr.type) {
792 case ListInstruction::Push:
793 {
794 ModelNode *n = nodes.top();
795 ModelNode *n2 = new ModelNode(rv->m_nested);
796 n->values << qVariantFromValue(n2);
797 nodes.push(n2);
798 if (processingSet)
799 n->isArray = true;
800 }
801 break;
802
803 case ListInstruction::Pop:
804 nodes.pop();
805 break;
806
807 case ListInstruction::Value:
808 {
809 ModelNode *n = nodes.top();
810 switch (QDeclarativeParser::Variant::Type(data[instr.dataIdx])) {
811 case QDeclarativeParser::Variant::Invalid:
812 n->isArray = true;
813 break;
814 case QDeclarativeParser::Variant::Boolean:
815 n->values.append(bool(data[1 + instr.dataIdx]));
816 break;
817 case QDeclarativeParser::Variant::Number:
818 n->values.append(QByteArray(data + 1 + instr.dataIdx).toDouble());
819 break;
820 case QDeclarativeParser::Variant::String:
821 n->values.append(QString::fromUtf8(data + 1 + instr.dataIdx));
822 break;
823 default:
824 Q_ASSERT("Format error in ListInstruction");
825 }
826
827 processingSet = false;
828 }
829 break;
830
831 case ListInstruction::Set:
832 {
833 ModelNode *n = nodes.top();
834 ModelNode *n2 = new ModelNode(rv->m_nested);
835 n->properties.insert(QString::fromUtf8(data + instr.dataIdx), n2);
836 nodes.push(n2);
837 processingSet = true;
838 }
839 break;
840 }
841 }
842
843 ModelNode *rootNode = rv->m_nested->_root;
844 for (int i=0; i<rootNode->values.count(); ++i) {
845 ModelNode *node = qvariant_cast<ModelNode *>(rootNode->values[i]);
846 node->listIndex = i;
847 node->updateListIndexes();
848 }
849}
850
851bool QDeclarativeListModelParser::definesEmptyList(const QString &s)
852{
853 if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) {
854 for (int i=1; i<s.length()-1; i++) {
855 if (!s[i].isSpace())
856 return false;
857 }
858 return true;
859 }
860 return false;
861}
862
863
864/*!
865 \qmlclass ListElement QDeclarativeListElement
866 \ingroup qml-working-with-data
867 \since 4.7
868 \brief The ListElement element defines a data item in a ListModel.
869
870 List elements are defined inside ListModel definitions, and represent items in a
871 list that will be displayed using ListView or \l Repeater items.
872
873 List elements are defined like other QML elements except that they contain
874 a collection of \e role definitions instead of properties. Using the same
875 syntax as property definitions, roles both define how the data is accessed
876 and include the data itself.
877
878 The names used for roles must begin with a lower-case letter and should be
879 common to all elements in a given model. Values must be simple constants; either
880 strings (quoted and optionally within a call to QT_TR_NOOP), boolean values
881 (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter).
882
883 \section1 Referencing Roles
884
885 The role names are used by delegates to obtain data from list elements.
886 Each role name is accessible in the delegate's scope, and refers to the
887 corresponding role in the current element. Where a role name would be
888 ambiguous to use, it can be accessed via the \l{ListView::}{model}
889 property (e.g., \c{model.cost} instead of \c{cost}).
890
891 \section1 Example Usage
892
893 The following model defines a series of list elements, each of which
894 contain "name" and "cost" roles and their associated values.
895
896 \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml model
897
898 The delegate obtains the name and cost for each element by simply referring
899 to \c name and \c cost:
900
901 \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml view
902
903 \sa ListModel
904*/
905
906FlatListModel::FlatListModel(QDeclarativeListModel *base)
907 : m_scriptEngine(0), m_listModel(base), m_scriptClass(0), m_parentAgent(0)
908{
909}
910
911FlatListModel::~FlatListModel()
912{
913 qDeleteAll(m_nodeData);
914}
915
916QVariant FlatListModel::data(int index, int role) const
917{
918 Q_ASSERT(index >= 0 && index < m_values.count());
919 if (m_values[index].contains(role))
920 return m_values[index][role];
921 return QVariant();
922}
923
924QList<int> FlatListModel::roles() const
925{
926 return m_roles.keys();
927}
928
929QString FlatListModel::toString(int role) const
930{
931 if (m_roles.contains(role))
932 return m_roles[role];
933 return QString();
934}
935
936int FlatListModel::count() const
937{
938 return m_values.count();
939}
940
941void FlatListModel::clear()
942{
943 m_values.clear();
944
945 qDeleteAll(m_nodeData);
946 m_nodeData.clear();
947}
948
949void FlatListModel::remove(int index)
950{
951 m_values.removeAt(index);
952 removedNode(index);
953}
954
955bool FlatListModel::insert(int index, const QScriptValue &value)
956{
957 Q_ASSERT(index >= 0 && index <= m_values.count());
958
959 QHash<int, QVariant> row;
960 if (!addValue(value, &row, 0))
961 return false;
962
963 m_values.insert(index, row);
964 insertedNode(index);
965
966 return true;
967}
968
969QScriptValue FlatListModel::get(int index) const
970{
971 QScriptEngine *scriptEngine = m_scriptEngine ? m_scriptEngine : QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(m_listModel));
972
973 if (!scriptEngine)
974 return 0;
975
976 if (index < 0 || index >= m_values.count())
977 return scriptEngine->undefinedValue();
978
979 FlatListModel *that = const_cast<FlatListModel*>(this);
980 if (!m_scriptClass)
981 that->m_scriptClass = new FlatListScriptClass(that, scriptEngine);
982
983 FlatNodeData *data = m_nodeData.value(index);
984 if (!data) {
985 data = new FlatNodeData(index);
986 that->m_nodeData.replace(index, data);
987 }
988
989 return QScriptDeclarativeClass::newObject(scriptEngine, m_scriptClass, new FlatNodeObjectData(data));
990}
991
992void FlatListModel::set(int index, const QScriptValue &value, QList<int> *roles)
993{
994 Q_ASSERT(index >= 0 && index < m_values.count());
995
996 QHash<int, QVariant> row = m_values[index];
997 if (addValue(value, &row, roles))
998 m_values[index] = row;
999}
1000
1001void FlatListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles)
1002{
1003 Q_ASSERT(index >= 0 && index < m_values.count());
1004
1005 QHash<QString, int>::Iterator iter = m_strings.find(property);
1006 int role;
1007 if (iter == m_strings.end()) {
1008 role = m_roles.count();
1009 m_roles.insert(role, property);
1010 m_strings.insert(property, role);
1011 } else {
1012 role = iter.value();
1013 }
1014 roles->append(role);
1015
1016 m_values[index][role] = value;
1017}
1018
1019void FlatListModel::move(int from, int to, int n)
1020{
1021 qdeclarativelistmodel_move<QList<QHash<int, QVariant> > >(from, to, n, &m_values);
1022 moveNodes(from, to, n);
1023}
1024
1025bool FlatListModel::addValue(const QScriptValue &value, QHash<int, QVariant> *row, QList<int> *roles)
1026{
1027 QScriptValueIterator it(value);
1028 while (it.hasNext()) {
1029 it.next();
1030 QScriptValue value = it.value();
1031 if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) {
1032 qmlInfo(m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script";
1033 return false;
1034 }
1035
1036 QString name = it.name();
1037 QVariant v = it.value().toVariant();
1038
1039 QHash<QString, int>::Iterator iter = m_strings.find(name);
1040 if (iter == m_strings.end()) {
1041 int role = m_roles.count();
1042 m_roles.insert(role, name);
1043 iter = m_strings.insert(name, role);
1044 if (roles)
1045 roles->append(role);
1046 }
1047 row->insert(*iter, v);
1048 }
1049 return true;
1050}
1051
1052void FlatListModel::insertedNode(int index)
1053{
1054 if (index >= 0 && index <= m_values.count()) {
1055 m_nodeData.insert(index, 0);
1056
1057 for (int i=index + 1; i<m_nodeData.count(); i++) {
1058 if (m_nodeData[i])
1059 m_nodeData[i]->index = i;
1060 }
1061 }
1062}
1063
1064void FlatListModel::removedNode(int index)
1065{
1066 if (index >= 0 && index < m_nodeData.count()) {
1067 delete m_nodeData.takeAt(index);
1068
1069 for (int i=index; i<m_nodeData.count(); i++) {
1070 if (m_nodeData[i])
1071 m_nodeData[i]->index = i;
1072 }
1073 }
1074}
1075
1076void FlatListModel::moveNodes(int from, int to, int n)
1077{
1078 if (!m_listModel->canMove(from, to, n))
1079 return;
1080
1081 qdeclarativelistmodel_move<QList<FlatNodeData *> >(from, to, n, &m_nodeData);
1082
1083 for (int i=from; i<from + (to-from); i++) {
1084 if (m_nodeData[i])
1085 m_nodeData[i]->index = i;
1086 }
1087}
1088
1089
1090
1091FlatNodeData::~FlatNodeData()
1092{
1093 for (QSet<FlatNodeObjectData *>::Iterator iter = objects.begin(); iter != objects.end(); ++iter) {
1094 FlatNodeObjectData *data = *iter;
1095 data->nodeData = 0;
1096 }
1097}
1098
1099void FlatNodeData::addData(FlatNodeObjectData *data)
1100{
1101 objects.insert(data);
1102}
1103
1104void FlatNodeData::removeData(FlatNodeObjectData *data)
1105{
1106 objects.remove(data);
1107}
1108
1109
1110FlatListScriptClass::FlatListScriptClass(FlatListModel *model, QScriptEngine *seng)
1111 : QScriptDeclarativeClass(seng),
1112 m_model(model)
1113{
1114}
1115
1116QScriptDeclarativeClass::Value FlatListScriptClass::property(Object *obj, const Identifier &name)
1117{
1118 FlatNodeObjectData *objData = static_cast<FlatNodeObjectData*>(obj);
1119 if (!objData->nodeData) // item at this index has been deleted
1120 return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue());
1121
1122 int index = objData->nodeData->index;
1123 QString propName = toString(name);
1124 int role = m_model->m_strings.value(propName, -1);
1125
1126 if (role >= 0 && index >=0 ) {
1127 const QHash<int, QVariant> &row = m_model->m_values[index];
1128 QScriptValue sv = engine()->toScriptValue<QVariant>(row[role]);
1129 return QScriptDeclarativeClass::Value(engine(), sv);
1130 }
1131
1132 return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue());
1133}
1134
1135void FlatListScriptClass::setProperty(Object *obj, const Identifier &name, const QScriptValue &value)
1136{
1137 if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) {
1138 qmlInfo(m_model->m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script";
1139 return;
1140 }
1141
1142 FlatNodeObjectData *objData = static_cast<FlatNodeObjectData*>(obj);
1143 if (!objData->nodeData) // item at this index has been deleted
1144 return;
1145
1146 int index = objData->nodeData->index;
1147 QString propName = toString(name);
1148
1149 int role = m_model->m_strings.value(propName, -1);
1150 if (role >= 0 && index >= 0) {
1151 QHash<int, QVariant> &row = m_model->m_values[index];
1152 row[role] = value.toVariant();
1153
1154 if (m_model->m_parentAgent) {
1155 // This is the list in the worker thread, so tell the agent to
1156 // emit itemsChanged() later
1157 m_model->m_parentAgent->changedData(index, 1);
1158 } else {
1159 // This is the list in the main thread, so emit itemsChanged()
1160 QList<int> roles;
1161 roles << role;
1162 emit m_model->m_listModel->itemsChanged(index, 1, roles);
1163 }
1164 }
1165}
1166
1167QScriptClass::QueryFlags FlatListScriptClass::queryProperty(Object *, const Identifier &, QScriptClass::QueryFlags)
1168{
1169 return (QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess);
1170}
1171
1172bool FlatListScriptClass::compare(Object *obj1, Object *obj2)
1173{
1174 FlatNodeObjectData *data1 = static_cast<FlatNodeObjectData*>(obj1);
1175 FlatNodeObjectData *data2 = static_cast<FlatNodeObjectData*>(obj2);
1176
1177 if (!data1->nodeData || !data2->nodeData)
1178 return false;
1179
1180 return data1->nodeData->index == data2->nodeData->index;
1181}
1182
1183
1184
1185NestedListModel::NestedListModel(QDeclarativeListModel *base)
1186 : _root(0), m_ownsRoot(false), m_listModel(base), _rolesOk(false)
1187{
1188}
1189
1190NestedListModel::~NestedListModel()
1191{
1192 if (m_ownsRoot)
1193 delete _root;
1194}
1195
1196QVariant NestedListModel::valueForNode(ModelNode *node, bool *hasNested) const
1197{
1198 QObject *rv = 0;
1199 if (hasNested)
1200 *hasNested = false;
1201
1202 if (node->isArray) {
1203 // List
1204 rv = node->model(this);
1205 if (hasNested)
1206 *hasNested = true;
1207 } else {
1208 if (!node->properties.isEmpty()) {
1209 // Object
1210 rv = node->object(this);
1211 } else if (node->values.count() == 0) {
1212 // Invalid
1213 return QVariant();
1214 } else if (node->values.count() == 1) {
1215 // Value
1216 QVariant &var = node->values[0];
1217 ModelNode *valueNode = qvariant_cast<ModelNode *>(var);
1218 if (valueNode) {
1219 if (!valueNode->properties.isEmpty())
1220 rv = valueNode->object(this);
1221 else
1222 rv = valueNode->model(this);
1223 } else {
1224 return var;
1225 }
1226 }
1227 }
1228
1229 if (rv) {
1230 return QVariant::fromValue(rv);
1231 } else {
1232 return QVariant();
1233 }
1234}
1235
1236QHash<int,QVariant> NestedListModel::data(int index, const QList<int> &roles, bool *hasNested) const
1237{
1238 Q_ASSERT(_root && index >= 0 && index < _root->values.count());
1239 checkRoles();
1240 QHash<int, QVariant> rv;
1241
1242 ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1243 if (!node)
1244 return rv;
1245
1246 for (int ii = 0; ii < roles.count(); ++ii) {
1247 const QString &roleString = roleStrings.at(roles.at(ii));
1248
1249 QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString);
1250 if (iter != node->properties.end()) {
1251 ModelNode *row = *iter;
1252 rv.insert(roles.at(ii), valueForNode(row, hasNested));
1253 }
1254 }
1255
1256 return rv;
1257}
1258
1259QVariant NestedListModel::data(int index, int role) const
1260{
1261 Q_ASSERT(_root && index >= 0 && index < _root->values.count());
1262 checkRoles();
1263 QVariant rv;
1264 if (roleStrings.count() < role)
1265 return rv;
1266
1267 ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1268 if (!node)
1269 return rv;
1270
1271 const QString &roleString = roleStrings.at(role);
1272
1273 QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString);
1274 if (iter != node->properties.end()) {
1275 ModelNode *row = *iter;
1276 rv = valueForNode(row);
1277 }
1278
1279 return rv;
1280}
1281
1282int NestedListModel::count() const
1283{
1284 if (!_root) return 0;
1285 return _root->values.count();
1286}
1287
1288void NestedListModel::clear()
1289{
1290 _rolesOk = false;
1291 roleStrings.clear();
1292
1293 if (_root)
1294 _root->clear();
1295}
1296
1297void NestedListModel::remove(int index)
1298{
1299 if (!_root)
1300 return;
1301 ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1302 _root->values.removeAt(index);
1303 if (node)
1304 delete node;
1305}
1306
1307bool NestedListModel::insert(int index, const QScriptValue& valuemap)
1308{
1309 if (!_root) {
1310 _root = new ModelNode(this);
1311 m_ownsRoot = true;
1312 }
1313
1314 ModelNode *mn = new ModelNode(this);
1315 mn->listIndex = index;
1316 mn->setObjectValue(valuemap);
1317 _root->values.insert(index,qVariantFromValue(mn));
1318 return true;
1319}
1320
1321void NestedListModel::move(int from, int to, int n)
1322{
1323 if (!_root)
1324 return;
1325 qdeclarativelistmodel_move<QVariantList>(from, to, n, &_root->values);
1326}
1327
1328QScriptValue NestedListModel::get(int index) const
1329{
1330 QDeclarativeEngine *eng = qmlEngine(m_listModel);
1331 if (!eng)
1332 return 0;
1333
1334 if (index < 0 || index >= count()) {
1335 QScriptEngine *seng = QDeclarativeEnginePrivate::getScriptEngine(eng);
1336 if (seng)
1337 return seng->undefinedValue();
1338 return 0;
1339 }
1340
1341 ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1342 if (!node)
1343 return 0;
1344
1345 return QDeclarativeEnginePrivate::qmlScriptObject(node->object(this), eng);
1346}
1347
1348void NestedListModel::set(int index, const QScriptValue& valuemap, QList<int> *roles)
1349{
1350 Q_ASSERT(index >=0 && index < count());
1351
1352 ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1353 node->setObjectValue(valuemap);
1354
1355 QScriptValueIterator it(valuemap);
1356 while (it.hasNext()) {
1357 it.next();
1358 int r = roleStrings.indexOf(it.name());
1359 if (r < 0) {
1360 r = roleStrings.count();
1361 roleStrings << it.name();
1362 }
1363 roles->append(r);
1364 }
1365}
1366
1367void NestedListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles)
1368{
1369 Q_ASSERT(index >=0 && index < count());
1370
1371 ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1372 node->setProperty(property, value);
1373
1374 int r = roleStrings.indexOf(property);
1375 if (r < 0) {
1376 r = roleStrings.count();
1377 roleStrings << property;
1378 }
1379 roles->append(r);
1380}
1381
1382void NestedListModel::checkRoles() const
1383{
1384 if (_rolesOk || !_root)
1385 return;
1386
1387 for (int i = 0; i<_root->values.count(); ++i) {
1388 ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(i));
1389 if (node) {
1390 foreach (const QString &role, node->properties.keys()) {
1391 if (!roleStrings.contains(role))
1392 roleStrings.append(role);
1393 }
1394 }
1395 }
1396
1397 _rolesOk = true;
1398}
1399
1400QList<int> NestedListModel::roles() const
1401{
1402 checkRoles();
1403 QList<int> rv;
1404 for (int ii = 0; ii < roleStrings.count(); ++ii)
1405 rv << ii;
1406 return rv;
1407}
1408
1409QString NestedListModel::toString(int role) const
1410{
1411 checkRoles();
1412 if (role < roleStrings.count())
1413 return roleStrings.at(role);
1414 else
1415 return QString();
1416}
1417
1418
1419ModelNode::ModelNode(NestedListModel *model)
1420: modelCache(0), objectCache(0), isArray(false), m_model(model), listIndex(-1)
1421{
1422}
1423
1424ModelNode::~ModelNode()
1425{
1426 clear();
1427 if (modelCache) { modelCache->m_nested->_root = 0/* ==this */; delete modelCache; modelCache = 0; }
1428 if (objectCache) { delete objectCache; objectCache = 0; }
1429}
1430
1431void ModelNode::clear()
1432{
1433 ModelNode *node;
1434 for (int ii = 0; ii < values.count(); ++ii) {
1435 node = qvariant_cast<ModelNode *>(values.at(ii));
1436 if (node) { delete node; node = 0; }
1437 }
1438 values.clear();
1439
1440 qDeleteAll(properties.values());
1441 properties.clear();
1442}
1443
1444void ModelNode::setObjectValue(const QScriptValue& valuemap, bool writeToCache) {
1445 QScriptValueIterator it(valuemap);
1446 while (it.hasNext()) {
1447 it.next();
1448 ModelNode *value = new ModelNode(m_model);
1449 QScriptValue v = it.value();
1450 if (v.isArray()) {
1451 value->isArray = true;
1452 value->setListValue(v);
1453 } else {
1454 value->values << v.toVariant();
1455 if (writeToCache && objectCache)
1456 objectCache->setValue(it.name().toUtf8(), value->values.last());
1457 }
1458 if (properties.contains(it.name()))
1459 delete properties[it.name()];
1460 properties.insert(it.name(), value);
1461 }
1462}
1463
1464void ModelNode::setListValue(const QScriptValue& valuelist) {
1465 values.clear();
1466 int size = valuelist.property(QLatin1String("length")).toInt32();
1467 for (int i=0; i<size; i++) {
1468 ModelNode *value = new ModelNode(m_model);
1469 QScriptValue v = valuelist.property(i);
1470 if (v.isArray()) {
1471 value->isArray = true;
1472 value->setListValue(v);
1473 } else if (v.isObject()) {
1474 value->listIndex = i;
1475 value->setObjectValue(v);
1476 } else {
1477 value->listIndex = i;
1478 value->values << v.toVariant();
1479 }
1480 values.append(qVariantFromValue(value));
1481 }
1482}
1483
1484void ModelNode::setProperty(const QString& prop, const QVariant& val) {
1485 QHash<QString, ModelNode *>::const_iterator it = properties.find(prop);
1486 if (it != properties.end()) {
1487 (*it)->values[0] = val;
1488 } else {
1489 ModelNode *n = new ModelNode(m_model);
1490 n->values << val;
1491 properties.insert(prop,n);
1492 }
1493 if (objectCache)
1494 objectCache->setValue(prop.toUtf8(), val);
1495}
1496
1497void ModelNode::updateListIndexes()
1498{
1499 for (QHash<QString, ModelNode *>::ConstIterator iter = properties.begin(); iter != properties.end(); ++iter) {
1500 ModelNode *node = iter.value();
1501 if (node->isArray) {
1502 for (int i=0; i<node->values.count(); ++i) {
1503 ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(i));
1504 if (subNode)
1505 subNode->listIndex = i;
1506 }
1507 }
1508 node->updateListIndexes();
1509 }
1510}
1511
1512/*
1513 Need to call this to emit itemsChanged() for modifications outside of set()
1514 and setProperty(), i.e. if an item returned from get() is modified
1515*/
1516void ModelNode::changedProperty(const QString &name) const
1517{
1518 if (listIndex < 0)
1519 return;
1520
1521 m_model->checkRoles();
1522 QList<int> roles;
1523 int role = m_model->roleStrings.indexOf(name);
1524 if (role < 0)
1525 roles = m_model->roles();
1526 else
1527 roles << role;
1528 emit m_model->m_listModel->itemsChanged(listIndex, 1, roles);
1529}
1530
1531void ModelNode::dump(ModelNode *node, int ind)
1532{
1533 QByteArray indentBa(ind * 4, ' ');
1534 const char *indent = indentBa.constData();
1535
1536 for (int ii = 0; ii < node->values.count(); ++ii) {
1537 ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(ii));
1538 if (subNode) {
1539 qWarning().nospace() << indent << "Sub-node " << ii;
1540 dump(subNode, ind + 1);
1541 } else {
1542 qWarning().nospace() << indent << "Sub-node " << ii << ": " << node->values.at(ii).toString();
1543 }
1544 }
1545
1546 for (QHash<QString, ModelNode *>::ConstIterator iter = node->properties.begin(); iter != node->properties.end(); ++iter) {
1547 qWarning().nospace() << indent << "Property " << iter.key() << ':';
1548 dump(iter.value(), ind + 1);
1549 }
1550}
1551
1552ModelObject::ModelObject(ModelNode *node, NestedListModel *model, QScriptEngine *seng)
1553 : m_model(model),
1554 m_node(node),
1555 m_meta(new ModelNodeMetaObject(seng, this))
1556{
1557}
1558
1559void ModelObject::setValue(const QByteArray &name, const QVariant &val)
1560{
1561 m_meta->setValue(name, val);
1562 setProperty(name.constData(), val);
1563}
1564
1565void ModelObject::setNodeUpdatesEnabled(bool enable)
1566{
1567 m_meta->m_enabled = enable;
1568}
1569
1570
1571ModelNodeMetaObject::ModelNodeMetaObject(QScriptEngine *seng, ModelObject *object)
1572 : QDeclarativeOpenMetaObject(object),
1573 m_enabled(false),
1574 m_seng(seng),
1575 m_obj(object)
1576{
1577}
1578
1579void ModelNodeMetaObject::propertyWritten(int index)
1580{
1581 if (!m_enabled)
1582 return;
1583
1584 QString propName = QString::fromUtf8(name(index));
1585 QVariant value = operator[](index);
1586
1587 QScriptValue sv = m_seng->newObject();
1588 sv.setProperty(propName, m_seng->newVariant(value));
1589 m_obj->m_node->setObjectValue(sv, false);
1590
1591 m_obj->m_node->changedProperty(propName);
1592}
1593
1594
1595QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.