src/declarative/util/qdeclarativelistmodel.cpp
changeset 30 5dc02b23752f
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
       
     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