|
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/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 |
|
57 Q_DECLARE_METATYPE(QListModelInterface *) |
|
58 |
|
59 QT_BEGIN_NAMESPACE |
|
60 |
|
61 #define DATA_ROLE_ID 1 |
|
62 #define DATA_ROLE_NAME "data" |
|
63 |
|
64 QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListModelData::instructions() const |
|
65 { |
|
66 return (QDeclarativeListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData)); |
|
67 } |
|
68 |
|
69 /*! |
|
70 \qmlclass ListModel QDeclarativeListModel |
|
71 \since 4.7 |
|
72 \brief The ListModel element defines a free-form list data source. |
|
73 |
|
74 The ListModel is a simple hierarchy of elements containing data roles. The contents can |
|
75 be defined dynamically, or explicitly in QML: |
|
76 |
|
77 For example: |
|
78 |
|
79 \code |
|
80 ListModel { |
|
81 id: fruitModel |
|
82 ListElement { |
|
83 name: "Apple" |
|
84 cost: 2.45 |
|
85 } |
|
86 ListElement { |
|
87 name: "Orange" |
|
88 cost: 3.25 |
|
89 } |
|
90 ListElement { |
|
91 name: "Banana" |
|
92 cost: 1.95 |
|
93 } |
|
94 } |
|
95 \endcode |
|
96 |
|
97 Roles (properties) must begin with a lower-case letter.The above example defines a |
|
98 ListModel containing three elements, with the roles "name" and "cost". |
|
99 |
|
100 Values must be simple constants - either strings (quoted), bools (true, false), numbers, |
|
101 or enum values (like Text.AlignHCenter). |
|
102 |
|
103 The defined model can be used in views such as ListView: |
|
104 \code |
|
105 Component { |
|
106 id: fruitDelegate |
|
107 Item { |
|
108 width: 200; height: 50 |
|
109 Text { text: name } |
|
110 Text { text: '$'+cost; anchors.right: parent.right } |
|
111 } |
|
112 } |
|
113 |
|
114 ListView { |
|
115 model: fruitModel |
|
116 delegate: fruitDelegate |
|
117 anchors.fill: parent |
|
118 } |
|
119 \endcode |
|
120 |
|
121 It is possible for roles to contain list data. In the example below we create a list of fruit attributes: |
|
122 |
|
123 \code |
|
124 ListModel { |
|
125 id: fruitModel |
|
126 ListElement { |
|
127 name: "Apple" |
|
128 cost: 2.45 |
|
129 attributes: [ |
|
130 ListElement { description: "Core" }, |
|
131 ListElement { description: "Deciduous" } |
|
132 ] |
|
133 } |
|
134 ListElement { |
|
135 name: "Orange" |
|
136 cost: 3.25 |
|
137 attributes: [ |
|
138 ListElement { description: "Citrus" } |
|
139 ] |
|
140 } |
|
141 ListElement { |
|
142 name: "Banana" |
|
143 cost: 1.95 |
|
144 attributes: [ |
|
145 ListElement { description: "Tropical" }, |
|
146 ListElement { description: "Seedless" } |
|
147 ] |
|
148 } |
|
149 } |
|
150 \endcode |
|
151 |
|
152 The delegate below will list all the fruit attributes: |
|
153 \code |
|
154 Component { |
|
155 id: fruitDelegate |
|
156 Item { |
|
157 width: 200; height: 50 |
|
158 Text { id: name; text: name } |
|
159 Text { text: '$'+cost; anchors.right: parent.right } |
|
160 Row { |
|
161 anchors.top: name.bottom |
|
162 spacing: 5 |
|
163 Text { text: "Attributes:" } |
|
164 Repeater { |
|
165 model: attributes |
|
166 Component { Text { text: description } } |
|
167 } |
|
168 } |
|
169 } |
|
170 } |
|
171 \endcode |
|
172 |
|
173 \section2 Modifying list models |
|
174 |
|
175 The content of a ListModel may be created and modified using the clear(), |
|
176 append(), and set() methods. For example: |
|
177 |
|
178 \code |
|
179 Component { |
|
180 id: fruitDelegate |
|
181 Item { |
|
182 width: 200; height: 50 |
|
183 Text { text: name } |
|
184 Text { text: '$'+cost; anchors.right: parent.right } |
|
185 |
|
186 // Double the price when clicked. |
|
187 MouseArea { |
|
188 anchors.fill: parent |
|
189 onClicked: fruitModel.set(index, "cost", cost*2) |
|
190 } |
|
191 } |
|
192 } |
|
193 \endcode |
|
194 |
|
195 When creating content dynamically, note that the set of available properties cannot be changed |
|
196 except by first clearing the model - whatever properties are first added are then the |
|
197 only permitted properties in the model. |
|
198 |
|
199 |
|
200 \section2 Using threaded list models with WorkerScript |
|
201 |
|
202 ListModel can be used together with WorkerScript access a list model |
|
203 from multiple threads. This is useful if list modifications are |
|
204 synchronous and take some time: the list operations can be moved to a |
|
205 different thread to avoid blocking of the main GUI thread. |
|
206 |
|
207 Here is an example that uses WorkerScript to periodically append the |
|
208 current time to a list model: |
|
209 |
|
210 \snippet examples/declarative/threading/threadedlistmodel/timedisplay.qml 0 |
|
211 |
|
212 The included file, \tt dataloader.js, looks like this: |
|
213 |
|
214 \snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0 |
|
215 |
|
216 The application's \tt Timer object periodically sends a message to the |
|
217 worker script by calling \tt WorkerScript::sendMessage(). When this message |
|
218 is received, \tt WorkerScript.onMessage() is invoked in |
|
219 \tt dataloader.js, which appends the current time to the list model. |
|
220 |
|
221 Note the call to sync() from the \c WorkerScript.onMessage() handler. |
|
222 You must call sync() or else the changes made to the list from the external |
|
223 thread will not be reflected in the list model in the main thread. |
|
224 |
|
225 \section3 Limitations |
|
226 |
|
227 If a list model is to be accessed from a WorkerScript, it \bold cannot |
|
228 contain list data. So, the following model cannot be used from a WorkerScript |
|
229 because of the list contained in the "attributes" property: |
|
230 |
|
231 \code |
|
232 ListModel { |
|
233 id: fruitModel |
|
234 ListElement { |
|
235 name: "Apple" |
|
236 cost: 2.45 |
|
237 attributes: [ |
|
238 ListElement { description: "Core" }, |
|
239 ListElement { description: "Deciduous" } |
|
240 ] |
|
241 } |
|
242 } |
|
243 \endcode |
|
244 |
|
245 In addition, the WorkerScript cannot add any list data to the model. |
|
246 |
|
247 \sa {qmlmodels}{Data Models}, WorkerScript, QtDeclarative |
|
248 */ |
|
249 |
|
250 |
|
251 /* |
|
252 A ListModel internally uses either a NestedListModel or FlatListModel. |
|
253 |
|
254 A NestedListModel can contain lists of ListElements (which |
|
255 when retrieved from get() is accessible as a list model within the list |
|
256 model) whereas a FlatListModel cannot. |
|
257 |
|
258 ListModel uses a NestedListModel to begin with, and if the model is later |
|
259 used from a WorkerScript, it changes to use a FlatListModel instead. This |
|
260 is because ModelNode (which abstracts the nested list model data) needs |
|
261 access to the declarative engine and script engine, which cannot be |
|
262 safely used from outside of the main thread. |
|
263 */ |
|
264 |
|
265 QDeclarativeListModel::QDeclarativeListModel(QObject *parent) |
|
266 : QListModelInterface(parent), m_agent(0), m_nested(new NestedListModel(this)), m_flat(0), m_isWorkerCopy(false) |
|
267 { |
|
268 } |
|
269 |
|
270 QDeclarativeListModel::QDeclarativeListModel(bool workerCopy, QObject *parent) |
|
271 : QListModelInterface(parent), m_agent(0), m_nested(0), m_flat(0), m_isWorkerCopy(workerCopy) |
|
272 { |
|
273 if (workerCopy) |
|
274 m_flat = new FlatListModel(this); |
|
275 else |
|
276 m_nested = new NestedListModel(this); |
|
277 } |
|
278 |
|
279 QDeclarativeListModel::~QDeclarativeListModel() |
|
280 { |
|
281 if (m_agent) |
|
282 m_agent->release(); |
|
283 |
|
284 delete m_nested; |
|
285 delete m_flat; |
|
286 } |
|
287 |
|
288 bool QDeclarativeListModel::flatten() |
|
289 { |
|
290 if (m_flat) |
|
291 return true; |
|
292 |
|
293 QList<int> roles = m_nested->roles(); |
|
294 |
|
295 QList<QHash<int, QVariant> > values; |
|
296 bool hasNested = false; |
|
297 for (int i=0; i<m_nested->count(); i++) { |
|
298 values.append(m_nested->data(i, roles, &hasNested)); |
|
299 if (hasNested) |
|
300 return false; |
|
301 } |
|
302 |
|
303 FlatListModel *flat = new FlatListModel(this); |
|
304 flat->m_values = values; |
|
305 |
|
306 for (int i=0; i<roles.count(); i++) { |
|
307 QString s = m_nested->toString(roles[i]); |
|
308 flat->m_roles.insert(roles[i], s); |
|
309 flat->m_strings.insert(s, roles[i]); |
|
310 } |
|
311 |
|
312 m_flat = flat; |
|
313 delete m_nested; |
|
314 m_nested = 0; |
|
315 return true; |
|
316 } |
|
317 |
|
318 QDeclarativeListModelWorkerAgent *QDeclarativeListModel::agent() |
|
319 { |
|
320 if (m_agent) |
|
321 return m_agent; |
|
322 |
|
323 if (!flatten()) { |
|
324 qmlInfo(this) << "List contains nested list values and cannot be used from a worker script"; |
|
325 return 0; |
|
326 } |
|
327 |
|
328 m_agent = new QDeclarativeListModelWorkerAgent(this); |
|
329 return m_agent; |
|
330 } |
|
331 |
|
332 QList<int> QDeclarativeListModel::roles() const |
|
333 { |
|
334 return m_flat ? m_flat->roles() : m_nested->roles(); |
|
335 } |
|
336 |
|
337 QString QDeclarativeListModel::toString(int role) const |
|
338 { |
|
339 return m_flat ? m_flat->toString(role) : m_nested->toString(role); |
|
340 } |
|
341 |
|
342 QHash<int,QVariant> QDeclarativeListModel::data(int index, const QList<int> &roles) const |
|
343 { |
|
344 if (index >= count() || index < 0) |
|
345 return QHash<int, QVariant>(); |
|
346 |
|
347 return m_flat ? m_flat->data(index, roles) : m_nested->data(index, roles); |
|
348 } |
|
349 |
|
350 QVariant QDeclarativeListModel::data(int index, int role) const |
|
351 { |
|
352 if (index >= count() || index < 0) |
|
353 return QVariant(); |
|
354 |
|
355 return m_flat ? m_flat->data(index, role) : m_nested->data(index, role); |
|
356 } |
|
357 |
|
358 /*! |
|
359 \qmlproperty int ListModel::count |
|
360 The number of data entries in the model. |
|
361 */ |
|
362 int QDeclarativeListModel::count() const |
|
363 { |
|
364 return m_flat ? m_flat->count() : m_nested->count(); |
|
365 } |
|
366 |
|
367 /*! |
|
368 \qmlmethod ListModel::clear() |
|
369 |
|
370 Deletes all content from the model. The properties are cleared such that |
|
371 different properties may be set on subsequent additions. |
|
372 |
|
373 \sa append() remove() |
|
374 */ |
|
375 void QDeclarativeListModel::clear() |
|
376 { |
|
377 int cleared = count(); |
|
378 if (m_flat) |
|
379 m_flat->clear(); |
|
380 else |
|
381 m_nested->clear(); |
|
382 |
|
383 if (!m_isWorkerCopy) { |
|
384 emit itemsRemoved(0, cleared); |
|
385 emit countChanged(); |
|
386 } |
|
387 } |
|
388 |
|
389 /*! |
|
390 \qmlmethod ListModel::remove(int index) |
|
391 |
|
392 Deletes the content at \a index from the model. |
|
393 |
|
394 \sa clear() |
|
395 */ |
|
396 void QDeclarativeListModel::remove(int index) |
|
397 { |
|
398 if (index < 0 || index >= count()) { |
|
399 qmlInfo(this) << tr("remove: index %1 out of range").arg(index); |
|
400 return; |
|
401 } |
|
402 |
|
403 if (m_flat) |
|
404 m_flat->remove(index); |
|
405 else |
|
406 m_nested->remove(index); |
|
407 |
|
408 if (!m_isWorkerCopy) { |
|
409 emit itemsRemoved(index, 1); |
|
410 emit countChanged(); |
|
411 } |
|
412 } |
|
413 |
|
414 /*! |
|
415 \qmlmethod ListModel::insert(int index, jsobject dict) |
|
416 |
|
417 Adds a new item to the list model at position \a index, with the |
|
418 values in \a dict. |
|
419 |
|
420 \code |
|
421 fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"}) |
|
422 \endcode |
|
423 |
|
424 The \a index must be to an existing item in the list, or one past |
|
425 the end of the list (equivalent to append). |
|
426 |
|
427 \sa set() append() |
|
428 */ |
|
429 void QDeclarativeListModel::insert(int index, const QScriptValue& valuemap) |
|
430 { |
|
431 if (!valuemap.isObject() || valuemap.isArray()) { |
|
432 qmlInfo(this) << tr("insert: value is not an object"); |
|
433 return; |
|
434 } |
|
435 |
|
436 if (index < 0 || index > count()) { |
|
437 qmlInfo(this) << tr("insert: index %1 out of range").arg(index); |
|
438 return; |
|
439 } |
|
440 |
|
441 bool ok = m_flat ? m_flat->insert(index, valuemap) : m_nested->insert(index, valuemap); |
|
442 if (ok && !m_isWorkerCopy) { |
|
443 emit itemsInserted(index, 1); |
|
444 emit countChanged(); |
|
445 } |
|
446 } |
|
447 |
|
448 /*! |
|
449 \qmlmethod ListModel::move(int from, int to, int n) |
|
450 |
|
451 Moves \a n items \a from one position \a to another. |
|
452 |
|
453 The from and to ranges must exist; for example, to move the first 3 items |
|
454 to the end of the list: |
|
455 |
|
456 \code |
|
457 fruitModel.move(0,fruitModel.count-3,3) |
|
458 \endcode |
|
459 |
|
460 \sa append() |
|
461 */ |
|
462 void QDeclarativeListModel::move(int from, int to, int n) |
|
463 { |
|
464 if (n==0 || from==to) |
|
465 return; |
|
466 if (from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0) { |
|
467 qmlInfo(this) << tr("move: out of range"); |
|
468 return; |
|
469 } |
|
470 |
|
471 int origfrom = from; |
|
472 int origto = to; |
|
473 int orign = n; |
|
474 if (from > to) { |
|
475 // Only move forwards - flip if backwards moving |
|
476 int tfrom = from; |
|
477 int tto = to; |
|
478 from = tto; |
|
479 to = tto+n; |
|
480 n = tfrom-tto; |
|
481 } |
|
482 |
|
483 if (m_flat) |
|
484 m_flat->move(from, to, n); |
|
485 else |
|
486 m_nested->move(from, to, n); |
|
487 |
|
488 if (!m_isWorkerCopy) |
|
489 emit itemsMoved(origfrom, origto, orign); |
|
490 } |
|
491 |
|
492 /*! |
|
493 \qmlmethod ListModel::append(jsobject dict) |
|
494 |
|
495 Adds a new item to the end of the list model, with the |
|
496 values in \a dict. |
|
497 |
|
498 \code |
|
499 fruitModel.append({"cost": 5.95, "name":"Pizza"}) |
|
500 \endcode |
|
501 |
|
502 \sa set() remove() |
|
503 */ |
|
504 void QDeclarativeListModel::append(const QScriptValue& valuemap) |
|
505 { |
|
506 if (!valuemap.isObject() || valuemap.isArray()) { |
|
507 qmlInfo(this) << tr("append: value is not an object"); |
|
508 return; |
|
509 } |
|
510 |
|
511 insert(count(), valuemap); |
|
512 } |
|
513 |
|
514 /*! |
|
515 \qmlmethod object ListModel::get(int index) |
|
516 |
|
517 Returns the item at \a index in the list model. |
|
518 |
|
519 \code |
|
520 fruitModel.append({"cost": 5.95, "name":"Jackfruit"}) |
|
521 fruitModel.get(0).cost |
|
522 \endcode |
|
523 |
|
524 The \a index must be an element in the list. |
|
525 |
|
526 Note that properties of the returned object that are themselves objects |
|
527 will also be models, and this get() method is used to access elements: |
|
528 |
|
529 \code |
|
530 fruitModel.append(..., "attributes": |
|
531 [{"name":"spikes","value":"7mm"}, |
|
532 {"name":"color","value":"green"}]); |
|
533 fruitModel.get(0).attributes.get(1).value; // == "green" |
|
534 \endcode |
|
535 |
|
536 \sa append() |
|
537 */ |
|
538 QScriptValue 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 The \a index must be an element in the list. |
|
556 |
|
557 \sa append() |
|
558 */ |
|
559 void QDeclarativeListModel::set(int index, const QScriptValue& valuemap) |
|
560 { |
|
561 if (!valuemap.isObject() || valuemap.isArray()) { |
|
562 qmlInfo(this) << tr("set: value is not an object"); |
|
563 return; |
|
564 } |
|
565 if (count() == 0 || index > count() || index < 0) { |
|
566 qmlInfo(this) << tr("set: index %1 out of range").arg(index); |
|
567 return; |
|
568 } |
|
569 |
|
570 if (index == count()) { |
|
571 append(valuemap); |
|
572 } else { |
|
573 QList<int> roles; |
|
574 if (m_flat) |
|
575 m_flat->set(index, valuemap, &roles); |
|
576 else |
|
577 m_nested->set(index, valuemap, &roles); |
|
578 |
|
579 if (!m_isWorkerCopy) |
|
580 emit itemsChanged(index, 1, roles); |
|
581 } |
|
582 } |
|
583 |
|
584 /*! |
|
585 \qmlmethod ListModel::setProperty(int index, string property, variant value) |
|
586 |
|
587 Changes the \a property of the item at \a index in the list model to \a value. |
|
588 |
|
589 \code |
|
590 fruitModel.setProperty(3, "cost", 5.95) |
|
591 \endcode |
|
592 |
|
593 The \a index must be an element in the list. |
|
594 |
|
595 \sa append() |
|
596 */ |
|
597 void QDeclarativeListModel::setProperty(int index, const QString& property, const QVariant& value) |
|
598 { |
|
599 if (count() == 0 || index >= count() || index < 0) { |
|
600 qmlInfo(this) << tr("set: index %1 out of range").arg(index); |
|
601 return; |
|
602 } |
|
603 |
|
604 QList<int> roles; |
|
605 if (m_flat) |
|
606 m_flat->setProperty(index, property, value, &roles); |
|
607 else |
|
608 m_nested->setProperty(index, property, value, &roles); |
|
609 |
|
610 if (!m_isWorkerCopy) |
|
611 emit itemsChanged(index, 1, roles); |
|
612 } |
|
613 |
|
614 /*! |
|
615 \qmlmethod ListModel::sync() |
|
616 |
|
617 Writes any unsaved changes to the list model after it has been modified |
|
618 from a worker script. |
|
619 */ |
|
620 void QDeclarativeListModel::sync() |
|
621 { |
|
622 // This is just a dummy method to make it look like sync() exists in |
|
623 // ListModel (and not just QDeclarativeListModelWorkerAgent) and to let |
|
624 // us document sync(). |
|
625 qmlInfo(this) << "List sync() can only be called from a WorkerScript"; |
|
626 } |
|
627 |
|
628 bool QDeclarativeListModelParser::compileProperty(const QDeclarativeCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data) |
|
629 { |
|
630 QList<QVariant> values = prop.assignedValues(); |
|
631 for(int ii = 0; ii < values.count(); ++ii) { |
|
632 const QVariant &value = values.at(ii); |
|
633 |
|
634 if(value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) { |
|
635 QDeclarativeCustomParserNode node = |
|
636 qvariant_cast<QDeclarativeCustomParserNode>(value); |
|
637 |
|
638 if (node.name() != "ListElement") { |
|
639 error(node, QDeclarativeListModel::tr("ListElement: cannot contain nested elements")); |
|
640 return false; |
|
641 } |
|
642 |
|
643 { |
|
644 ListInstruction li; |
|
645 li.type = ListInstruction::Push; |
|
646 li.dataIdx = -1; |
|
647 instr << li; |
|
648 } |
|
649 |
|
650 QList<QDeclarativeCustomParserProperty> props = node.properties(); |
|
651 for(int jj = 0; jj < props.count(); ++jj) { |
|
652 const QDeclarativeCustomParserProperty &nodeProp = props.at(jj); |
|
653 if (nodeProp.name() == "") { |
|
654 error(nodeProp, QDeclarativeListModel::tr("ListElement: cannot contain nested elements")); |
|
655 return false; |
|
656 } |
|
657 if (nodeProp.name() == "id") { |
|
658 error(nodeProp, QDeclarativeListModel::tr("ListElement: cannot use reserved \"id\" property")); |
|
659 return false; |
|
660 } |
|
661 |
|
662 ListInstruction li; |
|
663 int ref = data.count(); |
|
664 data.append(nodeProp.name()); |
|
665 data.append('\0'); |
|
666 li.type = ListInstruction::Set; |
|
667 li.dataIdx = ref; |
|
668 instr << li; |
|
669 |
|
670 if(!compileProperty(nodeProp, instr, data)) |
|
671 return false; |
|
672 |
|
673 li.type = ListInstruction::Pop; |
|
674 li.dataIdx = -1; |
|
675 instr << li; |
|
676 } |
|
677 |
|
678 { |
|
679 ListInstruction li; |
|
680 li.type = ListInstruction::Pop; |
|
681 li.dataIdx = -1; |
|
682 instr << li; |
|
683 } |
|
684 |
|
685 } else { |
|
686 |
|
687 QDeclarativeParser::Variant variant = |
|
688 qvariant_cast<QDeclarativeParser::Variant>(value); |
|
689 |
|
690 int ref = data.count(); |
|
691 |
|
692 QByteArray d; |
|
693 d += char(variant.type()); // type tag |
|
694 if (variant.isString()) { |
|
695 d += variant.asString().toUtf8(); |
|
696 } else if (variant.isNumber()) { |
|
697 d += QByteArray::number(variant.asNumber(),'g',20); |
|
698 } else if (variant.isBoolean()) { |
|
699 d += char(variant.asBoolean()); |
|
700 } else if (variant.isScript()) { |
|
701 if (definesEmptyList(variant.asScript())) { |
|
702 d[0] = char(QDeclarativeParser::Variant::Invalid); // marks empty list |
|
703 } else { |
|
704 QByteArray script = variant.asScript().toUtf8(); |
|
705 int v = evaluateEnum(script); |
|
706 if (v<0) { |
|
707 error(prop, QDeclarativeListModel::tr("ListElement: cannot use script for property value")); |
|
708 return false; |
|
709 } else { |
|
710 d[0] = char(QDeclarativeParser::Variant::Number); |
|
711 d += QByteArray::number(v); |
|
712 } |
|
713 } |
|
714 } |
|
715 d.append('\0'); |
|
716 data.append(d); |
|
717 |
|
718 ListInstruction li; |
|
719 li.type = ListInstruction::Value; |
|
720 li.dataIdx = ref; |
|
721 instr << li; |
|
722 } |
|
723 } |
|
724 |
|
725 return true; |
|
726 } |
|
727 |
|
728 QByteArray QDeclarativeListModelParser::compile(const QList<QDeclarativeCustomParserProperty> &customProps) |
|
729 { |
|
730 QList<ListInstruction> instr; |
|
731 QByteArray data; |
|
732 |
|
733 for(int ii = 0; ii < customProps.count(); ++ii) { |
|
734 const QDeclarativeCustomParserProperty &prop = customProps.at(ii); |
|
735 if(prop.name() != "") { // isn't default property |
|
736 error(prop, QDeclarativeListModel::tr("ListModel: undefined property '%1'").arg(QString::fromUtf8(prop.name()))); |
|
737 return QByteArray(); |
|
738 } |
|
739 |
|
740 if(!compileProperty(prop, instr, data)) { |
|
741 return QByteArray(); |
|
742 } |
|
743 } |
|
744 |
|
745 int size = sizeof(ListModelData) + |
|
746 instr.count() * sizeof(ListInstruction) + |
|
747 data.count(); |
|
748 |
|
749 QByteArray rv; |
|
750 rv.resize(size); |
|
751 |
|
752 ListModelData *lmd = (ListModelData *)rv.data(); |
|
753 lmd->dataOffset = sizeof(ListModelData) + |
|
754 instr.count() * sizeof(ListInstruction); |
|
755 lmd->instrCount = instr.count(); |
|
756 for (int ii = 0; ii < instr.count(); ++ii) |
|
757 lmd->instructions()[ii] = instr.at(ii); |
|
758 ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count()); |
|
759 |
|
760 return rv; |
|
761 } |
|
762 |
|
763 void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray &d) |
|
764 { |
|
765 QDeclarativeListModel *rv = static_cast<QDeclarativeListModel *>(obj); |
|
766 |
|
767 ModelNode *root = new ModelNode; |
|
768 rv->m_nested->_root = root; |
|
769 QStack<ModelNode *> nodes; |
|
770 nodes << root; |
|
771 |
|
772 bool processingSet = false; |
|
773 |
|
774 const ListModelData *lmd = (const ListModelData *)d.constData(); |
|
775 const char *data = ((const char *)lmd) + lmd->dataOffset; |
|
776 |
|
777 for (int ii = 0; ii < lmd->instrCount; ++ii) { |
|
778 const ListInstruction &instr = lmd->instructions()[ii]; |
|
779 |
|
780 switch(instr.type) { |
|
781 case ListInstruction::Push: |
|
782 { |
|
783 ModelNode *n = nodes.top(); |
|
784 ModelNode *n2 = new ModelNode; |
|
785 n->values << qVariantFromValue(n2); |
|
786 nodes.push(n2); |
|
787 if (processingSet) |
|
788 n->isArray = true; |
|
789 } |
|
790 break; |
|
791 |
|
792 case ListInstruction::Pop: |
|
793 nodes.pop(); |
|
794 break; |
|
795 |
|
796 case ListInstruction::Value: |
|
797 { |
|
798 ModelNode *n = nodes.top(); |
|
799 switch (QDeclarativeParser::Variant::Type(data[instr.dataIdx])) { |
|
800 case QDeclarativeParser::Variant::Invalid: |
|
801 n->isArray = true; |
|
802 break; |
|
803 case QDeclarativeParser::Variant::Boolean: |
|
804 n->values.append(bool(data[1 + instr.dataIdx])); |
|
805 break; |
|
806 case QDeclarativeParser::Variant::Number: |
|
807 n->values.append(QByteArray(data + 1 + instr.dataIdx).toDouble()); |
|
808 break; |
|
809 case QDeclarativeParser::Variant::String: |
|
810 n->values.append(QString::fromUtf8(data + 1 + instr.dataIdx)); |
|
811 break; |
|
812 default: |
|
813 Q_ASSERT("Format error in ListInstruction"); |
|
814 } |
|
815 |
|
816 processingSet = false; |
|
817 } |
|
818 break; |
|
819 |
|
820 case ListInstruction::Set: |
|
821 { |
|
822 ModelNode *n = nodes.top(); |
|
823 ModelNode *n2 = new ModelNode; |
|
824 n->properties.insert(QString::fromUtf8(data + instr.dataIdx), n2); |
|
825 nodes.push(n2); |
|
826 processingSet = true; |
|
827 } |
|
828 break; |
|
829 } |
|
830 } |
|
831 } |
|
832 |
|
833 bool QDeclarativeListModelParser::definesEmptyList(const QString &s) |
|
834 { |
|
835 if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { |
|
836 for (int i=1; i<s.length()-1; i++) { |
|
837 if (!s[i].isSpace()) |
|
838 return false; |
|
839 } |
|
840 return true; |
|
841 } |
|
842 return false; |
|
843 } |
|
844 |
|
845 /*! |
|
846 \qmlclass ListElement |
|
847 \since 4.7 |
|
848 \brief The ListElement element defines a data item in a ListModel. |
|
849 |
|
850 \sa ListModel |
|
851 */ |
|
852 |
|
853 FlatListModel::FlatListModel(QDeclarativeListModel *base) |
|
854 : m_scriptEngine(0), m_listModel(base) |
|
855 { |
|
856 } |
|
857 |
|
858 FlatListModel::~FlatListModel() |
|
859 { |
|
860 } |
|
861 |
|
862 QHash<int,QVariant> FlatListModel::data(int index, const QList<int> &roles) const |
|
863 { |
|
864 Q_ASSERT(index >= 0 && index < m_values.count()); |
|
865 |
|
866 QHash<int, QVariant> row; |
|
867 for (int i=0; i<roles.count(); i++) { |
|
868 int role = roles[i]; |
|
869 if (m_values[index].contains(role)) |
|
870 row.insert(role, m_values[index][role]); |
|
871 } |
|
872 return row; |
|
873 } |
|
874 |
|
875 QVariant FlatListModel::data(int index, int role) const |
|
876 { |
|
877 Q_ASSERT(index >= 0 && index < m_values.count()); |
|
878 if (m_values[index].contains(role)) |
|
879 return m_values[index][role]; |
|
880 return QVariant(); |
|
881 } |
|
882 |
|
883 QList<int> FlatListModel::roles() const |
|
884 { |
|
885 return m_roles.keys(); |
|
886 } |
|
887 |
|
888 QString FlatListModel::toString(int role) const |
|
889 { |
|
890 if (m_roles.contains(role)) |
|
891 return m_roles[role]; |
|
892 return QString(); |
|
893 } |
|
894 |
|
895 int FlatListModel::count() const |
|
896 { |
|
897 return m_values.count(); |
|
898 } |
|
899 |
|
900 void FlatListModel::clear() |
|
901 { |
|
902 m_values.clear(); |
|
903 } |
|
904 |
|
905 void FlatListModel::remove(int index) |
|
906 { |
|
907 m_values.removeAt(index); |
|
908 } |
|
909 |
|
910 bool FlatListModel::append(const QScriptValue &value) |
|
911 { |
|
912 return insert(m_values.count(), value); |
|
913 } |
|
914 |
|
915 bool FlatListModel::insert(int index, const QScriptValue &value) |
|
916 { |
|
917 Q_ASSERT(index >= 0 && index <= m_values.count()); |
|
918 |
|
919 QHash<int, QVariant> row; |
|
920 if (!addValue(value, &row, 0)) |
|
921 return false; |
|
922 |
|
923 m_values.insert(index, row); |
|
924 return true; |
|
925 } |
|
926 |
|
927 QScriptValue FlatListModel::get(int index) const |
|
928 { |
|
929 QScriptEngine *scriptEngine = m_scriptEngine ? m_scriptEngine : QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(m_listModel)); |
|
930 |
|
931 if (!scriptEngine) |
|
932 return 0; |
|
933 |
|
934 if (index < 0 || index >= m_values.count()) |
|
935 return scriptEngine->undefinedValue(); |
|
936 |
|
937 QScriptValue rv = scriptEngine->newObject(); |
|
938 |
|
939 QHash<int, QVariant> row = m_values.at(index); |
|
940 for (QHash<int, QVariant>::ConstIterator iter = row.begin(); iter != row.end(); ++iter) |
|
941 rv.setProperty(m_roles.value(iter.key()), qScriptValueFromValue(scriptEngine, iter.value())); |
|
942 |
|
943 return rv; |
|
944 } |
|
945 |
|
946 void FlatListModel::set(int index, const QScriptValue &value, QList<int> *roles) |
|
947 { |
|
948 Q_ASSERT(index >= 0 && index < m_values.count()); |
|
949 |
|
950 QHash<int, QVariant> row = m_values[index]; |
|
951 if (addValue(value, &row, roles)) |
|
952 m_values[index] = row; |
|
953 } |
|
954 |
|
955 void FlatListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles) |
|
956 { |
|
957 Q_ASSERT(index >= 0 && index < m_values.count()); |
|
958 |
|
959 QHash<QString, int>::Iterator iter = m_strings.find(property); |
|
960 int role; |
|
961 if (iter == m_strings.end()) { |
|
962 role = m_roles.count(); |
|
963 m_roles.insert(role, property); |
|
964 m_strings.insert(property, role); |
|
965 } else { |
|
966 role = iter.value(); |
|
967 } |
|
968 roles->append(role); |
|
969 |
|
970 m_values[index][role] = value; |
|
971 } |
|
972 |
|
973 void FlatListModel::move(int from, int to, int n) |
|
974 { |
|
975 if (n == 1) { |
|
976 m_values.move(from, to); |
|
977 } else { |
|
978 QList<QHash<int, QVariant> > replaced; |
|
979 int i=0; |
|
980 QList<QHash<int, QVariant> >::ConstIterator it=m_values.begin(); it += from+n; |
|
981 for (; i<to-from; ++i,++it) |
|
982 replaced.append(*it); |
|
983 i=0; |
|
984 it=m_values.begin(); it += from; |
|
985 for (; i<n; ++i,++it) |
|
986 replaced.append(*it); |
|
987 QList<QHash<int, QVariant> >::ConstIterator f=replaced.begin(); |
|
988 QList<QHash<int, QVariant> >::Iterator t=m_values.begin(); t += from; |
|
989 for (; f != replaced.end(); ++f, ++t) |
|
990 *t = *f; |
|
991 } |
|
992 } |
|
993 |
|
994 bool FlatListModel::addValue(const QScriptValue &value, QHash<int, QVariant> *row, QList<int> *roles) |
|
995 { |
|
996 QScriptValueIterator it(value); |
|
997 while (it.hasNext()) { |
|
998 it.next(); |
|
999 QScriptValue value = it.value(); |
|
1000 if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) { |
|
1001 qmlInfo(m_listModel) << "Cannot add nested list values when modifying or after modification from a worker script"; |
|
1002 return false; |
|
1003 } |
|
1004 |
|
1005 QString name = it.name(); |
|
1006 QVariant v = it.value().toVariant(); |
|
1007 |
|
1008 QHash<QString, int>::Iterator iter = m_strings.find(name); |
|
1009 if (iter == m_strings.end()) { |
|
1010 int role = m_roles.count(); |
|
1011 m_roles.insert(role, name); |
|
1012 iter = m_strings.insert(name, role); |
|
1013 if (roles) |
|
1014 roles->append(role); |
|
1015 } |
|
1016 row->insert(*iter, v); |
|
1017 } |
|
1018 return true; |
|
1019 } |
|
1020 |
|
1021 NestedListModel::NestedListModel(QDeclarativeListModel *base) |
|
1022 : _root(0), m_listModel(base), _rolesOk(false) |
|
1023 { |
|
1024 } |
|
1025 |
|
1026 NestedListModel::~NestedListModel() |
|
1027 { |
|
1028 delete _root; |
|
1029 } |
|
1030 |
|
1031 QVariant NestedListModel::valueForNode(ModelNode *node, bool *hasNested) const |
|
1032 { |
|
1033 QObject *rv = 0; |
|
1034 if (hasNested) |
|
1035 *hasNested = false; |
|
1036 |
|
1037 if (node->isArray) { |
|
1038 // List |
|
1039 rv = node->model(this); |
|
1040 if (hasNested) |
|
1041 *hasNested = true; |
|
1042 } else { |
|
1043 if (!node->properties.isEmpty()) { |
|
1044 // Object |
|
1045 rv = node->object(this); |
|
1046 } else if (node->values.count() == 0) { |
|
1047 // Invalid |
|
1048 return QVariant(); |
|
1049 } else if (node->values.count() == 1) { |
|
1050 // Value |
|
1051 QVariant &var = node->values[0]; |
|
1052 ModelNode *valueNode = qvariant_cast<ModelNode *>(var); |
|
1053 if (valueNode) { |
|
1054 if (!valueNode->properties.isEmpty()) |
|
1055 rv = valueNode->object(this); |
|
1056 else |
|
1057 rv = valueNode->model(this); |
|
1058 } else { |
|
1059 return var; |
|
1060 } |
|
1061 } |
|
1062 } |
|
1063 |
|
1064 if (rv) { |
|
1065 return QVariant::fromValue(rv); |
|
1066 } else { |
|
1067 return QVariant(); |
|
1068 } |
|
1069 } |
|
1070 |
|
1071 QHash<int,QVariant> NestedListModel::data(int index, const QList<int> &roles, bool *hasNested) const |
|
1072 { |
|
1073 Q_ASSERT(_root && index >= 0 && index < _root->values.count()); |
|
1074 checkRoles(); |
|
1075 QHash<int, QVariant> rv; |
|
1076 |
|
1077 ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); |
|
1078 if (!node) |
|
1079 return rv; |
|
1080 |
|
1081 for (int ii = 0; ii < roles.count(); ++ii) { |
|
1082 const QString &roleString = roleStrings.at(roles.at(ii)); |
|
1083 |
|
1084 QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString); |
|
1085 if (iter != node->properties.end()) { |
|
1086 ModelNode *row = *iter; |
|
1087 rv.insert(roles.at(ii), valueForNode(row, hasNested)); |
|
1088 } |
|
1089 } |
|
1090 |
|
1091 return rv; |
|
1092 } |
|
1093 |
|
1094 QVariant NestedListModel::data(int index, int role) const |
|
1095 { |
|
1096 Q_ASSERT(_root && index >= 0 && index < _root->values.count()); |
|
1097 checkRoles(); |
|
1098 QVariant rv; |
|
1099 |
|
1100 ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); |
|
1101 if (!node) |
|
1102 return rv; |
|
1103 |
|
1104 const QString &roleString = roleStrings.at(role); |
|
1105 |
|
1106 QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString); |
|
1107 if (iter != node->properties.end()) { |
|
1108 ModelNode *row = *iter; |
|
1109 rv = valueForNode(row); |
|
1110 } |
|
1111 |
|
1112 return rv; |
|
1113 } |
|
1114 |
|
1115 int NestedListModel::count() const |
|
1116 { |
|
1117 if (!_root) return 0; |
|
1118 return _root->values.count(); |
|
1119 } |
|
1120 |
|
1121 void NestedListModel::clear() |
|
1122 { |
|
1123 _rolesOk = false; |
|
1124 roleStrings.clear(); |
|
1125 |
|
1126 delete _root; |
|
1127 _root = 0; |
|
1128 } |
|
1129 |
|
1130 void NestedListModel::remove(int index) |
|
1131 { |
|
1132 if (!_root) |
|
1133 return; |
|
1134 ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); |
|
1135 _root->values.removeAt(index); |
|
1136 if (node) |
|
1137 delete node; |
|
1138 } |
|
1139 |
|
1140 bool NestedListModel::insert(int index, const QScriptValue& valuemap) |
|
1141 { |
|
1142 if (!_root) |
|
1143 _root = new ModelNode; |
|
1144 |
|
1145 ModelNode *mn = new ModelNode; |
|
1146 mn->setObjectValue(valuemap); |
|
1147 _root->values.insert(index,qVariantFromValue(mn)); |
|
1148 return true; |
|
1149 } |
|
1150 |
|
1151 void NestedListModel::move(int from, int to, int n) |
|
1152 { |
|
1153 if (n==1) { |
|
1154 _root->values.move(from,to); |
|
1155 } else { |
|
1156 QList<QVariant> replaced; |
|
1157 int i=0; |
|
1158 QVariantList::const_iterator it=_root->values.begin(); it += from+n; |
|
1159 for (; i<to-from; ++i,++it) |
|
1160 replaced.append(*it); |
|
1161 i=0; |
|
1162 it=_root->values.begin(); it += from; |
|
1163 for (; i<n; ++i,++it) |
|
1164 replaced.append(*it); |
|
1165 QVariantList::const_iterator f=replaced.begin(); |
|
1166 QVariantList::iterator t=_root->values.begin(); t += from; |
|
1167 for (; f != replaced.end(); ++f, ++t) |
|
1168 *t = *f; |
|
1169 } |
|
1170 } |
|
1171 |
|
1172 bool NestedListModel::append(const QScriptValue& valuemap) |
|
1173 { |
|
1174 if (!_root) |
|
1175 _root = new ModelNode; |
|
1176 ModelNode *mn = new ModelNode; |
|
1177 mn->setObjectValue(valuemap); |
|
1178 _root->values << qVariantFromValue(mn); |
|
1179 return true; |
|
1180 } |
|
1181 |
|
1182 QScriptValue NestedListModel::get(int index) const |
|
1183 { |
|
1184 QDeclarativeEngine *eng = qmlEngine(m_listModel); |
|
1185 if (!eng) |
|
1186 return 0; |
|
1187 |
|
1188 if (index < 0 || index >= count()) { |
|
1189 QScriptEngine *seng = QDeclarativeEnginePrivate::getScriptEngine(eng); |
|
1190 if (seng) |
|
1191 return seng->undefinedValue(); |
|
1192 return 0; |
|
1193 } |
|
1194 |
|
1195 ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); |
|
1196 if (!node) |
|
1197 return 0; |
|
1198 |
|
1199 return QDeclarativeEnginePrivate::qmlScriptObject(node->object(this), eng); |
|
1200 } |
|
1201 |
|
1202 void NestedListModel::set(int index, const QScriptValue& valuemap, QList<int> *roles) |
|
1203 { |
|
1204 Q_ASSERT(index >=0 && index < count()); |
|
1205 |
|
1206 ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); |
|
1207 node->setObjectValue(valuemap); |
|
1208 |
|
1209 QScriptValueIterator it(valuemap); |
|
1210 while (it.hasNext()) { |
|
1211 it.next(); |
|
1212 int r = roleStrings.indexOf(it.name()); |
|
1213 if (r < 0) { |
|
1214 r = roleStrings.count(); |
|
1215 roleStrings << it.name(); |
|
1216 } |
|
1217 roles->append(r); |
|
1218 } |
|
1219 } |
|
1220 |
|
1221 void NestedListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles) |
|
1222 { |
|
1223 Q_ASSERT(index >=0 && index < count()); |
|
1224 |
|
1225 ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); |
|
1226 node->setProperty(property, value); |
|
1227 |
|
1228 int r = roleStrings.indexOf(property); |
|
1229 if (r < 0) { |
|
1230 r = roleStrings.count(); |
|
1231 roleStrings << property; |
|
1232 } |
|
1233 roles->append(r); |
|
1234 } |
|
1235 |
|
1236 void NestedListModel::checkRoles() const |
|
1237 { |
|
1238 if (_rolesOk || !_root) |
|
1239 return; |
|
1240 |
|
1241 for (int i = 0; i<_root->values.count(); ++i) { |
|
1242 ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(i)); |
|
1243 if (node) { |
|
1244 foreach (const QString &role, node->properties.keys()) { |
|
1245 if (!roleStrings.contains(role)) |
|
1246 roleStrings.append(role); |
|
1247 } |
|
1248 } |
|
1249 } |
|
1250 |
|
1251 _rolesOk = true; |
|
1252 } |
|
1253 |
|
1254 QList<int> NestedListModel::roles() const |
|
1255 { |
|
1256 checkRoles(); |
|
1257 QList<int> rv; |
|
1258 for (int ii = 0; ii < roleStrings.count(); ++ii) |
|
1259 rv << ii; |
|
1260 return rv; |
|
1261 } |
|
1262 |
|
1263 QString NestedListModel::toString(int role) const |
|
1264 { |
|
1265 checkRoles(); |
|
1266 if (role < roleStrings.count()) |
|
1267 return roleStrings.at(role); |
|
1268 else |
|
1269 return QString(); |
|
1270 } |
|
1271 |
|
1272 |
|
1273 ModelNode::ModelNode() |
|
1274 : modelCache(0), objectCache(0), isArray(false) |
|
1275 { |
|
1276 } |
|
1277 |
|
1278 ModelNode::~ModelNode() |
|
1279 { |
|
1280 qDeleteAll(properties.values()); |
|
1281 |
|
1282 ModelNode *node; |
|
1283 for (int ii = 0; ii < values.count(); ++ii) { |
|
1284 node = qvariant_cast<ModelNode *>(values.at(ii)); |
|
1285 if (node) { delete node; node = 0; } |
|
1286 } |
|
1287 |
|
1288 if (modelCache) { modelCache->m_nested->_root = 0/* ==this */; delete modelCache; modelCache = 0; } |
|
1289 if (objectCache) { delete objectCache; objectCache = 0; } |
|
1290 } |
|
1291 |
|
1292 void ModelNode::setObjectValue(const QScriptValue& valuemap) { |
|
1293 QScriptValueIterator it(valuemap); |
|
1294 while (it.hasNext()) { |
|
1295 it.next(); |
|
1296 ModelNode *value = new ModelNode; |
|
1297 QScriptValue v = it.value(); |
|
1298 if (v.isArray()) { |
|
1299 value->isArray = true; |
|
1300 value->setListValue(v); |
|
1301 } else { |
|
1302 value->values << v.toVariant(); |
|
1303 if (objectCache) |
|
1304 objectCache->setValue(it.name().toUtf8(), value->values.last()); |
|
1305 } |
|
1306 if (properties.contains(it.name())) |
|
1307 delete properties[it.name()]; |
|
1308 properties.insert(it.name(), value); |
|
1309 } |
|
1310 } |
|
1311 |
|
1312 void ModelNode::setListValue(const QScriptValue& valuelist) { |
|
1313 QScriptValueIterator it(valuelist); |
|
1314 values.clear(); |
|
1315 int size = valuelist.property(QLatin1String("length")).toInt32(); |
|
1316 for (int i=0; i<size; i++) { |
|
1317 ModelNode *value = new ModelNode; |
|
1318 QScriptValue v = valuelist.property(i); |
|
1319 if (v.isArray()) { |
|
1320 value->isArray = true; |
|
1321 value->setListValue(v); |
|
1322 } else if (v.isObject()) { |
|
1323 value->setObjectValue(v); |
|
1324 } else { |
|
1325 value->values << v.toVariant(); |
|
1326 } |
|
1327 values.append(qVariantFromValue(value)); |
|
1328 } |
|
1329 } |
|
1330 |
|
1331 void ModelNode::setProperty(const QString& prop, const QVariant& val) { |
|
1332 QHash<QString, ModelNode *>::const_iterator it = properties.find(prop); |
|
1333 if (it != properties.end()) { |
|
1334 (*it)->values[0] = val; |
|
1335 } else { |
|
1336 ModelNode *n = new ModelNode; |
|
1337 n->values << val; |
|
1338 properties.insert(prop,n); |
|
1339 } |
|
1340 if (objectCache) |
|
1341 objectCache->setValue(prop.toUtf8(), val); |
|
1342 } |
|
1343 |
|
1344 void ModelNode::dump(ModelNode *node, int ind) |
|
1345 { |
|
1346 QByteArray indentBa(ind * 4, ' '); |
|
1347 const char *indent = indentBa.constData(); |
|
1348 |
|
1349 for (int ii = 0; ii < node->values.count(); ++ii) { |
|
1350 ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(ii)); |
|
1351 if (subNode) { |
|
1352 qWarning().nospace() << indent << "Sub-node " << ii; |
|
1353 dump(subNode, ind + 1); |
|
1354 } else { |
|
1355 qWarning().nospace() << indent << "Sub-node " << ii << ": " << node->values.at(ii).toString(); |
|
1356 } |
|
1357 } |
|
1358 |
|
1359 for (QHash<QString, ModelNode *>::ConstIterator iter = node->properties.begin(); iter != node->properties.end(); ++iter) { |
|
1360 qWarning().nospace() << indent << "Property " << iter.key() << ':'; |
|
1361 dump(iter.value(), ind + 1); |
|
1362 } |
|
1363 } |
|
1364 |
|
1365 ModelObject::ModelObject() |
|
1366 : _mo(new QDeclarativeOpenMetaObject(this)) |
|
1367 { |
|
1368 } |
|
1369 |
|
1370 void ModelObject::setValue(const QByteArray &name, const QVariant &val) |
|
1371 { |
|
1372 _mo->setValue(name, val); |
|
1373 setProperty(name.constData(), val); |
|
1374 } |
|
1375 |
|
1376 |
|
1377 QT_END_NAMESPACE |