From 94267bb148fe4c991a10d4aa4c01373821d9216e Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Tue, 8 Sep 2009 15:08:54 +1000 Subject: Rest of change 28ccffc53df374f7b890510f481e2dae2e01ed74 --- src/declarative/fx/qfxvisualitemmodel.cpp | 19 ++- src/declarative/util/qmllistmodel.cpp | 271 +++++++++++++++++++++++++++++- src/declarative/util/qmllistmodel.h | 8 + 3 files changed, 288 insertions(+), 10 deletions(-) diff --git a/src/declarative/fx/qfxvisualitemmodel.cpp b/src/declarative/fx/qfxvisualitemmodel.cpp index 82bec09..cac8b8d 100644 --- a/src/declarative/fx/qfxvisualitemmodel.cpp +++ b/src/declarative/fx/qfxvisualitemmodel.cpp @@ -243,6 +243,16 @@ public: QmlContext *m_context; QList m_roles; QHash m_roleNames; + void ensureRoles() { + if (m_roles.isEmpty()) { + if (m_listModelInterface) { + m_roles = m_listModelInterface->roles(); + for (int ii = 0; ii < m_roles.count(); ++ii) + m_roleNames.insert(m_roles.at(ii), + m_listModelInterface->toString(m_roles.at(ii))); + } + } + } struct ObjectRef { ObjectRef(QObject *object=0) : obj(object), ref(1) {} @@ -375,6 +385,7 @@ int QFxVisualDataModelDataMetaObject::createProperty(const char *name, const cha return QmlOpenMetaObject::createProperty(name, type); } else { const QLatin1String sname(name); + data->m_model->ensureRoles(); for (QHash::ConstIterator iter = data->m_model->m_roleNames.begin(); iter != data->m_model->m_roleNames.end(); ++iter) { @@ -397,6 +408,7 @@ QFxVisualDataModelDataMetaObject::propertyCreated(int, QMetaPropertyBuilder &pro && data->m_model->m_modelList) { return data->m_model->m_modelList->at(data->m_index); } else if (data->m_model->m_listModelInterface) { + data->m_model->ensureRoles(); for (QHash::ConstIterator iter = data->m_model->m_roleNames.begin(); iter != data->m_model->m_roleNames.end(); ++iter) { @@ -410,6 +422,7 @@ QFxVisualDataModelDataMetaObject::propertyCreated(int, QMetaPropertyBuilder &pro } } } else if (data->m_model->m_abstractItemModel) { + data->m_model->ensureRoles(); for (QHash::ConstIterator iter = data->m_model->m_roleNames.begin(); iter != data->m_model->m_roleNames.end(); ++iter) { @@ -560,12 +573,6 @@ void QFxVisualDataModel::setModel(const QVariant &model) if (object && (d->m_listModelInterface = qobject_cast(object))) { d->m_roles.clear(); d->m_roleNames.clear(); - if (d->m_listModelInterface) { - d->m_roles = d->m_listModelInterface->roles(); - for (int ii = 0; ii < d->m_roles.count(); ++ii) - d->m_roleNames.insert(d->m_roles.at(ii), - d->m_listModelInterface->toString(d->m_roles.at(ii))); - } QObject::connect(d->m_listModelInterface, SIGNAL(itemsChanged(int,int,QList)), this, SLOT(_q_itemsChanged(int,int,QList))); diff --git a/src/declarative/util/qmllistmodel.cpp b/src/declarative/util/qmllistmodel.cpp index 0d9ea94..a5ae60f 100644 --- a/src/declarative/util/qmllistmodel.cpp +++ b/src/declarative/util/qmllistmodel.cpp @@ -72,7 +72,9 @@ struct ListModelData \qmlclass ListModel \brief The ListModel element defines a free-form list data source. - The ListModel is a simple hierarchy of elements containing data roles. + The ListModel is a simple hierarchy of elements containing data roles. The contents can + be defined dynamically, or explicitly in QML: + For example: \code @@ -166,6 +168,29 @@ struct ListModelData } \endcode + The content of a ListModel may be created and modified using the clear(), + append(), and set() methods. For example: + + \code + Component { + id: FruitDelegate + Item { + width: 200; height: 50 + Text { text: name } + Text { text: '$'+cost; anchors.right: parent.right } + + // Double the price when clicked. + MouseRegion { + anchors.fill: parent + onClicked: FruitModel.set(index, "cost", cost*2) + } + } + } + \endcode + + When creating content dynamically, note that the set of available properties cannot be changed + except by first clearing the model - whatever properties are first added are then the + only permitted properties in the model. */ class ModelObject : public QObject @@ -189,7 +214,6 @@ struct ModelNode { ModelNode(); ~ModelNode(); - QString className; QList values; QHash properties; @@ -214,6 +238,19 @@ struct ModelNode return objectCache; } + void setProperty(const QString& prop, const QVariant& val) { + QHash::const_iterator it = properties.find(prop); + if (it != properties.end()) { + (*it)->values[0] = val; + } else { + ModelNode *n = new ModelNode; + n->values << val; + properties.insert(prop,n); + } + if (objectCache) + objectCache->setValue(prop.toLatin1(), val); + } + QmlListModel *modelCache; ModelObject *objectCache; }; @@ -235,7 +272,7 @@ QmlListModel::~QmlListModel() void QmlListModel::checkRoles() const { - if (_rolesOk) + if (_rolesOk || !_root) return; for (int ii = 0; ii < _root->values.count(); ++ii) { @@ -341,6 +378,232 @@ int QmlListModel::count() const return _root->values.count(); } +/*! + \qmlmethod ListModel::clear() + + Deletes all content from the model. The properties are cleared such that + different properties may be set on subsequent additions. + + \sa append() remove() +*/ +void QmlListModel::clear() +{ + int cleared = count(); + _rolesOk = false; + delete _root; + _root = 0; + roleStrings.clear(); + emit itemsRemoved(0,cleared); +} + +/*! + \qmlmethod ListModel::remove(int index) + + Deletes the content at \a index from the model. + + \sa clear() +*/ +void QmlListModel::remove(int index) +{ + if (_root) { + ModelNode *node = qvariant_cast(_root->values.at(index)); + _root->values.remove(index); + if (node) + delete node; + emit itemsRemoved(index,1); + } +} + +/*! + \qmlmethod ListModel::insert(index,dict) + + Adds a new item to the list model at position \a index, with the + values in \a dict. + + \code + FruitModel.insert(2, {"cost": 5.95, "name":"Pizza"}) + \endcode + + If \a index is not in the list, sufficient empty items are + added to the list. + + \sa set() append() +*/ +void QmlListModel::insert(int index, const QVariantMap& valuemap) +{ + if (!_root) + _root = new ModelNode; + if (index >= _root->values.count()) { + set(index,valuemap); + return; + } + ModelNode *mn = new ModelNode; + for (QVariantMap::const_iterator it=valuemap.begin(); it!=valuemap.end(); ++it) { + addRole(it.key()); + ModelNode *value = new ModelNode; + value->values << it.value(); + mn->properties.insert(it.key(),value); + } + _root->values.insert(index,qVariantFromValue(mn)); + emit itemsInserted(index,1); +} + +/*! + \qmlmethod ListModel::move(from,to,n) + + Moves \a n items \a from one position \a to another. + + The from and to ranges must exist; for example, to move the first 3 items + to the end of the list: + + \code + FruitModel.move(0,FruitModel.count-3,3) + \endcode + + \sa append() +*/ +void QmlListModel::move(int from, int to, int n) +{ + if (from+n > count() || to+n > count() || n==0 || from==to) + return; + 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 (n==1) { + _root->values.move(from,to); + } else { + QList replaced; + int i=0; + QVariantList::const_iterator it=_root->values.begin(); it += from+n; + for (; ivalues.begin(); it += from; + for (; ivalues.begin(); t += from; + for (; f != replaced.end(); ++f, ++t) + *t = *f; + } + emit itemsMoved(from,to,n); +} + +/*! + \qmlmethod ListModel::append(dict) + + Adds a new item to the end of the list model, with the + values in \a dict. + + \code + FruitModel.append({"cost": 5.95, "name":"Pizza"}) + \endcode + + \sa set() remove() +*/ +void QmlListModel::append(const QVariantMap& valuemap) +{ + if (!_root) + _root = new ModelNode; + ModelNode *mn = new ModelNode; + for (QVariantMap::const_iterator it=valuemap.begin(); it!=valuemap.end(); ++it) { + addRole(it.key()); + ModelNode *value = new ModelNode; + value->values << it.value(); + mn->properties.insert(it.key(),value); + } + _root->values << qVariantFromValue(mn); + emit itemsInserted(count()-1,1); +} + +/*! + \qmlmethod ListModel::set(index,dict) + + Changes the item at \a index in the list model to the + values in \a dict. + + \code + FruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) + \endcode + + If \a index is not in the list, sufficient empty items are + added to the list. + + \sa append() +*/ +void QmlListModel::set(int index, const QVariantMap& valuemap) +{ + if (!_root) + _root = new ModelNode; + int initialcount = _root->values.count(); + while (index > _root->values.count()) + _root->values.append(qVariantFromValue(new ModelNode)); + if (index == _root->values.count()) + append(valuemap); + else { + ModelNode *node = qvariant_cast(_root->values.at(index)); + QList roles; + for (QVariantMap::const_iterator it=valuemap.begin(); it!=valuemap.end(); ++it) { + node->setProperty(it.key(),it.value()); + int r = roleStrings.indexOf(it.key()); + if (r<0) { + r = roleStrings.count(); + roleStrings << it.key(); + } + roles.append(r); + } + if (initialcount < index) { + emit itemsInserted(initialcount,index-initialcount+1); + } else { + emit itemsChanged(index,1,roles); + } + } +} + +/*! + \qmlmethod ListModel::set(index,property,value) + + Changes the \a property of the item at \a index in the list model to \a value. + + \code + FruitModel.set(3, "cost", 5.95) + \endcode + + If \a index is not in the list, sufficient empty items are + added to the list. + + \sa append() +*/ +void QmlListModel::set(int index, const QString& property, const QVariant& value) +{ + if (!_root) + _root = new ModelNode; + int initialcount = _root->values.count(); + while (index >= _root->values.count()) + _root->values.append(qVariantFromValue(new ModelNode)); + ModelNode *node = qvariant_cast(_root->values.at(index)); + int r = roleStrings.indexOf(property); + if (r<0) { + r = roleStrings.count(); + roleStrings << property; + } + QList roles; + roles.append(r); + + if (node) + node->setProperty(property,value); + if (initialcount < index) + emit itemsInserted(initialcount,index-initialcount+1); + else + emit itemsChanged(index,1,roles); +} + + class QmlListModelParser : public QmlCustomParser { public: @@ -518,7 +781,7 @@ static void dump(ModelNode *node, int ind) for (int ii = 0; ii < node->values.count(); ++ii) { ModelNode *subNode = qvariant_cast(node->values.at(ii)); if (subNode) { - qWarning().nospace() << indent << "Sub-node " << ii << ": class " << subNode->className; + qWarning().nospace() << indent << "Sub-node " << ii; dump(subNode, ind + 1); } else { qWarning().nospace() << indent << "Sub-node " << ii << ": " << node->values.at(ii).toString(); diff --git a/src/declarative/util/qmllistmodel.h b/src/declarative/util/qmllistmodel.h index 39edbe4..8bef347 100644 --- a/src/declarative/util/qmllistmodel.h +++ b/src/declarative/util/qmllistmodel.h @@ -72,6 +72,14 @@ public: virtual int count() const; virtual QHash data(int index, const QList &roles = (QList())) const; + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(int index); + Q_INVOKABLE void append(const QVariantMap& valuemap); + Q_INVOKABLE void insert(int index, const QVariantMap& valuemap); + Q_INVOKABLE void set(int index, const QVariantMap& valuemap); + Q_INVOKABLE void set(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + private: QVariant valueForNode(ModelNode *) const; mutable QStringList roleStrings; -- cgit v0.12