aboutsummaryrefslogtreecommitdiffstats
path: root/src/declarative/util/qdeclarativelistmodel.cpp
diff options
context:
space:
mode:
authorGlenn Watson <glenn.watson@nokia.com>2011-10-26 10:28:01 +1000
committerQt by Nokia <qt-info@nokia.com>2011-10-26 03:41:53 +0200
commitca3dfc52667a036901d640e4f9ed30f267153b2c (patch)
tree99c036580179b73e74e3e798cd91ff2b3fc5341d /src/declarative/util/qdeclarativelistmodel.cpp
parent31d34d98f192035c42bd1d1c07bf6e297da76220 (diff)
Optimize listmodel and allow nested elements from worker script.
Added support for nested listmodels when used from a worker script thread. Optimized the implementation of ListModel, especially the performance of appending a large number of items. Added a batch append mode (with an array of JS objects) to reduce the overhead of calling from JS into native code for each append operation. Task-number:QTBUG-21508 Change-Id: I07b381dc3e8200d92d6e0af458df8850d78b510f Reviewed-by: Martin Jones <martin.jones@nokia.com>
Diffstat (limited to 'src/declarative/util/qdeclarativelistmodel.cpp')
-rw-r--r--src/declarative/util/qdeclarativelistmodel.cpp2218
1 files changed, 1198 insertions, 1020 deletions
diff --git a/src/declarative/util/qdeclarativelistmodel.cpp b/src/declarative/util/qdeclarativelistmodel.cpp
index 4e67182048..445944043b 100644
--- a/src/declarative/util/qdeclarativelistmodel.cpp
+++ b/src/declarative/util/qdeclarativelistmodel.cpp
@@ -59,141 +59,1022 @@ Q_DECLARE_METATYPE(QListModelInterface *)
QT_BEGIN_NAMESPACE
-QV8ListModelResource::QV8ListModelResource(FlatListModel *model, FlatNodeData *data, QV8Engine *engine)
-: QV8ObjectResource(engine), model(model), nodeData(data)
+// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models.
+enum { MIN_LISTMODEL_UID = 1024 };
+
+QAtomicInt ListModel::uidCounter(MIN_LISTMODEL_UID);
+
+const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type)
+{
+ QStringHash<Role *>::Node *node = roleHash.findNode(key);
+ if (node) {
+ const Role &r = *node->value;
+ if (type != r.type) {
+ qmlInfo(0) << "Can't assign to pre-existing role of different type " << r.name;
+ }
+ return r;
+ }
+
+ return createRole(key, type);
+}
+
+const ListLayout::Role &ListLayout::getRoleOrCreate(v8::Handle<v8::String> key, Role::DataType type)
+{
+ QHashedV8String hashedKey(key);
+ QStringHash<Role *>::Node *node = roleHash.findNode(hashedKey);
+ if (node) {
+ const Role &r = *node->value;
+ if (type != r.type) {
+ qmlInfo(0) << "Can't assign to pre-existing role of different type " << r.name;
+ }
+ return r;
+ }
+
+ QString qkey;
+ qkey.resize(key->Length());
+ key->Write(reinterpret_cast<uint16_t*>(qkey.data()));
+
+ return createRole(qkey, type);
+}
+
+const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type)
+{
+ const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(QDeclarativeListModel *), sizeof(ListModel *), sizeof(QDeclarativeGuard<QObject>) };
+ const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(QDeclarativeListModel *), sizeof(ListModel *), sizeof(QObject *) };
+
+ Role *r = new Role;
+ r->name = key;
+ r->type = type;
+
+ if (type == Role::List) {
+ r->subLayout = new ListLayout;
+ } else {
+ r->subLayout = 0;
+ }
+
+ int dataSize = dataSizes[type];
+ int dataAlignment = dataAlignments[type];
+
+ int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1);
+ if (dataOffset + dataSize > ListElement::BLOCK_SIZE) {
+ r->blockIndex = ++currentBlock;
+ r->blockOffset = 0;
+ currentBlockOffset = dataSize;
+ } else {
+ r->blockIndex = currentBlock;
+ r->blockOffset = dataOffset;
+ currentBlockOffset = dataOffset + dataSize;
+ }
+
+ int roleIndex = roles.count();
+ r->index = roleIndex;
+
+ roles.append(r);
+ roleHash.insert(key, r);
+
+ return *r;
+}
+
+ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0)
{
- if (nodeData) nodeData->addData(this);
+ for (int i=0 ; i < other->roles.count() ; ++i) {
+ Role *role = new Role(other->roles[i]);
+ roles.append(role);
+ roleHash.insert(role->name, role);
+ }
+ currentBlockOffset = other->currentBlockOffset;
+ currentBlock = other->currentBlock;
}
-QV8ListModelResource::~QV8ListModelResource()
+ListLayout::~ListLayout()
{
- if (nodeData) nodeData->removeData(this);
+ for (int i=0 ; i < roles.count() ; ++i) {
+ delete roles[i];
+ }
}
-class QDeclarativeListModelV8Data : public QV8Engine::Deletable
+void ListLayout::sync(ListLayout *src, ListLayout *target)
{
-public:
- QDeclarativeListModelV8Data();
- ~QDeclarativeListModelV8Data();
+ int roleOffset = target->roles.count();
+ int newRoleCount = src->roles.count() - roleOffset;
+
+ for (int i=0 ; i < newRoleCount ; ++i) {
+ Role *role = new Role(src->roles[roleOffset + i]);
+ target->roles.append(role);
+ target->roleHash.insert(role->name, role);
+ }
- v8::Persistent<v8::Function> constructor;
+ target->currentBlockOffset = src->currentBlockOffset;
+ target->currentBlock = src->currentBlock;
+}
- static v8::Local<v8::Object> create(QV8Engine *);
+ListLayout::Role::Role(const Role *other)
+{
+ name = other->name;
+ type = other->type;
+ blockIndex = other->blockIndex;
+ blockOffset = other->blockOffset;
+ index = other->index;
+ if (other->subLayout)
+ subLayout = new ListLayout(other->subLayout);
+ else
+ subLayout = 0;
+}
- static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property,
- const v8::AccessorInfo &info);
- static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property,
- v8::Local<v8::Value> value,
- const v8::AccessorInfo &info);
-};
+ListLayout::Role::~Role()
+{
+ delete subLayout;
+}
-v8::Local<v8::Object> QDeclarativeListModelV8Data::create(QV8Engine *engine)
+const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QVariant &data)
{
- if (!engine->listModelData()) {
- QDeclarativeListModelV8Data *d = new QDeclarativeListModelV8Data;
- engine->setListModelData(d);
+ Role::DataType type;
+
+ switch (data.type()) {
+ case QVariant::Double: type = Role::Number; break;
+ case QVariant::Int: type = Role::Number; break;
+ case QVariant::UserType: type = Role::List; break;
+ case QVariant::Bool: type = Role::Bool; break;
+ case QVariant::String: type = Role::String; break;
+ default: type = Role::Invalid; break;
+ }
+
+ if (type == Role::Invalid) {
+ qmlInfo(0) << "Can't create role for unsupported data type";
+ return 0;
}
- QDeclarativeListModelV8Data *d = (QDeclarativeListModelV8Data *)engine->listModelData();
- return d->constructor->NewInstance();
+ return &getRoleOrCreate(key, type);
+}
+
+const ListLayout::Role *ListLayout::getExistingRole(const QString &key)
+{
+ Role *r = 0;
+ QStringHash<Role *>::Node *node = roleHash.findNode(key);
+ if (node)
+ r = node->value;
+ return r;
}
-QDeclarativeListModelV8Data::QDeclarativeListModelV8Data()
+const ListLayout::Role *ListLayout::getExistingRole(v8::Handle<v8::String> key)
{
- v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
- ft->InstanceTemplate()->SetNamedPropertyHandler(Getter, Setter);
- ft->InstanceTemplate()->SetHasExternalResource(true);
- constructor = qPersistentNew<v8::Function>(ft->GetFunction());
+ Role *r = 0;
+ QHashedV8String hashedKey(key);
+ QStringHash<Role *>::Node *node = roleHash.findNode(hashedKey);
+ if (node)
+ r = node->value;
+ return r;
}
-QDeclarativeListModelV8Data::~QDeclarativeListModelV8Data()
+ModelObject *ListModel::getOrCreateModelObject(QDeclarativeListModel *model, int elementIndex)
{
- qPersistentDispose(constructor);
+ ListElement *e = elements[elementIndex];
+ if (e->m_objectCache == 0) {
+ e->m_objectCache = new ModelObject(model, elementIndex);
+ }
+ return e->m_objectCache;
}
-v8::Handle<v8::Value> QDeclarativeListModelV8Data::Getter(v8::Local<v8::String> property,
- const v8::AccessorInfo &info)
+void ListModel::sync(ListModel *src, ListModel *target, QHash<int, ListModel *> *targetModelHash)
{
- QV8ListModelResource *r = v8_resource_cast<QV8ListModelResource>(info.This());
- if (!r)
- return v8::Undefined();
+ // Sanity check
+ target->m_uid = src->m_uid;
+ if (targetModelHash)
+ targetModelHash->insert(target->m_uid, target);
- if (!r->nodeData) // Item at this index has been deleted
- return v8::Undefined();
+ // Build hash of elements <-> uid for each of the lists
+ QHash<int, ElementSync> elementHash;
+ for (int i=0 ; i < target->elements.count() ; ++i) {
+ ListElement *e = target->elements.at(i);
+ int uid = e->getUid();
+ ElementSync sync;
+ sync.target = e;
+ elementHash.insert(uid, sync);
+ }
+ for (int i=0 ; i < src->elements.count() ; ++i) {
+ ListElement *e = src->elements.at(i);
+ int uid = e->getUid();
+
+ QHash<int, ElementSync>::iterator it = elementHash.find(uid);
+ if (it == elementHash.end()) {
+ ElementSync sync;
+ sync.src = e;
+ elementHash.insert(uid, sync);
+ } else {
+ ElementSync &sync = it.value();
+ sync.src = e;
+ }
+ }
+ // Get list of elements that are in the target but no longer in the source. These get deleted first.
+ QHash<int, ElementSync>::iterator it = elementHash.begin();
+ QHash<int, ElementSync>::iterator end = elementHash.end();
+ while (it != end) {
+ const ElementSync &s = it.value();
+ if (s.src == 0) {
+ s.target->destroy(target->m_layout);
+ target->elements.removeOne(s.target);
+ delete s.target;
+ }
+ ++it;
+ }
- int index = r->nodeData->index;
- QString propName = r->engine->toString(property);
+ // Sync the layouts
+ ListLayout::sync(src->m_layout, target->m_layout);
+
+ // Clear the target list, and append in correct order from the source
+ target->elements.clear();
+ for (int i=0 ; i < src->elements.count() ; ++i) {
+ ListElement *srcElement = src->elements.at(i);
+ it = elementHash.find(srcElement->getUid());
+ const ElementSync &s = it.value();
+ ListElement *targetElement = s.target;
+ if (targetElement == 0) {
+ targetElement = new ListElement(srcElement->getUid());
+ }
+ ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout, targetModelHash);
+ target->elements.append(targetElement);
+ }
- int role = r->model->m_strings.value(propName, -1);
+ target->updateCacheIndices();
- if (role >= 0 && index >=0 ) {
- const QHash<int, QVariant> &row = r->model->m_values[index];
- return r->engine->fromVariant(row[role]);
+ // Update values stored in target meta objects
+ for (int i=0 ; i < target->elements.count() ; ++i) {
+ ListElement *e = target->elements[i];
+ if (e->m_objectCache)
+ e->m_objectCache->updateValues();
}
+}
+
+int ListModel::allocateUid()
+{
+ return uidCounter.fetchAndAddOrdered(1);
+}
+
+ListModel::ListModel(ListLayout *layout, QDeclarativeListModel *modelCache, int uid) : m_layout(layout), m_modelCache(modelCache)
+{
+ if (uid == -1)
+ uid = allocateUid();
+ m_uid = uid;
+}
+
+void ListModel::destroy()
+{
+ clear();
+ m_uid = -1;
+ m_layout = 0;
+ if (m_modelCache && m_modelCache->m_primary == false)
+ delete m_modelCache;
+ m_modelCache = 0;
+}
+
+int ListModel::appendElement()
+{
+ int elementIndex = elements.count();
+ newElement(elementIndex);
+ return elementIndex;
+}
- return v8::Undefined();
+void ListModel::insertElement(int index)
+{
+ newElement(index);
+ updateCacheIndices();
}
-v8::Handle<v8::Value> QDeclarativeListModelV8Data::Setter(v8::Local<v8::String> property,
- v8::Local<v8::Value> value,
- const v8::AccessorInfo &info)
+void ListModel::move(int from, int to, int n)
{
- QV8ListModelResource *r = v8_resource_cast<QV8ListModelResource>(info.This());
- if (!r)
- return v8::Undefined();
+ if (from > to) {
+ // Only move forwards - flip if backwards moving
+ int tfrom = from;
+ int tto = to;
+ from = tto;
+ to = tto+n;
+ n = tfrom-tto;
+ }
+
+ QPODVector<ListElement *, 4> store;
+ for (int i=0 ; i < (to-from) ; ++i)
+ store.append(elements[from+n+i]);
+ for (int i=0 ; i < n ; ++i)
+ store.append(elements[from+i]);
+ for (int i=0 ; i < store.count() ; ++i)
+ elements[from+i] = store[i];
- if (!r->nodeData) // item at this index has been deleted
- return value;
+ updateCacheIndices();
+}
+void ListModel::newElement(int index)
+{
+ ListElement *e = new ListElement;
+ elements.insert(index, e);
+}
- if (!value->IsRegExp() && !value->IsDate() && value->IsObject() && !r->engine->isVariant(value)) {
- qmlInfo(r->model->m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script";
- return value;
+void ListModel::updateCacheIndices()
+{
+ for (int i=0 ; i < elements.count() ; ++i) {
+ ListElement *e = elements.at(i);
+ if (e->m_objectCache) {
+ e->m_objectCache->m_elementIndex = i;
+ }
}
+}
+
+QVariant ListModel::getProperty(int elementIndex, int roleIndex, const QDeclarativeListModel *owner, QV8Engine *eng)
+{
+ ListElement *e = elements[elementIndex];
+ const ListLayout::Role &r = m_layout->getExistingRole(roleIndex);
+ return e->getProperty(r, owner, eng);
+}
- int index = r->nodeData->index;
- QString propName = r->engine->toString(property);
+ListModel *ListModel::getListProperty(int elementIndex, const ListLayout::Role &role)
+{
+ ListElement *e = elements[elementIndex];
+ return e->getListProperty(role);
+}
+
+void ListModel::set(int elementIndex, v8::Handle<v8::Object> object, QList<int> *roles)
+{
+ ListElement *e = elements[elementIndex];
- int role = r->model->m_strings.value(propName, -1);
- if (role >= 0 && index >= 0) {
- QHash<int, QVariant> &row = r->model->m_values[index];
- row[role] = r->engine->toVariant(value, -1);
+ v8::Local<v8::Array> propertyNames = object->GetPropertyNames();
+ int propertyCount = propertyNames->Length();
- QList<int> roles;
- roles << role;
- if (r->model->m_parentAgent) {
- // This is the list in the worker thread, so tell the agent to
- // emit itemsChanged() later
- r->model->m_parentAgent->changedData(index, 1, roles);
+ for (int i=0 ; i < propertyCount ; ++i) {
+ v8::Local<v8::String> propertyName = propertyNames->Get(i)->ToString();
+ v8::Local<v8::Value> propertyValue = object->Get(propertyName);
+
+ // Check if this key exists yet
+ int roleIndex = -1;
+
+ // Add the value now
+ if (propertyValue->IsString()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String);
+ v8::Handle<v8::String> jsString = propertyValue->ToString();
+ QString qstr;
+ qstr.resize(jsString->Length());
+ jsString->Write(reinterpret_cast<uint16_t*>(qstr.data()));
+ roleIndex = e->setStringProperty(r, qstr);
+ } else if (propertyValue->IsNumber()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number);
+ roleIndex = e->setDoubleProperty(r, propertyValue->NumberValue());
+ } else if (propertyValue->IsArray()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
+ ListModel *subModel = new ListModel(r.subLayout, 0, -1);
+
+ v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(propertyValue);
+ int arrayLength = subArray->Length();
+ for (int j=0 ; j < arrayLength ; ++j) {
+ v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject();
+ subModel->append(subObject);
+ }
+
+ roleIndex = e->setListProperty(r, subModel);
+ } else if (propertyValue->IsInt32()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number);
+ roleIndex = e->setDoubleProperty(r, (double) propertyValue->Int32Value());
+ } else if (propertyValue->IsBoolean()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool);
+ roleIndex = e->setBoolProperty(r, propertyValue->BooleanValue());
+ } else if (propertyValue->IsObject()) {
+ QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource();
+ if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
+ QObject *o = QV8QObjectWrapper::toQObject(r);
+ const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
+ if (role.type == ListLayout::Role::QObject)
+ e->setQObjectProperty(role, o);
+ }
+ } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) {
+ const ListLayout::Role *r = m_layout->getExistingRole(propertyName);
+ if (r)
+ e->clearProperty(*r);
+ }
+
+ if (roleIndex != -1)
+ roles->append(roleIndex);
+ }
+
+ if (e->m_objectCache) {
+ e->m_objectCache->updateValues(*roles);
+ }
+}
+
+void ListModel::set(int elementIndex, v8::Handle<v8::Object> object)
+{
+ ListElement *e = elements[elementIndex];
+
+ v8::Local<v8::Array> propertyNames = object->GetPropertyNames();
+ int propertyCount = propertyNames->Length();
+
+ for (int i=0 ; i < propertyCount ; ++i) {
+ v8::Local<v8::String> propertyName = propertyNames->Get(i)->ToString();
+ v8::Local<v8::Value> propertyValue = object->Get(propertyName);
+
+ // Add the value now
+ if (propertyValue->IsString()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String);
+ if (r.type == ListLayout::Role::String) {
+ v8::Handle<v8::String> jsString = propertyValue->ToString();
+ QString qstr;
+ qstr.resize(jsString->Length());
+ jsString->Write(reinterpret_cast<uint16_t*>(qstr.data()));
+ e->setStringPropertyFast(r, qstr);
+ }
+ } else if (propertyValue->IsNumber()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number);
+ if (r.type == ListLayout::Role::Number) {
+ e->setDoublePropertyFast(r, propertyValue->NumberValue());
+ }
+ } else if (propertyValue->IsArray()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
+ if (r.type == ListLayout::Role::List) {
+ ListModel *subModel = new ListModel(r.subLayout, 0, -1);
+
+ v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(propertyValue);
+ int arrayLength = subArray->Length();
+ for (int j=0 ; j < arrayLength ; ++j) {
+ v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject();
+ subModel->append(subObject);
+ }
+
+ e->setListPropertyFast(r, subModel);
+ }
+ } else if (propertyValue->IsInt32()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number);
+ if (r.type == ListLayout::Role::Number) {
+ e->setDoublePropertyFast(r, (double) propertyValue->Int32Value());
+ }
+ } else if (propertyValue->IsBoolean()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool);
+ if (r.type == ListLayout::Role::Bool) {
+ e->setBoolPropertyFast(r, propertyValue->BooleanValue());
+ }
+ } else if (propertyValue->IsObject()) {
+ QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource();
+ if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
+ QObject *o = QV8QObjectWrapper::toQObject(r);
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
+ if (r.type == ListLayout::Role::QObject)
+ e->setQObjectPropertyFast(r, o);
+ }
+ } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) {
+ const ListLayout::Role *r = m_layout->getExistingRole(propertyName);
+ if (r)
+ e->clearProperty(*r);
+ }
+ }
+}
+
+void ListModel::clear()
+{
+ int elementCount = elements.count();
+ for (int i=0 ; i < elementCount ; ++i) {
+ elements[i]->destroy(m_layout);
+ delete elements[i];
+ }
+ elements.clear();
+}
+
+void ListModel::remove(int index)
+{
+ elements[index]->destroy(m_layout);
+ delete elements[index];
+ elements.remove(index);
+ updateCacheIndices();
+}
+
+void ListModel::insert(int elementIndex, v8::Handle<v8::Object> object)
+{
+ insertElement(elementIndex);
+ set(elementIndex, object);
+}
+
+int ListModel::append(v8::Handle<v8::Object> object)
+{
+ int elementIndex = appendElement();
+ set(elementIndex, object);
+ return elementIndex;
+}
+
+int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data)
+{
+ int roleIndex = -1;
+
+ if (elementIndex >= 0 && elementIndex < elements.count()) {
+ ListElement *e = elements[elementIndex];
+ const ListLayout::Role *r = m_layout->getRoleOrCreate(key, data);
+ if (r) {
+ roleIndex = e->setVariantProperty(*r, data);
+
+ if (roleIndex != -1 && e->m_objectCache) {
+ QList<int> roles;
+ roles << roleIndex;
+ e->m_objectCache->updateValues(roles);
+ }
+ }
+ }
+
+ return roleIndex;
+}
+
+int ListModel::setExistingProperty(int elementIndex, const QString &key, v8::Handle<v8::Value> data)
+{
+ int roleIndex = -1;
+
+ if (elementIndex >= 0 && elementIndex < elements.count()) {
+ ListElement *e = elements[elementIndex];
+ const ListLayout::Role *r = m_layout->getExistingRole(key);
+ if (r)
+ roleIndex = e->setJsProperty(*r, data);
+ }
+
+ return roleIndex;
+}
+
+inline char *ListElement::getPropertyMemory(const ListLayout::Role &role)
+{
+ ListElement *e = this;
+ int blockIndex = 0;
+ while (blockIndex < role.blockIndex) {
+ if (e->next == 0) {
+ e->next = new ListElement;
+ e->next->uid = uid;
+ }
+ e = e->next;
+ ++blockIndex;
+ }
+
+ char *mem = &e->data[role.blockOffset];
+ return mem;
+}
+
+QString *ListElement::getStringProperty(const ListLayout::Role &role)
+{
+ char *mem = getPropertyMemory(role);
+ QString *s = reinterpret_cast<QString *>(mem);
+ return s->data_ptr() ? s : 0;
+}
+
+QObject *ListElement::getQObjectProperty(const ListLayout::Role &role)
+{
+ char *mem = getPropertyMemory(role);
+ QDeclarativeGuard<QObject> *o = reinterpret_cast<QDeclarativeGuard<QObject> *>(mem);
+ return o->data();
+}
+
+QDeclarativeGuard<QObject> *ListElement::getGuardProperty(const ListLayout::Role &role)
+{
+ char *mem = getPropertyMemory(role);
+ QDeclarativeGuard<QObject> *o = reinterpret_cast<QDeclarativeGuard<QObject> *>(mem);
+ return o;
+}
+
+ListModel *ListElement::getListProperty(const ListLayout::Role &role)
+{
+ char *mem = getPropertyMemory(role);
+ ListModel **value = reinterpret_cast<ListModel **>(mem);
+ return *value;
+}
+
+QVariant ListElement::getProperty(const ListLayout::Role &role, const QDeclarativeListModel *owner, QV8Engine *eng)
+{
+ char *mem = getPropertyMemory(role);
+
+ QVariant data;
+
+ switch (role.type) {
+ case ListLayout::Role::Number:
+ {
+ double *value = reinterpret_cast<double *>(mem);
+ data = *value;
+ }
+ break;
+ case ListLayout::Role::String:
+ {
+ QString *value = reinterpret_cast<QString *>(mem);
+ if (value->data_ptr() != 0)
+ data = *value;
+ }
+ break;
+ case ListLayout::Role::Bool:
+ {
+ bool *value = reinterpret_cast<bool *>(mem);
+ data = *value;
+ }
+ break;
+ case ListLayout::Role::List:
+ {
+ ListModel **value = reinterpret_cast<ListModel **>(mem);
+ ListModel *model = *value;
+
+ if (model) {
+ if (model->m_modelCache == 0) {
+ model->m_modelCache = new QDeclarativeListModel(owner, model, eng);
+ QDeclarativeEngine::setContextForObject(model->m_modelCache, QDeclarativeEngine::contextForObject(owner));
+ }
+
+ QObject *object = model->m_modelCache;
+ data = QVariant::fromValue(object);
+ }
+ }
+ break;
+ case ListLayout::Role::QObject:
+ {
+ QDeclarativeGuard<QObject> *guard = reinterpret_cast<QDeclarativeGuard<QObject> *>(mem);
+ QObject *object = guard->data();
+ if (object)
+ data = QVariant::fromValue(object);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return data;
+}
+
+int ListElement::setStringProperty(const ListLayout::Role &role, const QString &s)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::String) {
+ char *mem = getPropertyMemory(role);
+ QString *c = reinterpret_cast<QString *>(mem);
+ bool changed;
+ if (c->data_ptr() == 0) {
+ new (mem) QString(s);
+ changed = true;
} else {
- // This is the list in the main thread, so emit itemsChanged()
- emit r->model->m_listModel->itemsChanged(index, 1, roles);
+ changed = c->compare(s) != 0;
+ *c = s;
+ }
+ if (changed)
+ roleIndex = role.index;
+ } else {
+ qmlInfo(0) << "Unable to assign string to role '" << role.name << "' of different type.";
+ }
+
+ return roleIndex;
+}
+
+int ListElement::setDoubleProperty(const ListLayout::Role &role, double d)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::Number) {
+ char *mem = getPropertyMemory(role);
+ double *value = new (mem) double;
+ bool changed = *value != d;
+ *value = d;
+ if (changed)
+ roleIndex = role.index;
+ } else {
+ qmlInfo(0) << "Unable to assign number to role '" << role.name << "' of different type.";
+ }
+
+ return roleIndex;
+}
+
+int ListElement::setBoolProperty(const ListLayout::Role &role, bool b)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::Bool) {
+ char *mem = getPropertyMemory(role);
+ bool *value = new (mem) bool;
+ bool changed = *value != b;
+ *value = b;
+ if (changed)
+ roleIndex = role.index;
+ } else {
+ qmlInfo(0) << "Unable to assign bool to role '" << role.name << "' of different type.";
+ }
+
+ return roleIndex;
+}
+
+int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::List) {
+ char *mem = getPropertyMemory(role);
+ ListModel **value = new (mem) ListModel *;
+ if (*value) {
+ (*value)->destroy();
+ delete *value;
}
+ *value = m;
+ roleIndex = role.index;
+ } else {
+ qmlInfo(0) << "Unable to assign list to role '" << role.name << "' of different type.";
}
- return value;
+ return roleIndex;
}
-template<typename T>
-void qdeclarativelistmodel_move(int from, int to, int n, T *items)
+int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o)
{
- if (n == 1) {
- items->move(from, to);
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::QObject) {
+ char *mem = getPropertyMemory(role);
+ QDeclarativeGuard<QObject> *g = reinterpret_cast<QDeclarativeGuard<QObject> *>(mem);
+ bool changed = g->data() != o;
+ g->~QDeclarativeGuard();
+ new (mem) QDeclarativeGuard<QObject>(o);
+ if (changed)
+ roleIndex = role.index;
} else {
- T replaced;
- int i=0;
- typename T::ConstIterator it=items->begin(); it += from+n;
- for (; i<to-from; ++i,++it)
- replaced.append(*it);
- i=0;
- it=items->begin(); it += from;
- for (; i<n; ++i,++it)
- replaced.append(*it);
- typename T::ConstIterator f=replaced.begin();
- typename T::Iterator t=items->begin(); t += from;
- for (; f != replaced.end(); ++f, ++t)
- *t = *f;
+ qmlInfo(0) << "Unable to assign string to role '" << role.name << "' of different type.";
+ }
+
+ return roleIndex;
+}
+
+void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s)
+{
+ char *mem = getPropertyMemory(role);
+ new (mem) QString(s);
+}
+
+void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d)
+{
+ char *mem = getPropertyMemory(role);
+ double *value = new (mem) double;
+ *value = d;
+}
+
+void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b)
+{
+ char *mem = getPropertyMemory(role);
+ bool *value = new (mem) bool;
+ *value = b;
+}
+
+void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QObject *o)
+{
+ char *mem = getPropertyMemory(role);
+ new (mem) QDeclarativeGuard<QObject>(o);
+}
+
+void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m)
+{
+ char *mem = getPropertyMemory(role);
+ ListModel **value = new (mem) ListModel *;
+ *value = m;
+}
+
+void ListElement::clearProperty(const ListLayout::Role &role)
+{
+ switch (role.type) {
+ case ListLayout::Role::String:
+ setStringProperty(role, QString());
+ break;
+ case ListLayout::Role::Number:
+ setDoubleProperty(role, 0.0);
+ break;
+ case ListLayout::Role::Bool:
+ setBoolProperty(role, false);
+ break;
+ case ListLayout::Role::List:
+ setListProperty(role, 0);
+ break;
+ case ListLayout::Role::QObject:
+ setQObjectProperty(role, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+ListElement::ListElement()
+{
+ m_objectCache = 0;
+ uid = ListModel::allocateUid();
+ next = 0;
+ qMemSet(data, 0, sizeof(data));
+}
+
+ListElement::ListElement(int existingUid)
+{
+ m_objectCache = 0;
+ uid = existingUid;
+ next = 0;
+ qMemSet(data, 0, sizeof(data));
+}
+
+ListElement::~ListElement()
+{
+ delete next;
+}
+
+void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash<int, ListModel *> *targetModelHash)
+{
+ for (int i=0 ; i < srcLayout->roleCount() ; ++i) {
+ const ListLayout::Role &srcRole = srcLayout->getExistingRole(i);
+ const ListLayout::Role &targetRole = targetLayout->getExistingRole(i);
+
+ switch (srcRole.type) {
+ case ListLayout::Role::List:
+ {
+ ListModel *srcSubModel = src->getListProperty(srcRole);
+ ListModel *targetSubModel = target->getListProperty(targetRole);
+
+ if (srcSubModel) {
+ if (targetSubModel == 0) {
+ targetSubModel = new ListModel(targetRole.subLayout, 0, srcSubModel->getUid());
+ target->setListPropertyFast(targetRole, targetSubModel);
+ }
+ ListModel::sync(srcSubModel, targetSubModel, targetModelHash);
+ }
+ }
+ break;
+ case ListLayout::Role::QObject:
+ {
+ QObject *object = src->getQObjectProperty(srcRole);
+ target->setQObjectProperty(targetRole, object);
+ }
+ break;
+ case ListLayout::Role::String:
+ case ListLayout::Role::Number:
+ case ListLayout::Role::Bool:
+ {
+ QVariant v = src->getProperty(srcRole, 0, 0);
+ target->setVariantProperty(targetRole, v);
+ }
+ default:
+ break;
+ }
+ }
+
+}
+
+void ListElement::destroy(ListLayout *layout)
+{
+ if (layout) {
+ for (int i=0 ; i < layout->roleCount() ; ++i) {
+ const ListLayout::Role &r = layout->getExistingRole(i);
+
+ switch (r.type) {
+ case ListLayout::Role::String:
+ {
+ QString *string = getStringProperty(r);
+ if (string)
+ string->~QString();
+ }
+ break;
+ case ListLayout::Role::List:
+ {
+ ListModel *model = getListProperty(r);
+ if (model) {
+ model->destroy();
+ delete model;
+ }
+ }
+ break;
+ case ListLayout::Role::QObject:
+ {
+ QDeclarativeGuard<QObject> *guard = getGuardProperty(r);
+ guard->~QDeclarativeGuard();
+ }
+ break;
+ default:
+ // other types don't need explicit cleanup.
+ break;
+ }
+ }
+
+ delete m_objectCache;
+ }
+
+ if (next)
+ next->destroy(0);
+ uid = -1;
+}
+
+int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant &d)
+{
+ int roleIndex = -1;
+
+ switch (role.type) {
+ case ListLayout::Role::Number:
+ roleIndex = setDoubleProperty(role, d.toDouble());
+ break;
+ case ListLayout::Role::String:
+ roleIndex = setStringProperty(role, d.toString());
+ break;
+ case ListLayout::Role::Bool:
+ roleIndex = setBoolProperty(role, d.toBool());
+ break;
+ case ListLayout::Role::List:
+ roleIndex = setListProperty(role, d.value<ListModel *>());
+ break;
+ default:
+ break;
+ }
+
+ return roleIndex;
+}
+
+int ListElement::setJsProperty(const ListLayout::Role &role, v8::Handle<v8::Value> d)
+{
+ // Check if this key exists yet
+ int roleIndex = -1;
+
+ // Add the value now
+ if (d->IsString()) {
+ v8::Handle<v8::String> jsString = d->ToString();
+ QString qstr;
+ qstr.resize(jsString->Length());
+ jsString->Write(reinterpret_cast<uint16_t*>(qstr.data()));
+ roleIndex = setStringProperty(role, qstr);
+ } else if (d->IsNumber()) {
+ roleIndex = setDoubleProperty(role, d->NumberValue());
+ } else if (d->IsArray()) {
+ ListModel *subModel = new ListModel(role.subLayout, 0, -1);
+ v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(d);
+ int arrayLength = subArray->Length();
+ for (int j=0 ; j < arrayLength ; ++j) {
+ v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject();
+ subModel->append(subObject);
+ }
+ roleIndex = setListProperty(role, subModel);
+ } else if (d->IsInt32()) {
+ roleIndex = setDoubleProperty(role, (double) d->Int32Value());
+ } else if (d->IsBoolean()) {
+ roleIndex = setBoolProperty(role, d->BooleanValue());
+ } else if (d->IsObject()) {
+ QV8ObjectResource *r = (QV8ObjectResource *) d->ToObject()->GetExternalResource();
+ if (role.type == ListLayout::Role::QObject && r && r->resourceType() == QV8ObjectResource::QObjectType) {
+ QObject *o = QV8QObjectWrapper::toQObject(r);
+ roleIndex = setQObjectProperty(role, o);
+ }
+ } else if (d.IsEmpty() || d->IsUndefined() || d->IsNull()) {
+ clearProperty(role);
+ }
+
+ return roleIndex;
+}
+
+ModelObject::ModelObject(QDeclarativeListModel *model, int elementIndex)
+: m_model(model), m_elementIndex(elementIndex), m_meta(new ModelNodeMetaObject(this))
+{
+ updateValues();
+ setNodeUpdatesEnabled(true);
+}
+
+void ModelObject::updateValues()
+{
+ int roleCount = m_model->m_listModel->roleCount();
+ for (int i=0 ; i < roleCount ; ++i) {
+ const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i);
+ QByteArray name = role.name.toUtf8();
+ const QVariant &data = m_model->data(m_elementIndex, i);
+ setValue(name, data, role.type == ListLayout::Role::List);
+ }
+}
+
+void ModelObject::updateValues(const QList<int> &roles)
+{
+ int roleCount = roles.count();
+ for (int i=0 ; i < roleCount ; ++i) {
+ int roleIndex = roles.at(i);
+ const ListLayout::Role &role = m_model->m_listModel->getExistingRole(roleIndex);
+ QByteArray name = role.name.toUtf8();
+ const QVariant &data = m_model->data(m_elementIndex, roleIndex);
+ setValue(name, data, role.type == ListLayout::Role::List);
+ }
+}
+
+ModelNodeMetaObject::ModelNodeMetaObject(ModelObject *object)
+: QDeclarativeOpenMetaObject(object), m_enabled(false), m_obj(object)
+{
+}
+
+ModelNodeMetaObject::~ModelNodeMetaObject()
+{
+}
+
+void ModelNodeMetaObject::propertyWritten(int index)
+{
+ if (!m_enabled)
+ return;
+
+ QV8Engine *eng = m_obj->m_model->engine();
+
+ QString propName = QString::fromUtf8(name(index));
+ QVariant value = operator[](index);
+
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(eng->context());
+
+ v8::Handle<v8::Value> v = eng->fromVariant(value);
+
+ int roleIndex = m_obj->m_model->m_listModel->setExistingProperty(m_obj->m_elementIndex, propName, v);
+ if (roleIndex != -1) {
+ QList<int> roles;
+ roles << roleIndex;
+ m_obj->m_model->emitItemsChanged(m_obj->m_elementIndex, 1, roles);
}
}
@@ -293,114 +1174,112 @@ QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListM
handler. You must call sync() or else the changes made to the list from the external
thread will not be reflected in the list model in the main thread.
- \section1 Restrictions
-
- If a list model is to be accessed from a WorkerScript, it cannot
- contain list-type data. So, the following model cannot be used from a WorkerScript
- because of the list contained in the "attributes" property:
-
- \code
- ListModel {
- id: fruitModel
- ListElement {
- name: "Apple"
- cost: 2.45
- attributes: [
- ListElement { description: "Core" },
- ListElement { description: "Deciduous" }
- ]
- }
- }
- \endcode
-
- In addition, the WorkerScript cannot add list-type data to the model.
-
\sa {qmlmodels}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtDeclarative
*/
+QDeclarativeListModel::QDeclarativeListModel(QObject *parent)
+: QListModelInterface(parent)
+{
+ m_mainThread = true;
+ m_primary = true;
+ m_agent = 0;
-/*
- A ListModel internally uses either a NestedListModel or FlatListModel.
-
- A NestedListModel can contain lists of ListElements (which
- when retrieved from get() is accessible as a list model within the list
- model) whereas a FlatListModel cannot.
+ m_layout = new ListLayout;
+ m_listModel = new ListModel(m_layout, this, -1);
- ListModel uses a NestedListModel to begin with, and if the model is later
- used from a WorkerScript, it changes to use a FlatListModel instead. This
- is because ModelNode (which abstracts the nested list model data) needs
- access to the declarative engine and script engine, which cannot be
- safely used from outside of the main thread.
-*/
+ m_engine = 0;
+}
-QDeclarativeListModel::QDeclarativeListModel(QObject *parent)
-: QListModelInterface(parent), m_agent(0), m_nested(new NestedListModel(this)), m_flat(0)
+QDeclarativeListModel::QDeclarativeListModel(const QDeclarativeListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent)
+: QListModelInterface(parent)
{
+ m_mainThread = owner->m_mainThread;
+ m_primary = false;
+ m_agent = owner->m_agent;
+
+ m_layout = 0;
+ m_listModel = data;
+
+ m_engine = eng;
}
-QDeclarativeListModel::QDeclarativeListModel(const QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *parent)
-: QListModelInterface(parent), m_agent(0), m_nested(0), m_flat(0)
+QDeclarativeListModel::QDeclarativeListModel(QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *agent)
+: QListModelInterface(agent)
{
- m_flat = new FlatListModel(this);
- m_flat->m_parentAgent = parent;
+ m_mainThread = false;
+ m_primary = true;
+ m_agent = agent;
- if (orig->m_flat) {
- m_flat->m_roles = orig->m_flat->m_roles;
- m_flat->m_strings = orig->m_flat->m_strings;
- m_flat->m_values = orig->m_flat->m_values;
+ m_layout = new ListLayout(orig->m_layout);
+ m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid());
+ ListModel::sync(orig->m_listModel, m_listModel, 0);
- m_flat->m_nodeData.reserve(m_flat->m_values.count());
- for (int i=0; i<m_flat->m_values.count(); i++)
- m_flat->m_nodeData << 0;
- }
+ m_engine = 0;
}
QDeclarativeListModel::~QDeclarativeListModel()
{
- if (m_agent)
- m_agent->release();
+ if (m_primary) {
+ m_listModel->destroy();
+ delete m_listModel;
- delete m_nested;
- delete m_flat;
-}
+ if (m_mainThread && m_agent)
+ m_agent->release();
+ }
-bool QDeclarativeListModel::flatten()
-{
- if (m_flat)
- return true;
+ m_listModel = 0;
- QList<int> roles = m_nested->roles();
+ delete m_layout;
+ m_layout = 0;
+}
- QList<QHash<int, QVariant> > values;
- bool hasNested = false;
- for (int i=0; i<m_nested->count(); i++) {
- values.append(m_nested->data(i, roles, &hasNested));
- if (hasNested)
- return false;
+QV8Engine *QDeclarativeListModel::engine() const
+{
+ if (m_engine == 0) {
+ m_engine = QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this));
}
- FlatListModel *flat = new FlatListModel(this);
- flat->m_values = values;
+ return m_engine;
+}
- for (int i=0; i<roles.count(); i++) {
- QString s = m_nested->toString(roles[i]);
- flat->m_roles.insert(roles[i], s);
- flat->m_strings.insert(s, roles[i]);
+void QDeclarativeListModel::emitItemsChanged(int index, int count, const QList<int> &roles)
+{
+ if (m_mainThread) {
+ emit itemsChanged(index, count, roles);
+ } else {
+ m_agent->data.changedChange(this, index, count, roles);
}
+}
- flat->m_nodeData.reserve(flat->m_values.count());
- for (int i=0; i<flat->m_values.count(); i++)
- flat->m_nodeData << 0;
+void QDeclarativeListModel::emitItemsRemoved(int index, int count)
+{
+ if (m_mainThread) {
+ emit itemsRemoved(index, count);
+ emit countChanged();
+ } else {
+ if (index == 0 && count == this->count())
+ m_agent->data.clearChange(this);
+ m_agent->data.removeChange(this, index, count);
+ }
+}
- m_flat = flat;
- delete m_nested;
- m_nested = 0;
- return true;
+void QDeclarativeListModel::emitItemsInserted(int index, int count)
+{
+ if (m_mainThread) {
+ emit itemsInserted(index, count);
+ emit countChanged();
+ } else {
+ m_agent->data.insertChange(this, index, count);
+ }
}
-bool QDeclarativeListModel::inWorkerThread() const
+void QDeclarativeListModel::emitItemsMoved(int from, int to, int n)
{
- return m_flat && m_flat->m_parentAgent;
+ if (m_mainThread) {
+ emit itemsMoved(from, to, n);
+ } else {
+ m_agent->data.moveChange(this, from, n, to);
+ }
}
QDeclarativeListModelWorkerAgent *QDeclarativeListModel::agent()
@@ -408,23 +1287,24 @@ QDeclarativeListModelWorkerAgent *QDeclarativeListModel::agent()
if (m_agent)
return m_agent;
- if (!flatten()) {
- qmlInfo(this) << "List contains list-type data and cannot be used from a worker script";
- return 0;
- }
-
m_agent = new QDeclarativeListModelWorkerAgent(this);
return m_agent;
}
QList<int> QDeclarativeListModel::roles() const
{
- return m_flat ? m_flat->roles() : m_nested->roles();
+ QList<int> rolesArray;
+
+ for (int i=0 ; i < m_listModel->roleCount() ; ++i)
+ rolesArray << i;
+
+ return rolesArray;
}
QString QDeclarativeListModel::toString(int role) const
{
- return m_flat ? m_flat->toString(role) : m_nested->toString(role);
+ const ListLayout::Role &r = m_listModel->getExistingRole(role);
+ return r.name;
}
QVariant QDeclarativeListModel::data(int index, int role) const
@@ -432,7 +1312,7 @@ QVariant QDeclarativeListModel::data(int index, int role) const
if (index >= count() || index < 0)
return QVariant();
- return m_flat ? m_flat->data(index, role) : m_nested->data(index, role);
+ return m_listModel->getProperty(index, role, this, engine());
}
/*!
@@ -441,7 +1321,7 @@ QVariant QDeclarativeListModel::data(int index, int role) const
*/
int QDeclarativeListModel::count() const
{
- return m_flat ? m_flat->count() : m_nested->count();
+ return m_listModel->elementCount();
}
/*!
@@ -454,47 +1334,9 @@ int QDeclarativeListModel::count() const
void QDeclarativeListModel::clear()
{
int cleared = count();
- if (m_flat)
- m_flat->clear();
- else
- m_nested->clear();
- if (!inWorkerThread()) {
- emit itemsRemoved(0, cleared);
- emit countChanged();
- }
-}
-
-QDeclarativeListModel *ModelNode::model(const NestedListModel *model)
-{
- if (!modelCache) {
- modelCache = new QDeclarativeListModel;
- QDeclarativeEngine::setContextForObject(modelCache,QDeclarativeEngine::contextForObject(model->m_listModel));
- modelCache->m_nested->_root = this; // ListModel defaults to nestable model
-
- for (int i=0; i<values.count(); ++i) {
- ModelNode *subNode = qvariant_cast<ModelNode *>(values.at(i));
- if (subNode)
- subNode->m_model = modelCache->m_nested;
- }
- }
- return modelCache;
-}
-
-ModelObject *ModelNode::object(const NestedListModel *model)
-{
- if (!objectCache) {
- QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(qmlEngine(model->m_listModel));
- objectCache = new ModelObject(this, const_cast<NestedListModel*>(model), ep->v8engine());
-
- QHash<QString, ModelNode *>::iterator it;
- for (it = properties.begin(); it != properties.end(); ++it) {
- objectCache->setValue(it.key().toUtf8(), model->valueForNode(*it));
- }
-
- objectCache->setNodeUpdatesEnabled(true);
- }
- return objectCache;
+ m_listModel->clear();
+ emitItemsRemoved(0, cleared);
}
/*!
@@ -511,15 +1353,9 @@ void QDeclarativeListModel::remove(int index)
return;
}
- if (m_flat)
- m_flat->remove(index);
- else
- m_nested->remove(index);
+ m_listModel->remove(index);
- if (!inWorkerThread()) {
- emit itemsRemoved(index, 1);
- emit countChanged();
- }
+ emitItemsRemoved(index, 1);
}
/*!
@@ -537,25 +1373,39 @@ void QDeclarativeListModel::remove(int index)
\sa set() append()
*/
-void QDeclarativeListModel::insert(int index, const QDeclarativeV8Handle &handle)
+
+void QDeclarativeListModel::insert(QDeclarativeV8Function *args)
{
- v8::Handle<v8::Value> valuemap = handle.toHandle();
+ if (args->Length() == 2) {
- if (!valuemap->IsObject() || valuemap->IsArray()) {
- qmlInfo(this) << tr("insert: value is not an object");
- return;
- }
+ v8::Handle<v8::Value> arg0 = (*args)[0];
+ int index = arg0->Int32Value();
- if (index < 0 || index > count()) {
- qmlInfo(this) << tr("insert: index %1 out of range").arg(index);
- return;
- }
+ if (index < 0 || index > count()) {
+ qmlInfo(this) << tr("insert: index %1 out of range").arg(index);
+ return;
+ }
- bool ok = m_flat ? m_flat->insert(index, valuemap) : m_nested->insert(index, valuemap);
+ v8::Handle<v8::Value> arg1 = (*args)[1];
- if (ok && !inWorkerThread()) {
- emit itemsInserted(index, 1);
- emit countChanged();
+ if (arg1->IsArray()) {
+ v8::Handle<v8::Array> objectArray = v8::Handle<v8::Array>::Cast(arg1);
+ int objectArrayLength = objectArray->Length();
+ for (int i=0 ; i < objectArrayLength ; ++i) {
+ v8::Handle<v8::Object> argObject = objectArray->Get(i)->ToObject();
+ m_listModel->insert(index+i, argObject);
+ }
+ emitItemsInserted(index, objectArrayLength);
+ } else if (arg1->IsObject()) {
+ v8::Handle<v8::Object> argObject = arg1->ToObject();
+
+ m_listModel->insert(index, argObject);
+ emitItemsInserted(index, 1);
+ } else {
+ qmlInfo(this) << tr("insert: value is not an object");
+ }
+ } else {
+ qmlInfo(this) << tr("insert: value is not an object");
}
}
@@ -582,25 +1432,8 @@ void QDeclarativeListModel::move(int from, int to, int n)
return;
}
- int origfrom = from;
- int origto = to;
- int orign = n;
- if (from > to) {
- // Only move forwards - flip if backwards moving
- int tfrom = from;
- int tto = to;
- from = tto;
- to = tto+n;
- n = tfrom-tto;
- }
-
- if (m_flat)
- m_flat->move(from, to, n);
- else
- m_nested->move(from, to, n);
-
- if (!inWorkerThread())
- emit itemsMoved(origfrom, origto, orign);
+ m_listModel->move(from, to, n);
+ emitItemsMoved(from, to, n);
}
/*!
@@ -615,16 +1448,32 @@ void QDeclarativeListModel::move(int from, int to, int n)
\sa set() remove()
*/
-void QDeclarativeListModel::append(const QDeclarativeV8Handle &handle)
-{
- v8::Handle<v8::Value> valuemap = handle.toHandle();
+void QDeclarativeListModel::append(QDeclarativeV8Function *args)
+{
+ if (args->Length() == 1) {
+ v8::Handle<v8::Value> arg = (*args)[0];
+
+ if (arg->IsArray()) {
+ v8::Handle<v8::Array> objectArray = v8::Handle<v8::Array>::Cast(arg);
+ int objectArrayLength = objectArray->Length();
+ int index = m_listModel->elementCount();
+ for (int i=0 ; i < objectArrayLength ; ++i) {
+ v8::Handle<v8::Object> argObject = objectArray->Get(i)->ToObject();
+ m_listModel->append(argObject);
+ }
+ emitItemsInserted(index, objectArrayLength);
+ } else if (arg->IsObject()) {
+ v8::Handle<v8::Object> argObject = arg->ToObject();
- if (!valuemap->IsObject() || valuemap->IsArray()) {
+ int index = m_listModel->append(argObject);
+ emitItemsInserted(index, 1);
+
+ } else {
+ qmlInfo(this) << tr("append: value is not an object");
+ }
+ } else {
qmlInfo(this) << tr("append: value is not an object");
- return;
}
-
- insert(count(), handle);
}
/*!
@@ -660,8 +1509,16 @@ void QDeclarativeListModel::append(const QDeclarativeV8Handle &handle)
*/
QDeclarativeV8Handle QDeclarativeListModel::get(int index) const
{
- // the internal flat/nested class checks for bad index
- return QDeclarativeV8Handle::fromHandle(m_flat ? m_flat->get(index) : m_nested->get(index));
+ v8::Handle<v8::Value> result = v8::Undefined();
+
+ if (index >= 0 && index < m_listModel->elementCount()) {
+ QV8Engine *v8engine = engine();
+
+ ModelObject *object = m_listModel->getOrCreateModelObject(const_cast<QDeclarativeListModel *>(this), index);
+ result = v8engine->newQObject(object);
+ }
+
+ return QDeclarativeV8Handle::fromHandle(result);
}
/*!
@@ -680,15 +1537,7 @@ QDeclarativeV8Handle QDeclarativeListModel::get(int index) const
\sa append()
*/
-void QDeclarativeListModel::set(int index, const QDeclarativeV8Handle &valuemap)
-{
- QList<int> roles;
- set(index, valuemap, &roles);
- if (!roles.isEmpty() && !inWorkerThread())
- emit itemsChanged(index, 1, roles);
-}
-
-void QDeclarativeListModel::set(int index, const QDeclarativeV8Handle &handle, QList<int> *roles)
+void QDeclarativeListModel::set(int index, const QDeclarativeV8Handle &handle)
{
v8::Handle<v8::Value> valuemap = handle.toHandle();
@@ -701,13 +1550,18 @@ void QDeclarativeListModel::set(int index, const QDeclarativeV8Handle &handle, Q
return;
}
+ v8::Handle<v8::Object> object = valuemap->ToObject();
+
if (index == count()) {
- append(handle);
+ m_listModel->insert(index, object);
+ emitItemsInserted(index, 1);
} else {
- if (m_flat)
- m_flat->set(index, valuemap, roles);
- else
- m_nested->set(index, valuemap, roles);
+
+ QList<int> roles;
+ m_listModel->set(index, object, &roles);
+
+ if (roles.count())
+ emitItemsChanged(index, 1, roles);
}
}
@@ -726,23 +1580,19 @@ void QDeclarativeListModel::set(int index, const QDeclarativeV8Handle &handle, Q
*/
void QDeclarativeListModel::setProperty(int index, const QString& property, const QVariant& value)
{
- QList<int> roles;
- setProperty(index, property, value, &roles);
- if (!roles.isEmpty() && !inWorkerThread())
- emit itemsChanged(index, 1, roles);
-}
-
-void QDeclarativeListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles)
-{
if (count() == 0 || index >= count() || index < 0) {
qmlInfo(this) << tr("set: index %1 out of range").arg(index);
return;
}
- if (m_flat)
- m_flat->setProperty(index, property, value, roles);
- else
- m_nested->setProperty(index, property, value, roles);
+ int roleIndex = m_listModel->setOrCreateProperty(index, property, value);
+ if (roleIndex != -1) {
+
+ QList<int> roles;
+ roles << roleIndex;
+
+ emitItemsChanged(index, 1, roles);
+ }
}
/*!
@@ -931,78 +1781,93 @@ void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray &
{
QDeclarativeListModel *rv = static_cast<QDeclarativeListModel *>(obj);
- ModelNode *root = new ModelNode(rv->m_nested);
- rv->m_nested->m_ownsRoot = true;
- rv->m_nested->_root = root;
- QStack<ModelNode *> nodes;
- nodes << root;
-
- bool processingSet = false;
+ QV8Engine *engine = QDeclarativeEnginePrivate::getV8Engine(qmlEngine(rv));
+ rv->m_engine = engine;
const ListModelData *lmd = (const ListModelData *)d.constData();
const char *data = ((const char *)lmd) + lmd->dataOffset;
+ QStack<DataStackElement> stack;
+
for (int ii = 0; ii < lmd->instrCount; ++ii) {
const ListInstruction &instr = lmd->instructions()[ii];
switch(instr.type) {
case ListInstruction::Push:
{
- ModelNode *n = nodes.top();
- ModelNode *n2 = new ModelNode(rv->m_nested);
- n->values << QVariant::fromValue(n2);
- nodes.push(n2);
- if (processingSet)
- n->isArray = true;
+ ListModel *subModel = 0;
+
+ if (stack.count() == 0) {
+ subModel = rv->m_listModel;
+ } else {
+ const DataStackElement &e0 = stack.at(stack.size() - 1);
+ DataStackElement &e1 = stack[stack.size() - 2];
+
+ const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name);
+ if (role.type == ListLayout::Role::List) {
+ subModel = e1.model->getListProperty(e1.elementIndex, role);
+
+ if (subModel == 0) {
+ subModel = new ListModel(role.subLayout, 0, -1);
+ QVariant vModel = QVariant::fromValue(subModel);
+ e1.model->setOrCreateProperty(e1.elementIndex, e0.name, vModel);
+ }
+ }
+ }
+
+ DataStackElement e;
+ e.model = subModel;
+ e.elementIndex = subModel ? subModel->appendElement() : -1;
+ stack.push(e);
}
break;
case ListInstruction::Pop:
- nodes.pop();
+ stack.pop();
break;
case ListInstruction::Value:
{
- ModelNode *n = nodes.top();
+ const DataStackElement &e0 = stack.at(stack.size() - 1);
+ DataStackElement &e1 = stack[stack.size() - 2];
+
+ QString name = e0.name;
+ QVariant value;
+
switch (QDeclarativeScript::Variant::Type(data[instr.dataIdx])) {
- case QDeclarativeScript::Variant::Invalid:
- n->isArray = true;
- break;
- case QDeclarativeScript::Variant::Boolean:
- n->values.append(bool(data[1 + instr.dataIdx]));
- break;
- case QDeclarativeScript::Variant::Number:
- n->values.append(QByteArray(data + 1 + instr.dataIdx).toDouble());
- break;
- case QDeclarativeScript::Variant::String:
- n->values.append(QString::fromUtf8(data + 1 + instr.dataIdx));
- break;
- default:
- Q_ASSERT("Format error in ListInstruction");
+ case QDeclarativeScript::Variant::Invalid:
+ {
+ const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name);
+ ListModel *emptyModel = new ListModel(role.subLayout, 0, -1);
+ value = QVariant::fromValue(emptyModel);
+ }
+ break;
+ case QDeclarativeScript::Variant::Boolean:
+ value = bool(data[1 + instr.dataIdx]);
+ break;
+ case QDeclarativeScript::Variant::Number:
+ value = QByteArray(data + 1 + instr.dataIdx).toDouble();
+ break;
+ case QDeclarativeScript::Variant::String:
+ value = QString::fromUtf8(data + 1 + instr.dataIdx);
+ break;
+ default:
+ Q_ASSERT("Format error in ListInstruction");
}
- processingSet = false;
+ e1.model->setOrCreateProperty(e1.elementIndex, name, value);
}
break;
case ListInstruction::Set:
{
- ModelNode *n = nodes.top();
- ModelNode *n2 = new ModelNode(rv->m_nested);
- n->properties.insert(QString::fromUtf8(data + instr.dataIdx), n2);
- nodes.push(n2);
- processingSet = true;
+ DataStackElement e;
+ e.name = QString::fromUtf8(data + instr.dataIdx);
+ stack.push(e);
}
break;
}
}
-
- ModelNode *rootNode = rv->m_nested->_root;
- for (int i=0; i<rootNode->values.count(); ++i) {
- ModelNode *node = qvariant_cast<ModelNode *>(rootNode->values[i]);
- node->listIndex = i;
- node->updateListIndexes();
- }
}
bool QDeclarativeListModelParser::definesEmptyList(const QString &s)
@@ -1060,691 +1925,4 @@ bool QDeclarativeListModelParser::definesEmptyList(const QString &s)
\sa ListModel
*/
-FlatListModel::FlatListModel(QDeclarativeListModel *base)
-: m_engine(0), m_listModel(base), m_parentAgent(0)
-{
-}
-
-FlatListModel::~FlatListModel()
-{
- qDeleteAll(m_nodeData);
-}
-
-QVariant FlatListModel::data(int index, int role) const
-{
- Q_ASSERT(index >= 0 && index < m_values.count());
- if (m_values[index].contains(role))
- return m_values[index][role];
- return QVariant();
-}
-
-QList<int> FlatListModel::roles() const
-{
- return m_roles.keys();
-}
-
-QString FlatListModel::toString(int role) const
-{
- if (m_roles.contains(role))
- return m_roles[role];
- return QString();
-}
-
-int FlatListModel::count() const
-{
- return m_values.count();
-}
-
-void FlatListModel::clear()
-{
- m_values.clear();
-
- qDeleteAll(m_nodeData);
- m_nodeData.clear();
-}
-
-void FlatListModel::remove(int index)
-{
- m_values.removeAt(index);
- removedNode(index);
-}
-
-bool FlatListModel::insert(int index, v8::Handle<v8::Value> value)
-{
- Q_ASSERT(index >= 0 && index <= m_values.count());
-
- QHash<int, QVariant> row;
- if (!addValue(value, &row, 0))
- return false;
-
- m_values.insert(index, row);
- insertedNode(index);
-
- return true;
-}
-
-QV8Engine *FlatListModel::engine() const
-{
- return m_engine?m_engine:QDeclarativeEnginePrivate::getV8Engine(qmlEngine(m_listModel));
-}
-
-QV8Engine *NestedListModel::engine() const
-{
- return QDeclarativeEnginePrivate::getV8Engine(qmlEngine(m_listModel));
-}
-
-v8::Handle<v8::Value> FlatListModel::get(int index) const
-{
- QV8Engine *v8engine = engine();
-
- if (!v8engine)
- return v8::Undefined();
-
- if (index < 0 || index >= m_values.count())
- return v8::Undefined();
-
- FlatListModel *that = const_cast<FlatListModel*>(this);
-
- FlatNodeData *data = m_nodeData.value(index);
- if (!data) {
- data = new FlatNodeData(index);
- that->m_nodeData.replace(index, data);
- }
-
- v8::Local<v8::Object> rv = QDeclarativeListModelV8Data::create(v8engine);
- QV8ListModelResource *r = new QV8ListModelResource(that, data, v8engine);
- rv->SetExternalResource(r);
- return rv;
-}
-
-void FlatListModel::set(int index, v8::Handle<v8::Value> value, QList<int> *roles)
-{
- Q_ASSERT(index >= 0 && index < m_values.count());
-
- QHash<int, QVariant> row = m_values[index];
- if (addValue(value, &row, roles))
- m_values[index] = row;
-}
-
-void FlatListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles)
-{
- Q_ASSERT(index >= 0 && index < m_values.count());
-
- QHash<QString, int>::Iterator iter = m_strings.find(property);
- int role;
- if (iter == m_strings.end()) {
- role = m_roles.count();
- m_roles.insert(role, property);
- m_strings.insert(property, role);
- } else {
- role = iter.value();
- }
-
- if (m_values[index][role] != value) {
- roles->append(role);
- m_values[index][role] = value;
- }
-}
-
-void FlatListModel::move(int from, int to, int n)
-{
- qdeclarativelistmodel_move<QList<QHash<int, QVariant> > >(from, to, n, &m_values);
- moveNodes(from, to, n);
-}
-
-bool FlatListModel::addValue(v8::Handle<v8::Value> value, QHash<int, QVariant> *row, QList<int> *roles)
-{
- if (!value->IsObject())
- return false;
-
- v8::Local<v8::Array> properties = engine()->getOwnPropertyNames(value->ToObject());
- uint32_t length = properties->Length();
- for (uint32_t ii = 0; ii < length; ++ii) {
- // XXX TryCatch?
- v8::Handle<v8::Value> property = properties->Get(ii);
- v8::Handle<v8::Value> jsv = value->ToObject()->Get(property);
-
- if (!jsv->IsRegExp() && !jsv->IsDate() && jsv->IsObject() && !engine()->isVariant(jsv)) {
- qmlInfo(m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script";
- return false;
- }
-
- QString name = engine()->toString(property);
- QVariant v = engine()->toVariant(jsv, -1);
-
- QHash<QString, int>::Iterator iter = m_strings.find(name);
- if (iter == m_strings.end()) {
- int role = m_roles.count();
- m_roles.insert(role, name);
- iter = m_strings.insert(name, role);
- if (roles)
- roles->append(role);
- } else {
- int role = iter.value();
- if (roles && row->contains(role) && row->value(role) != v)
- roles->append(role);
- }
- row->insert(*iter, v);
- }
- return true;
-}
-
-void FlatListModel::insertedNode(int index)
-{
- if (index >= 0 && index <= m_values.count()) {
- m_nodeData.insert(index, 0);
-
- for (int i=index + 1; i<m_nodeData.count(); i++) {
- if (m_nodeData[i])
- m_nodeData[i]->index = i;
- }
- }
-}
-
-void FlatListModel::removedNode(int index)
-{
- if (index >= 0 && index < m_nodeData.count()) {
- delete m_nodeData.takeAt(index);
-
- for (int i=index; i<m_nodeData.count(); i++) {
- if (m_nodeData[i])
- m_nodeData[i]->index = i;
- }
- }
-}
-
-void FlatListModel::moveNodes(int from, int to, int n)
-{
- if (!m_listModel->canMove(from, to, n))
- return;
-
- qdeclarativelistmodel_move<QList<FlatNodeData *> >(from, to, n, &m_nodeData);
-
- for (int i=from; i<from + (to-from); i++) {
- if (m_nodeData[i])
- m_nodeData[i]->index = i;
- }
-}
-
-FlatNodeData::~FlatNodeData()
-{
- for (QSet<QV8ListModelResource *>::Iterator iter = objects.begin(); iter != objects.end(); ++iter) {
- QV8ListModelResource *data = *iter;
- data->nodeData = 0;
- }
-}
-
-void FlatNodeData::addData(QV8ListModelResource *data)
-{
- objects.insert(data);
-}
-
-void FlatNodeData::removeData(QV8ListModelResource *data)
-{
- objects.remove(data);
-}
-
-NestedListModel::NestedListModel(QDeclarativeListModel *base)
-: _root(0), m_ownsRoot(false), m_listModel(base), _rolesOk(false)
-{
-}
-
-NestedListModel::~NestedListModel()
-{
- if (m_ownsRoot)
- delete _root;
-}
-
-QVariant NestedListModel::valueForNode(ModelNode *node, bool *hasNested) const
-{
- QObject *rv = 0;
- if (hasNested)
- *hasNested = false;
-
- if (node->isArray) {
- // List
- rv = node->model(this);
- if (hasNested)
- *hasNested = true;
- } else {
- if (!node->properties.isEmpty()) {
- // Object
- rv = node->object(this);
- } else if (node->values.count() == 0) {
- // Invalid
- return QVariant();
- } else if (node->values.count() == 1) {
- // Value
- QVariant &var = node->values[0];
- ModelNode *valueNode = qvariant_cast<ModelNode *>(var);
- if (valueNode) {
- if (!valueNode->properties.isEmpty())
- rv = valueNode->object(this);
- else
- rv = valueNode->model(this);
- } else {
- return var;
- }
- }
- }
-
- if (rv) {
- return QVariant::fromValue(rv);
- } else {
- return QVariant();
- }
-}
-
-QHash<int,QVariant> NestedListModel::data(int index, const QList<int> &roles, bool *hasNested) const
-{
- Q_ASSERT(_root && index >= 0 && index < _root->values.count());
- checkRoles();
- QHash<int, QVariant> rv;
-
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
- if (!node)
- return rv;
-
- for (int ii = 0; ii < roles.count(); ++ii) {
- const QString &roleString = roleStrings.at(roles.at(ii));
-
- QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString);
- if (iter != node->properties.end()) {
- ModelNode *row = *iter;
- rv.insert(roles.at(ii), valueForNode(row, hasNested));
- }
- }
-
- return rv;
-}
-
-QVariant NestedListModel::data(int index, int role) const
-{
- Q_ASSERT(_root && index >= 0 && index < _root->values.count());
- checkRoles();
- QVariant rv;
- if (roleStrings.count() < role)
- return rv;
-
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
- if (!node)
- return rv;
-
- const QString &roleString = roleStrings.at(role);
-
- QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString);
- if (iter != node->properties.end()) {
- ModelNode *row = *iter;
- rv = valueForNode(row);
- }
-
- return rv;
-}
-
-int NestedListModel::count() const
-{
- if (!_root) return 0;
- return _root->values.count();
-}
-
-void NestedListModel::clear()
-{
- if (_root)
- _root->clear();
-}
-
-void NestedListModel::remove(int index)
-{
- if (!_root)
- return;
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
- _root->values.removeAt(index);
- for (int i = 0; i < _root->values.count(); ++i) {
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(i));
- if (node)
- node->listIndex = i;
- }
- if (node)
- delete node;
-}
-
-bool NestedListModel::insert(int index, v8::Handle<v8::Value> valuemap)
-{
- if (!_root) {
- _root = new ModelNode(this);
- m_ownsRoot = true;
- }
-
- ModelNode *mn = new ModelNode(this);
- mn->listIndex = index;
- mn->setObjectValue(valuemap);
- _root->values.insert(index,QVariant::fromValue(mn));
- for (int i = index + 1; i < _root->values.count(); ++i) {
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(i));
- if (node)
- node->listIndex = i;
- }
- return true;
-}
-
-void NestedListModel::move(int from, int to, int n)
-{
- if (!_root)
- return;
- qdeclarativelistmodel_move<QVariantList>(from, to, n, &_root->values);
- for (int i = qMin(from, to), end = qMin(_root->values.count(), qMax(from, to) + n); i < end; ++i) {
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(i));
- if (node)
- node->listIndex = i;
- }
-}
-
-v8::Handle<v8::Value> NestedListModel::get(int index) const
-{
- QDeclarativeEngine *eng = qmlEngine(m_listModel);
- if (!eng)
- return v8::Undefined();;
-
- if (index < 0 || index >= count())
- return v8::Undefined();
-
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
- if (!node)
- return v8::Undefined();;
-
- return QDeclarativeEnginePrivate::get(eng)->v8engine()->newQObject(node->object(this));
-}
-
-void NestedListModel::set(int index, v8::Handle<v8::Value> valuemap, QList<int> *roles)
-{
- Q_ASSERT(index >=0 && index < count());
-
- if (!valuemap->IsObject())
- return;
-
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
- bool emitItemsChanged = node->setObjectValue(valuemap);
- if (!emitItemsChanged)
- return;
-
- QV8Engine *v8engine = engine();
-
- v8::Local<v8::Array> properties = v8engine->getOwnPropertyNames(valuemap->ToObject());
- uint32_t length = properties->Length();
- for (uint32_t ii = 0; ii < length; ++ii) {
- // XXX TryCatch?
- v8::Handle<v8::Value> property = properties->Get(ii);
- QString name = v8engine->toString(property);
-
- int r = roleStrings.indexOf(name);
- if (r < 0) {
- r = roleStrings.count();
- roleStrings << name;
- }
- roles->append(r);
- }
-}
-
-void NestedListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles)
-{
- Q_ASSERT(index >=0 && index < count());
-
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
- bool emitItemsChanged = node->setProperty(property, value);
- if (!emitItemsChanged)
- return;
-
- int r = roleStrings.indexOf(property);
- if (r < 0) {
- r = roleStrings.count();
- roleStrings << property;
- }
- roles->append(r);
-}
-
-void NestedListModel::checkRoles() const
-{
- if (_rolesOk || !_root)
- return;
-
- for (int i = 0; i<_root->values.count(); ++i) {
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(i));
- if (node) {
- foreach (const QString &role, node->properties.keys()) {
- if (!roleStrings.contains(role))
- roleStrings.append(role);
- }
- }
- }
-
- _rolesOk = true;
-}
-
-QList<int> NestedListModel::roles() const
-{
- checkRoles();
- QList<int> rv;
- for (int ii = 0; ii < roleStrings.count(); ++ii)
- rv << ii;
- return rv;
-}
-
-QString NestedListModel::toString(int role) const
-{
- checkRoles();
- if (role < roleStrings.count())
- return roleStrings.at(role);
- else
- return QString();
-}
-
-
-ModelNode::ModelNode(NestedListModel *model)
-: modelCache(0), objectCache(0), isArray(false), m_model(model), listIndex(-1)
-{
-}
-
-ModelNode::~ModelNode()
-{
- clear();
- if (modelCache) { modelCache->m_nested->_root = 0/* ==this */; delete modelCache; modelCache = 0; }
- if (objectCache) { delete objectCache; objectCache = 0; }
-}
-
-void ModelNode::clear()
-{
- ModelNode *node;
- for (int ii = 0; ii < values.count(); ++ii) {
- node = qvariant_cast<ModelNode *>(values.at(ii));
- if (node) { delete node; node = 0; }
- }
- values.clear();
-
- qDeleteAll(properties.values());
- properties.clear();
-}
-
-bool ModelNode::setObjectValue(v8::Handle<v8::Value> valuemap, bool writeToCache)
-{
- if (!valuemap->IsObject())
- return false;
-
- bool emitItemsChanged = false;
-
- QV8Engine *v8engine = m_model->engine();
-
- v8::Local<v8::Array> propertyNames = v8engine->getOwnPropertyNames(valuemap->ToObject());
- uint32_t length = propertyNames->Length();
-
- for (uint32_t ii = 0; ii < length; ++ii) {
- // XXX TryCatch?
- v8::Handle<v8::Value> property = propertyNames->Get(ii);
- v8::Handle<v8::Value> v = valuemap->ToObject()->Get(property);
-
- QString name = v8engine->toString(property);
- ModelNode *prev = properties.value(name);
- ModelNode *value = new ModelNode(m_model);
-
- if (v->IsArray()) {
- value->isArray = true;
- value->setListValue(v);
- if (writeToCache && objectCache)
- objectCache->setValue(name.toUtf8(), QVariant::fromValue(value->model(m_model)));
- emitItemsChanged = true; // for now, too inefficient to check whether list and sublists have changed
- } else {
- value->values << v8engine->toVariant(v, -1);
- if (writeToCache && objectCache)
- objectCache->setValue(name.toUtf8(), value->values.last());
- if (!emitItemsChanged && prev && prev->values.count() == 1
- && prev->values[0] != value->values.last()) {
- emitItemsChanged = true;
- }
- }
- if (properties.contains(name))
- delete properties[name];
- properties.insert(name, value);
- }
- return emitItemsChanged;
-}
-
-void ModelNode::setListValue(v8::Handle<v8::Value> valuelist)
-{
- Q_ASSERT(valuelist->IsArray());
- values.clear();
-
- QV8Engine *engine = m_model->engine();
-
- v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(valuelist);
- uint32_t length = array->Length();
- for (uint32_t ii = 0; ii < length; ++ii) {
- ModelNode *value = new ModelNode(m_model);
-
- // XXX TryCatch?
- v8::Handle<v8::Value> v = array->Get(ii);
-
- if (v->IsArray()) {
- value->isArray = true;
- value->setListValue(v);
- } else if (v->IsObject()) {
- value->listIndex = ii;
- value->setObjectValue(v);
- } else {
- value->listIndex = ii;
- value->values << engine->toVariant(v, -1);
- }
-
- values.append(QVariant::fromValue(value));
- }
-}
-
-bool ModelNode::setProperty(const QString& prop, const QVariant& val) {
- QHash<QString, ModelNode *>::const_iterator it = properties.find(prop);
- bool emitItemsChanged = false;
- if (it != properties.end()) {
- if (val != (*it)->values[0])
- emitItemsChanged = true;
- (*it)->values[0] = val;
- } else {
- ModelNode *n = new ModelNode(m_model);
- n->values << val;
- properties.insert(prop,n);
- }
- if (objectCache)
- objectCache->setValue(prop.toUtf8(), val);
- return emitItemsChanged;
-}
-
-void ModelNode::updateListIndexes()
-{
- for (QHash<QString, ModelNode *>::ConstIterator iter = properties.begin(); iter != properties.end(); ++iter) {
- ModelNode *node = iter.value();
- if (node->isArray) {
- for (int i=0; i<node->values.count(); ++i) {
- ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(i));
- if (subNode)
- subNode->listIndex = i;
- }
- }
- node->updateListIndexes();
- }
-}
-
-/*
- Need to call this to emit itemsChanged() for modifications outside of set()
- and setProperty(), i.e. if an item returned from get() is modified
-*/
-void ModelNode::changedProperty(const QString &name) const
-{
- if (listIndex < 0)
- return;
-
- m_model->checkRoles();
- QList<int> roles;
- int role = m_model->roleStrings.indexOf(name);
- if (role < 0)
- roles = m_model->roles();
- else
- roles << role;
- emit m_model->m_listModel->itemsChanged(listIndex, 1, roles);
-}
-
-void ModelNode::dump(ModelNode *node, int ind)
-{
- QByteArray indentBa(ind * 4, ' ');
- const char *indent = indentBa.constData();
-
- for (int ii = 0; ii < node->values.count(); ++ii) {
- ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(ii));
- if (subNode) {
- qWarning().nospace() << indent << "Sub-node " << ii;
- dump(subNode, ind + 1);
- } else {
- qWarning().nospace() << indent << "Sub-node " << ii << ": " << node->values.at(ii).toString();
- }
- }
-
- for (QHash<QString, ModelNode *>::ConstIterator iter = node->properties.begin(); iter != node->properties.end(); ++iter) {
- qWarning().nospace() << indent << "Property " << iter.key() << ':';
- dump(iter.value(), ind + 1);
- }
-}
-
-ModelObject::ModelObject(ModelNode *node, NestedListModel *model, QV8Engine *eng)
-: m_model(model), m_node(node), m_meta(new ModelNodeMetaObject(eng, this))
-{
-}
-
-void ModelObject::setValue(const QByteArray &name, const QVariant &val)
-{
- m_meta->setValue(name, val);
- //setProperty(name.constData(), val);
-}
-
-void ModelObject::setNodeUpdatesEnabled(bool enable)
-{
- m_meta->m_enabled = enable;
-}
-
-ModelNodeMetaObject::ModelNodeMetaObject(QV8Engine *eng, ModelObject *object)
-: QDeclarativeOpenMetaObject(object), m_enabled(false), m_engine(eng), m_obj(object)
-{
-}
-
-void ModelNodeMetaObject::propertyWritten(int index)
-{
- if (!m_enabled)
- return;
-
- QString propName = QString::fromUtf8(name(index));
- QVariant value = operator[](index);
-
- v8::HandleScope handle_scope;
- v8::Context::Scope scope(m_engine->context());
- v8::Local<v8::Object> object = v8::Object::New();
- object->Set(m_engine->toString(propName), m_engine->variantWrapper()->newVariant(value));
- bool changed = m_obj->m_node->setObjectValue(object, false);
- if (changed)
- m_obj->m_node->changedProperty(propName);
-}
-
QT_END_NAMESPACE