From 78b3aed3264439cd95ca23a47e53e15b769a58ae Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Wed, 23 Jan 2013 15:17:27 -0800 Subject: Move QML types to types folder The QtQml module has gain a bunch of QML types cluttering up the qml folder. Moving them to types organizes them a bit better. Change-Id: I570884c00f4abc48f4f1aea048bf002bc70223f3 Reviewed-by: Gabriel de Dietrich Reviewed-by: Lars Knoll --- src/qml/items/items.pri | 12 - src/qml/items/qqmldelegatemodel.cpp | 3189 ---------------------------- src/qml/items/qqmldelegatemodel_p.h | 234 -- src/qml/items/qqmldelegatemodel_p_p.h | 411 ---- src/qml/items/qqmlmodelsmodule.cpp | 60 - src/qml/items/qqmlmodelsmodule_p.h | 57 - src/qml/items/qqmlobjectmodel.cpp | 269 --- src/qml/items/qqmlobjectmodel_p.h | 170 -- src/qml/items/qquickpackage.cpp | 198 -- src/qml/items/qquickpackage_p.h | 92 - src/qml/qml.pro | 2 +- src/qml/qml/qml.pri | 19 +- src/qml/qml/qqmlbind.cpp | 310 --- src/qml/qml/qqmlbind_p.h | 92 - src/qml/qml/qqmlconnections.cpp | 321 --- src/qml/qml/qqmlconnections_p.h | 96 - src/qml/qml/qqmlengine.cpp | 2 +- src/qml/qml/qqmllistmodel.cpp | 2588 ---------------------- src/qml/qml/qqmllistmodel_p.h | 198 -- src/qml/qml/qqmllistmodel_p_p.h | 378 ---- src/qml/qml/qqmllistmodelworkeragent.cpp | 259 --- src/qml/qml/qqmllistmodelworkeragent_p.h | 156 -- src/qml/qml/qqmltimer.cpp | 328 --- src/qml/qml/qqmltimer_p.h | 107 - src/qml/qml/qquickworkerscript.cpp | 740 ------- src/qml/qml/qquickworkerscript_p.h | 126 -- src/qml/types/qqmlbind.cpp | 310 +++ src/qml/types/qqmlbind_p.h | 92 + src/qml/types/qqmlconnections.cpp | 321 +++ src/qml/types/qqmlconnections_p.h | 96 + src/qml/types/qqmldelegatemodel.cpp | 3189 ++++++++++++++++++++++++++++ src/qml/types/qqmldelegatemodel_p.h | 234 ++ src/qml/types/qqmldelegatemodel_p_p.h | 411 ++++ src/qml/types/qqmllistmodel.cpp | 2588 ++++++++++++++++++++++ src/qml/types/qqmllistmodel_p.h | 198 ++ src/qml/types/qqmllistmodel_p_p.h | 378 ++++ src/qml/types/qqmllistmodelworkeragent.cpp | 259 +++ src/qml/types/qqmllistmodelworkeragent_p.h | 156 ++ src/qml/types/qqmlmodelsmodule.cpp | 60 + src/qml/types/qqmlmodelsmodule_p.h | 57 + src/qml/types/qqmlobjectmodel.cpp | 269 +++ src/qml/types/qqmlobjectmodel_p.h | 170 ++ src/qml/types/qqmltimer.cpp | 328 +++ src/qml/types/qqmltimer_p.h | 107 + src/qml/types/qquickpackage.cpp | 198 ++ src/qml/types/qquickpackage_p.h | 92 + src/qml/types/qquickworkerscript.cpp | 740 +++++++ src/qml/types/qquickworkerscript_p.h | 126 ++ src/qml/types/types.pri | 26 + 49 files changed, 10410 insertions(+), 10409 deletions(-) delete mode 100644 src/qml/items/items.pri delete mode 100644 src/qml/items/qqmldelegatemodel.cpp delete mode 100644 src/qml/items/qqmldelegatemodel_p.h delete mode 100644 src/qml/items/qqmldelegatemodel_p_p.h delete mode 100644 src/qml/items/qqmlmodelsmodule.cpp delete mode 100644 src/qml/items/qqmlmodelsmodule_p.h delete mode 100644 src/qml/items/qqmlobjectmodel.cpp delete mode 100644 src/qml/items/qqmlobjectmodel_p.h delete mode 100644 src/qml/items/qquickpackage.cpp delete mode 100644 src/qml/items/qquickpackage_p.h delete mode 100644 src/qml/qml/qqmlbind.cpp delete mode 100644 src/qml/qml/qqmlbind_p.h delete mode 100644 src/qml/qml/qqmlconnections.cpp delete mode 100644 src/qml/qml/qqmlconnections_p.h delete mode 100644 src/qml/qml/qqmllistmodel.cpp delete mode 100644 src/qml/qml/qqmllistmodel_p.h delete mode 100644 src/qml/qml/qqmllistmodel_p_p.h delete mode 100644 src/qml/qml/qqmllistmodelworkeragent.cpp delete mode 100644 src/qml/qml/qqmllistmodelworkeragent_p.h delete mode 100644 src/qml/qml/qqmltimer.cpp delete mode 100644 src/qml/qml/qqmltimer_p.h delete mode 100644 src/qml/qml/qquickworkerscript.cpp delete mode 100644 src/qml/qml/qquickworkerscript_p.h create mode 100644 src/qml/types/qqmlbind.cpp create mode 100644 src/qml/types/qqmlbind_p.h create mode 100644 src/qml/types/qqmlconnections.cpp create mode 100644 src/qml/types/qqmlconnections_p.h create mode 100644 src/qml/types/qqmldelegatemodel.cpp create mode 100644 src/qml/types/qqmldelegatemodel_p.h create mode 100644 src/qml/types/qqmldelegatemodel_p_p.h create mode 100644 src/qml/types/qqmllistmodel.cpp create mode 100644 src/qml/types/qqmllistmodel_p.h create mode 100644 src/qml/types/qqmllistmodel_p_p.h create mode 100644 src/qml/types/qqmllistmodelworkeragent.cpp create mode 100644 src/qml/types/qqmllistmodelworkeragent_p.h create mode 100644 src/qml/types/qqmlmodelsmodule.cpp create mode 100644 src/qml/types/qqmlmodelsmodule_p.h create mode 100644 src/qml/types/qqmlobjectmodel.cpp create mode 100644 src/qml/types/qqmlobjectmodel_p.h create mode 100644 src/qml/types/qqmltimer.cpp create mode 100644 src/qml/types/qqmltimer_p.h create mode 100644 src/qml/types/qquickpackage.cpp create mode 100644 src/qml/types/qquickpackage_p.h create mode 100644 src/qml/types/qquickworkerscript.cpp create mode 100644 src/qml/types/qquickworkerscript_p.h create mode 100644 src/qml/types/types.pri (limited to 'src/qml') diff --git a/src/qml/items/items.pri b/src/qml/items/items.pri deleted file mode 100644 index f5224f0acc..0000000000 --- a/src/qml/items/items.pri +++ /dev/null @@ -1,12 +0,0 @@ -SOURCES += \ - $$PWD/qquickpackage.cpp \ - $$PWD/qqmldelegatemodel.cpp \ - $$PWD/qqmlobjectmodel.cpp \ - $$PWD/qqmlmodelsmodule.cpp - -HEADERS += \ - $$PWD/qquickpackage_p.h \ - $$PWD/qqmldelegatemodel_p.h \ - $$PWD/qqmldelegatemodel_p_p.h \ - $$PWD/qqmlobjectmodel_p.h \ - $$PWD/qqmlmodelsmodule_p.h diff --git a/src/qml/items/qqmldelegatemodel.cpp b/src/qml/items/qqmldelegatemodel.cpp deleted file mode 100644 index efbd98bdbc..0000000000 --- a/src/qml/items/qqmldelegatemodel.cpp +++ /dev/null @@ -1,3189 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmldelegatemodel_p_p.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQmlDelegateModelEngineData : public QV8Engine::Deletable -{ -public: - enum - { - Model, - Groups, - IsUnresolved, - ItemsIndex, - PersistedItemsIndex, - InItems, - InPersistedItems, - StringCount - }; - - QQmlDelegateModelEngineData(QV8Engine *engine); - ~QQmlDelegateModelEngineData(); - - v8::Local array( - QV8Engine *engine, const QVector &changes); - v8::Local array( - QV8Engine *engine, const QVector &changes); - v8::Local array( - QV8Engine *engine, const QVector &changes); - - - inline v8::Local model() { return strings->Get(Model)->ToString(); } - inline v8::Local groups() { return strings->Get(Groups)->ToString(); } - inline v8::Local isUnresolved() { return strings->Get(IsUnresolved)->ToString(); } - inline v8::Local itemsIndex() { return strings->Get(ItemsIndex)->ToString(); } - inline v8::Local persistedItemsIndex() { return strings->Get(PersistedItemsIndex)->ToString(); } - inline v8::Local inItems() { return strings->Get(InItems)->ToString(); } - inline v8::Local inPersistedItems() { return strings->Get(InPersistedItems)->ToString(); } - - v8::Persistent strings; - v8::Persistent constructorChange; - v8::Persistent constructorChangeArray; -}; - -V8_DEFINE_EXTENSION(QQmlDelegateModelEngineData, engineData) - - -void QQmlDelegateModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) -{ - prop.setWritable(false); -} - -QVariant QQmlDelegateModelPartsMetaObject::initialValue(int id) -{ - QQmlDelegateModelParts *parts = static_cast(object()); - QQmlPartsModel *m = new QQmlPartsModel( - parts->model, QString::fromUtf8(name(id)), parts); - parts->models.append(m); - return QVariant::fromValue(static_cast(m)); -} - -QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent) -: QObject(parent), model(parent) -{ - new QQmlDelegateModelPartsMetaObject(this); -} - -//--------------------------------------------------------------------------- - -/*! - \qmltype VisualDataModel - \instantiates QQmlDelegateModel - \inqmlmodule QtQuick 2 - \ingroup qtquick-models - \brief Encapsulates a model and delegate - - The VisualDataModel type encapsulates a model and the delegate that will - be instantiated for items in a model. - - This type is provided by QtQuick 2 for compatibility reasons. The same implementation - is now primarily available as DelegateModel in the QtQml.Models module. - - \sa {QtQml.Models2::DelegateModel} -*/ -/*! - \qmltype DelegateModel - \instantiates QQmlDelegateModel - \inqmlmodule QtQml.Models 2 - \brief Encapsulates a model and delegate - - The DelegateModel type encapsulates a model and the delegate that will - be instantiated for items in the model. - - This element is also available as DelegateModel in the QtQuick module. For full details, - see the \l DelegateModel documentation. - - The DelegateModel type encapsulates a model and the delegate that will - be instantiated for items in the model. - - It is usually not necessary to create a DelegateModel. - However, it can be useful for manipulating and accessing the \l modelIndex - when a QAbstractItemModel subclass is used as the - model. Also, DelegateModel is used together with \l Package to - provide delegates to multiple views, and with DelegateModelGroup to sort and filter - delegate items. - - The example below illustrates using a DelegateModel with a ListView. - - \snippet qml/visualdatamodel.qml 0 -*/ - -QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt) - : m_delegate(0) - , m_cacheMetaType(0) - , m_context(ctxt) - , m_parts(0) - , m_filterGroup(QStringLiteral("items")) - , m_count(0) - , m_groupCount(Compositor::MinimumGroupCount) - , m_compositorGroup(Compositor::Cache) - , m_complete(false) - , m_delegateValidated(false) - , m_reset(false) - , m_transaction(false) - , m_incubatorCleanupScheduled(false) - , m_cacheItems(0) - , m_items(0) - , m_persistedItems(0) -{ -} - -QQmlDelegateModelPrivate::~QQmlDelegateModelPrivate() -{ - qDeleteAll(m_finishedIncubating); - - if (m_cacheMetaType) - m_cacheMetaType->release(); -} - -void QQmlDelegateModelPrivate::init() -{ - Q_Q(QQmlDelegateModel); - m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag); - - m_items = new QQmlDelegateModelGroup(QStringLiteral("items"), q, Compositor::Default, q); - m_items->setDefaultInclude(true); - m_persistedItems = new QQmlDelegateModelGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q); - QQmlDelegateModelGroupPrivate::get(m_items)->emitters.insert(this); -} - -QQmlDelegateModel::QQmlDelegateModel() -: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(0))) -{ - Q_D(QQmlDelegateModel); - d->init(); -} - -QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent) -: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(ctxt)), parent) -{ - Q_D(QQmlDelegateModel); - d->init(); -} - -QQmlDelegateModel::~QQmlDelegateModel() -{ - Q_D(QQmlDelegateModel); - - foreach (QQmlDelegateModelItem *cacheItem, d->m_cache) { - if (cacheItem->object) { - delete cacheItem->object; - - cacheItem->object = 0; - cacheItem->contextData->destroy(); - cacheItem->contextData = 0; - cacheItem->scriptRef -= 1; - } - cacheItem->groups &= ~Compositor::UnresolvedFlag; - cacheItem->objectRef = 0; - if (!cacheItem->isReferenced()) - delete cacheItem; - } -} - - -void QQmlDelegateModel::classBegin() -{ - Q_D(QQmlDelegateModel); - if (!d->m_context) - d->m_context = qmlContext(this); -} - -void QQmlDelegateModel::componentComplete() -{ - Q_D(QQmlDelegateModel); - d->m_complete = true; - - int defaultGroups = 0; - QStringList groupNames; - groupNames.append(QStringLiteral("items")); - groupNames.append(QStringLiteral("persistedItems")); - if (QQmlDelegateModelGroupPrivate::get(d->m_items)->defaultInclude) - defaultGroups |= Compositor::DefaultFlag; - if (QQmlDelegateModelGroupPrivate::get(d->m_persistedItems)->defaultInclude) - defaultGroups |= Compositor::PersistedFlag; - for (int i = Compositor::MinimumGroupCount; i < d->m_groupCount; ++i) { - QString name = d->m_groups[i]->name(); - if (name.isEmpty()) { - d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; - --d->m_groupCount; - --i; - } else if (name.at(0).isUpper()) { - qmlInfo(d->m_groups[i]) << QQmlDelegateModelGroup::tr("Group names must start with a lower case letter"); - d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; - --d->m_groupCount; - --i; - } else { - groupNames.append(name); - - QQmlDelegateModelGroupPrivate *group = QQmlDelegateModelGroupPrivate::get(d->m_groups[i]); - group->setModel(this, Compositor::Group(i)); - if (group->defaultInclude) - defaultGroups |= (1 << i); - } - } - - d->m_cacheMetaType = new QQmlDelegateModelItemMetaType( - QQmlEnginePrivate::getV8Engine(d->m_context->engine()), this, groupNames); - - d->m_compositor.setGroupCount(d->m_groupCount); - d->m_compositor.setDefaultGroups(defaultGroups); - d->updateFilterGroup(); - - while (!d->m_pendingParts.isEmpty()) - static_cast(d->m_pendingParts.first())->updateFilterGroup(); - - QVector inserts; - d->m_count = d->m_adaptorModel.count(); - d->m_compositor.append( - &d->m_adaptorModel, - 0, - d->m_count, - defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag, - &inserts); - d->itemsInserted(inserts); - d->emitChanges(); - - if (d->m_adaptorModel.canFetchMore()) - QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); -} - -/*! - \qmlproperty model QtQml.Models2::DelegateModel::model - This property holds the model providing data for the DelegateModel. - - The model provides a set of data that is used to create the items - for a view. For large or dynamic datasets the model is usually - provided by a C++ model object. The C++ model object must be a \l - {QAbstractItemModel} subclass or a simple list. - - Models can also be created directly in QML, using a \l{ListModel} or - \l{XmlListModel}. - - \sa {qml-data-models}{Data Models} -*/ -QVariant QQmlDelegateModel::model() const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.model(); -} - -void QQmlDelegateModel::setModel(const QVariant &model) -{ - Q_D(QQmlDelegateModel); - - if (d->m_complete) - _q_itemsRemoved(0, d->m_count); - - d->m_adaptorModel.setModel(model, this, d->m_context->engine()); - d->m_adaptorModel.replaceWatchedRoles(QList(), d->m_watchedRoles); - for (int i = 0; d->m_parts && i < d->m_parts->models.count(); ++i) { - d->m_adaptorModel.replaceWatchedRoles( - QList(), d->m_parts->models.at(i)->watchedRoles()); - } - - if (d->m_complete) { - _q_itemsInserted(0, d->m_adaptorModel.count()); - if (d->m_adaptorModel.canFetchMore()) - QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); - } -} - -/*! - \qmlproperty Component QtQml.Models2::DelegateModel::delegate - - The delegate provides a template defining each item instantiated by a view. - The index is exposed as an accessible \c index property. Properties of the - model are also available depending upon the type of \l {qml-data-models}{Data Model}. -*/ -QQmlComponent *QQmlDelegateModel::delegate() const -{ - Q_D(const QQmlDelegateModel); - return d->m_delegate; -} - -void QQmlDelegateModel::setDelegate(QQmlComponent *delegate) -{ - Q_D(QQmlDelegateModel); - if (d->m_transaction) { - qmlInfo(this) << tr("The delegate of a DelegateModel cannot be changed within onUpdated."); - return; - } - bool wasValid = d->m_delegate != 0; - d->m_delegate = delegate; - d->m_delegateValidated = false; - if (wasValid && d->m_complete) { - for (int i = 1; i < d->m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.remove( - 0, d->m_compositor.count(Compositor::Group(i))); - } - } - if (d->m_complete && d->m_delegate) { - for (int i = 1; i < d->m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.insert( - 0, d->m_compositor.count(Compositor::Group(i))); - } - } - d->emitChanges(); -} - -/*! - \qmlproperty QModelIndex QtQml.Models2::DelegateModel::rootIndex - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. \c rootIndex allows the children of - any node in a QAbstractItemModel to be provided by this model. - - This property only affects models of type QAbstractItemModel that - are hierarchical (e.g, a tree model). - - For example, here is a simple interactive file system browser. - When a directory name is clicked, the view's \c rootIndex is set to the - QModelIndex node of the clicked directory, thus updating the view to show - the new directory's contents. - - \c main.cpp: - \snippet qml/visualdatamodel_rootindex/main.cpp 0 - - \c view.qml: - \snippet qml/visualdatamodel_rootindex/view.qml 0 - - If the \l model is a QAbstractItemModel subclass, the delegate can also - reference a \c hasModelChildren property (optionally qualified by a - \e model. prefix) that indicates whether the delegate's model item has - any child nodes. - - - \sa modelIndex(), parentModelIndex() -*/ -QVariant QQmlDelegateModel::rootIndex() const -{ - Q_D(const QQmlDelegateModel); - return QVariant::fromValue(QModelIndex(d->m_adaptorModel.rootIndex)); -} - -void QQmlDelegateModel::setRootIndex(const QVariant &root) -{ - Q_D(QQmlDelegateModel); - - QModelIndex modelIndex = qvariant_cast(root); - const bool changed = d->m_adaptorModel.rootIndex != modelIndex; - if (changed || !d->m_adaptorModel.isValid()) { - const int oldCount = d->m_count; - d->m_adaptorModel.rootIndex = modelIndex; - if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) // The previous root index was invalidated, so we need to reconnect the model. - d->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine()); - if (d->m_adaptorModel.canFetchMore()) - d->m_adaptorModel.fetchMore(); - if (d->m_complete) { - const int newCount = d->m_adaptorModel.count(); - if (oldCount) - _q_itemsRemoved(0, oldCount); - if (newCount) - _q_itemsInserted(0, newCount); - } - if (changed) - emit rootIndexChanged(); - } -} - -/*! - \qmlmethod QModelIndex QtQml.Models2::DelegateModel::modelIndex(int index) - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. This function assists in using - tree models in QML. - - Returns a QModelIndex for the specified index. - This value can be assigned to rootIndex. - - \sa rootIndex -*/ -QVariant QQmlDelegateModel::modelIndex(int idx) const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.modelIndex(idx); -} - -/*! - \qmlmethod QModelIndex QtQml.Models2::DelegateModel::parentModelIndex() - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. This function assists in using - tree models in QML. - - Returns a QModelIndex for the parent of the current rootIndex. - This value can be assigned to rootIndex. - - \sa rootIndex -*/ -QVariant QQmlDelegateModel::parentModelIndex() const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.parentModelIndex(); -} - -/*! - \qmlproperty int QtQml.Models2::DelegateModel::count -*/ - -int QQmlDelegateModel::count() const -{ - Q_D(const QQmlDelegateModel); - if (!d->m_delegate) - return 0; - return d->m_compositor.count(d->m_compositorGroup); -} - -QQmlDelegateModel::ReleaseFlags QQmlDelegateModelPrivate::release(QObject *object) -{ - QQmlDelegateModel::ReleaseFlags stat = 0; - if (!object) - return stat; - - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object)) { - if (cacheItem->releaseObject()) { - cacheItem->destroyObject(); - emitDestroyingItem(object); - if (cacheItem->incubationTask) { - releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = 0; - } - cacheItem->Dispose(); - stat |= QQmlInstanceModel::Destroyed; - } else { - stat |= QQmlDelegateModel::Referenced; - } - } - return stat; -} - -/* - Returns ReleaseStatus flags. -*/ - -QQmlDelegateModel::ReleaseFlags QQmlDelegateModel::release(QObject *item) -{ - Q_D(QQmlDelegateModel); - QQmlInstanceModel::ReleaseFlags stat = d->release(item); - return stat; -} - -// Cancel a requested async item -void QQmlDelegateModel::cancel(int index) -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "DelegateModel::cancel: index out range" << index << d->m_compositor.count(d->m_compositorGroup); - return; - } - - Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index); - QQmlDelegateModelItem *cacheItem = it->inCache() ? d->m_cache.at(it.cacheIndex) : 0; - if (cacheItem) { - if (cacheItem->incubationTask && !cacheItem->isObjectReferenced()) { - d->releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = 0; - - if (cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickPackage *package = qmlobject_cast(object)) - d->emitDestroyingPackage(package); - else - d->emitDestroyingItem(object); - } - - cacheItem->scriptRef -= 1; - } - if (!cacheItem->isReferenced()) { - d->m_compositor.clearFlags(Compositor::Cache, it.cacheIndex, 1, Compositor::CacheFlag); - d->m_cache.removeAt(it.cacheIndex); - delete cacheItem; - Q_ASSERT(d->m_cache.count() == d->m_compositor.count(Compositor::Cache)); - } - } -} - -void QQmlDelegateModelPrivate::group_append( - QQmlListProperty *property, QQmlDelegateModelGroup *group) -{ - QQmlDelegateModelPrivate *d = static_cast(property->data); - if (d->m_complete) - return; - if (d->m_groupCount == Compositor::MaximumGroupCount) { - qmlInfo(d->q_func()) << QQmlDelegateModel::tr("The maximum number of supported DelegateModelGroups is 8"); - return; - } - d->m_groups[d->m_groupCount] = group; - d->m_groupCount += 1; -} - -int QQmlDelegateModelPrivate::group_count( - QQmlListProperty *property) -{ - QQmlDelegateModelPrivate *d = static_cast(property->data); - return d->m_groupCount - 1; -} - -QQmlDelegateModelGroup *QQmlDelegateModelPrivate::group_at( - QQmlListProperty *property, int index) -{ - QQmlDelegateModelPrivate *d = static_cast(property->data); - return index >= 0 && index < d->m_groupCount - 1 - ? d->m_groups[index + 1] - : 0; -} - -/*! - \qmlproperty list QtQml.Models2::DelegateModel::groups - - This property holds a visual data model's group definitions. - - Groups define a sub-set of the items in a visual data model and can be used to filter - a model. - - For every group defined in a DelegateModel two attached properties are added to each - delegate item. The first of the form DelegateModel.in\e{GroupName} holds whether the - item belongs to the group and the second DelegateModel.\e{groupName}Index holds the - index of the item in that group. - - The following example illustrates using groups to select items in a model. - - \snippet qml/visualdatagroup.qml 0 -*/ - -QQmlListProperty QQmlDelegateModel::groups() -{ - Q_D(QQmlDelegateModel); - return QQmlListProperty( - this, - d, - QQmlDelegateModelPrivate::group_append, - QQmlDelegateModelPrivate::group_count, - QQmlDelegateModelPrivate::group_at, - 0); -} - -/*! - \qmlproperty DelegateModelGroup QtQml.Models2::DelegateModel::items - - This property holds visual data model's default group to which all new items are added. -*/ - -QQmlDelegateModelGroup *QQmlDelegateModel::items() -{ - Q_D(QQmlDelegateModel); - return d->m_items; -} - -/*! - \qmlproperty DelegateModelGroup QtQml.Models2::DelegateModel::persistedItems - - This property holds visual data model's persisted items group. - - Items in this group are not destroyed when released by a view, instead they are persisted - until removed from the group. - - An item can be removed from the persistedItems group by setting the - DelegateModel.inPersistedItems property to false. If the item is not referenced by a view - at that time it will be destroyed. Adding an item to this group will not create a new - instance. - - Items returned by the \l QtQml.Models2::DelegateModelGroup::create() function are automatically added - to this group. -*/ - -QQmlDelegateModelGroup *QQmlDelegateModel::persistedItems() -{ - Q_D(QQmlDelegateModel); - return d->m_persistedItems; -} - -/*! - \qmlproperty string QtQml.Models2::DelegateModel::filterOnGroup - - This property holds the name of the group used to filter the visual data model. - - Only items which belong to this group are visible to a view. - - By default this is the \l items group. -*/ - -QString QQmlDelegateModel::filterGroup() const -{ - Q_D(const QQmlDelegateModel); - return d->m_filterGroup; -} - -void QQmlDelegateModel::setFilterGroup(const QString &group) -{ - Q_D(QQmlDelegateModel); - - if (d->m_transaction) { - qmlInfo(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); - return; - } - - if (d->m_filterGroup != group) { - d->m_filterGroup = group; - d->updateFilterGroup(); - emit filterGroupChanged(); - } -} - -void QQmlDelegateModel::resetFilterGroup() -{ - setFilterGroup(QStringLiteral("items")); -} - -void QQmlDelegateModelPrivate::updateFilterGroup() -{ - Q_Q(QQmlDelegateModel); - if (!m_cacheMetaType) - return; - - QQmlListCompositor::Group previousGroup = m_compositorGroup; - m_compositorGroup = Compositor::Default; - for (int i = 1; i < m_groupCount; ++i) { - if (m_filterGroup == m_cacheMetaType->groupNames.at(i - 1)) { - m_compositorGroup = Compositor::Group(i); - break; - } - } - - QQmlDelegateModelGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this); - if (m_compositorGroup != previousGroup) { - QVector removes; - QVector inserts; - m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); - - QQmlChangeSet changeSet; - changeSet.move(removes, inserts); - emit q->modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit q->countChanged(); - - if (m_parts) { - foreach (QQmlPartsModel *model, m_parts->models) - model->updateFilterGroup(m_compositorGroup, changeSet); - } - } -} - -/*! - \qmlproperty object QtQml.Models2::DelegateModel::parts - - The \a parts property selects a DelegateModel which creates - delegates from the part named. This is used in conjunction with - the \l Package type. - - For example, the code below selects a model which creates - delegates named \e list from a \l Package: - - \code - DelegateModel { - id: visualModel - delegate: Package { - Item { Package.name: "list" } - } - model: myModel - } - - ListView { - width: 200; height:200 - model: visualModel.parts.list - } - \endcode - - \sa Package -*/ - -QObject *QQmlDelegateModel::parts() -{ - Q_D(QQmlDelegateModel); - if (!d->m_parts) - d->m_parts = new QQmlDelegateModelParts(this); - return d->m_parts; -} - -void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->createdPackage(incubationTask->index[i], package); -} - -void QQmlDelegateModelPrivate::emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->initPackage(incubationTask->index[i], package); -} - -void QQmlDelegateModelPrivate::emitDestroyingPackage(QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->destroyingPackage(package); -} - -void QQDMIncubationTask::statusChanged(Status status) -{ - vdm->incubatorStatusChanged(this, status); -} - -void QQmlDelegateModelPrivate::releaseIncubator(QQDMIncubationTask *incubationTask) -{ - Q_Q(QQmlDelegateModel); - if (!incubationTask->isError()) - incubationTask->clear(); - m_finishedIncubating.append(incubationTask); - if (!m_incubatorCleanupScheduled) { - m_incubatorCleanupScheduled = true; - QCoreApplication::postEvent(q, new QEvent(QEvent::User)); - } -} - -void QQmlDelegateModelPrivate::removeCacheItem(QQmlDelegateModelItem *cacheItem) -{ - int cidx = m_cache.indexOf(cacheItem); - if (cidx >= 0) { - m_compositor.clearFlags(Compositor::Cache, cidx, 1, Compositor::CacheFlag); - m_cache.removeAt(cidx); - } - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); -} - -void QQmlDelegateModelPrivate::incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status) -{ - Q_Q(QQmlDelegateModel); - if (status != QQmlIncubator::Ready && status != QQmlIncubator::Error) - return; - - QQmlDelegateModelItem *cacheItem = incubationTask->incubating; - cacheItem->incubationTask = 0; - incubationTask->incubating = 0; - releaseIncubator(incubationTask); - - if (status == QQmlIncubator::Ready) { - if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) - emitCreatedPackage(incubationTask, package); - else - emitCreatedItem(incubationTask, cacheItem->object); - } else if (status == QQmlIncubator::Error) { - qmlInfo(q, m_delegate->errors()) << "Error creating delegate"; - } - - if (!cacheItem->isObjectReferenced()) { - if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) - emitDestroyingPackage(package); - else - emitDestroyingItem(cacheItem->object); - delete cacheItem->object; - cacheItem->object = 0; - cacheItem->scriptRef -= 1; - cacheItem->contextData->destroy(); - cacheItem->contextData = 0; - if (!cacheItem->isReferenced()) { - removeCacheItem(cacheItem); - delete cacheItem; - } - } -} - -void QQDMIncubationTask::setInitialState(QObject *o) -{ - vdm->setInitialState(this, o); -} - -void QQmlDelegateModelPrivate::setInitialState(QQDMIncubationTask *incubationTask, QObject *o) -{ - QQmlDelegateModelItem *cacheItem = incubationTask->incubating; - cacheItem->object = o; - - if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) - emitInitPackage(incubationTask, package); - else - emitInitItem(incubationTask, cacheItem->object); -} - -QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, bool asynchronous) -{ - Q_Q(QQmlDelegateModel); - if (!m_delegate || index < 0 || index >= m_compositor.count(group)) { - qWarning() << "DelegateModel::item: index out range" << index << m_compositor.count(group); - return 0; - } else if (!m_context->isValid()) { - return 0; - } - - Compositor::iterator it = m_compositor.find(group, index); - - QQmlDelegateModelItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0; - - if (!cacheItem) { - cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), it.modelIndex()); - if (!cacheItem) - return 0; - - cacheItem->groups = it->flags; - - m_cache.insert(it.cacheIndex, cacheItem); - m_compositor.setFlags(it, 1, Compositor::CacheFlag); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - } - - // Bump the reference counts temporarily so neither the content data or the delegate object - // are deleted if incubatorStatusChanged() is called synchronously. - cacheItem->scriptRef += 1; - cacheItem->referenceObject(); - - if (cacheItem->incubationTask) { - if (!asynchronous && cacheItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) { - // previously requested async - now needed immediately - cacheItem->incubationTask->forceCompletion(); - } - } else if (!cacheItem->object) { - QQmlContext *creationContext = m_delegate->creationContext(); - - cacheItem->scriptRef += 1; - - cacheItem->incubationTask = new QQDMIncubationTask(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested); - cacheItem->incubationTask->incubating = cacheItem; - cacheItem->incubationTask->clear(); - - for (int i = 1; i < m_groupCount; ++i) - cacheItem->incubationTask->index[i] = it.index[i]; - - QQmlContextData *ctxt = new QQmlContextData; - ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context)); - ctxt->contextObject = cacheItem; - cacheItem->contextData = ctxt; - - if (m_adaptorModel.hasProxyObject()) { - if (QQmlAdaptorModelProxyInterface *proxy - = qobject_cast(cacheItem)) { - ctxt = new QQmlContextData; - ctxt->setParent(cacheItem->contextData, true); - ctxt->contextObject = proxy->proxiedObject(); - } - } - - cacheItem->incubateObject( - m_delegate, - m_context->engine(), - ctxt, - QQmlContextData::get(m_context)); - } - - if (index == m_compositor.count(group) - 1 && m_adaptorModel.canFetchMore()) - QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); - - // Remove the temporary reference count. - cacheItem->scriptRef -= 1; - if (cacheItem->object) - return cacheItem->object; - - cacheItem->releaseObject(); - if (!cacheItem->isReferenced()) { - removeCacheItem(cacheItem); - delete cacheItem; - } - - return 0; -} - -/* - If asynchronous is true or the component is being loaded asynchronously due - to an ancestor being loaded asynchronously, item() may return 0. In this - case itemCreated() will be emitted when the item is available. The item - at this stage does not have any references, so item() must be called again - to ensure a reference is held. Any call to item() which returns a valid item - must be matched by a call to release() in order to destroy the item. -*/ -QObject *QQmlDelegateModel::object(int index, bool asynchronous) -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "DelegateModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup); - return 0; - } - - QObject *object = d->object(d->m_compositorGroup, index, asynchronous); - if (!object) - return 0; - - return object; -} - -QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) -{ - Compositor::iterator it = m_compositor.find(group, index); - if (QQmlAdaptorModel *model = it.list()) { - QString role = name; - int dot = name.indexOf(QLatin1Char('.')); - if (dot > 0) - role = name.left(dot); - QVariant value = model->value(it.modelIndex(), role); - while (dot > 0) { - QObject *obj = qvariant_cast(value); - if (!obj) - return QString(); - int from = dot+1; - dot = name.indexOf(QLatin1Char('.'), from); - value = obj->property(name.mid(from, dot-from).toUtf8()); - } - return value.toString(); - } - return QString(); -} - -QString QQmlDelegateModel::stringValue(int index, const QString &name) -{ - Q_D(QQmlDelegateModel); - return d->stringValue(d->m_compositorGroup, index, name); -} - -int QQmlDelegateModel::indexOf(QObject *item, QObject *) const -{ - Q_D(const QQmlDelegateModel); - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(item)) - return cacheItem->groupIndex(d->m_compositorGroup); - return -1; -} - -void QQmlDelegateModel::setWatchedRoles(QList roles) -{ - Q_D(QQmlDelegateModel); - d->m_adaptorModel.replaceWatchedRoles(d->m_watchedRoles, roles); - d->m_watchedRoles = roles; -} - -void QQmlDelegateModelPrivate::addGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector inserts; - m_compositor.setFlags(from, count, group, groupFlags, &inserts); - itemsInserted(inserts); - emitChanges(); -} - -void QQmlDelegateModelPrivate::removeGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector removes; - m_compositor.clearFlags(from, count, group, groupFlags, &removes); - itemsRemoved(removes); - emitChanges(); -} - -void QQmlDelegateModelPrivate::setGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector removes; - QVector inserts; - - m_compositor.setFlags(from, count, group, groupFlags, &inserts); - itemsInserted(inserts); - const int removeFlags = ~groupFlags & Compositor::GroupMask; - - from = m_compositor.find(from.group, from.index[from.group]); - m_compositor.clearFlags(from, count, group, removeFlags, &removes); - itemsRemoved(removes); - emitChanges(); -} - -bool QQmlDelegateModel::event(QEvent *e) -{ - Q_D(QQmlDelegateModel); - if (e->type() == QEvent::UpdateRequest) { - d->m_adaptorModel.fetchMore(); - } else if (e->type() == QEvent::User) { - d->m_incubatorCleanupScheduled = false; - qDeleteAll(d->m_finishedIncubating); - d->m_finishedIncubating.clear(); - } - return QQmlInstanceModel::event(e); -} - -void QQmlDelegateModelPrivate::itemsChanged(const QVector &changes) -{ - if (!m_delegate) - return; - - QVarLengthArray, Compositor::MaximumGroupCount> translatedChanges(m_groupCount); - - foreach (const Compositor::Change &change, changes) { - for (int i = 1; i < m_groupCount; ++i) { - if (change.inGroup(i)) { - translatedChanges[i].append(QQmlChangeSet::Change(change.index[i], change.count)); - } - } - } - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.change(translatedChanges.at(i)); -} - -void QQmlDelegateModel::_q_itemsChanged(int index, int count, const QVector &roles) -{ - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - if (d->m_adaptorModel.notify(d->m_cache, index, count, roles)) { - QVector changes; - d->m_compositor.listItemsChanged(&d->m_adaptorModel, index, count, &changes); - d->itemsChanged(changes); - d->emitChanges(); - } -} - -static void incrementIndexes(QQmlDelegateModelItem *cacheItem, int count, const int *deltas) -{ - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < count; ++i) - incubationTask->index[i] += deltas[i]; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < count; ++i) - attached->m_currentIndex[i] += deltas[i]; - } -} - -void QQmlDelegateModelPrivate::itemsInserted( - const QVector &inserts, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, - QHash > *movedItems) -{ - int cacheIndex = 0; - - int inserted[Compositor::MaximumGroupCount]; - for (int i = 1; i < m_groupCount; ++i) - inserted[i] = 0; - - foreach (const Compositor::Insert &insert, inserts) { - for (; cacheIndex < insert.cacheIndex; ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); - - for (int i = 1; i < m_groupCount; ++i) { - if (insert.inGroup(i)) { - (*translatedInserts)[i].append( - QQmlChangeSet::Insert(insert.index[i], insert.count, insert.moveId)); - inserted[i] += insert.count; - } - } - - if (!insert.inCache()) - continue; - - if (movedItems && insert.isMove()) { - QList items = movedItems->take(insert.moveId); - Q_ASSERT(items.count() == insert.count); - m_cache = m_cache.mid(0, insert.cacheIndex) + items + m_cache.mid(insert.cacheIndex); - } - if (insert.inGroup()) { - for (int offset = 0; cacheIndex < insert.cacheIndex + insert.count; ++cacheIndex, ++offset) { - QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); - cacheItem->groups |= insert.flags & Compositor::GroupMask; - - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) - incubationTask->index[i] = cacheItem->groups & (1 << i) - ? insert.index[i] + offset - : insert.index[i]; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) - attached->m_currentIndex[i] = cacheItem->groups & (1 << i) - ? insert.index[i] + offset - : insert.index[i]; - } - } - } else { - cacheIndex = insert.cacheIndex + insert.count; - } - } - for (; cacheIndex < m_cache.count(); ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); -} - -void QQmlDelegateModelPrivate::itemsInserted(const QVector &inserts) -{ - QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); - itemsInserted(inserts, &translatedInserts); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert(translatedInserts.at(i)); -} - -void QQmlDelegateModel::_q_itemsInserted(int index, int count) -{ - - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - d->m_count += count; - - for (int i = 0, c = d->m_cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = d->m_cache.at(i); - if (item->modelIndex() >= index) - item->setModelIndex(item->modelIndex() + count); - } - - QVector inserts; - d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts); - d->itemsInserted(inserts); - d->emitChanges(); -} - -void QQmlDelegateModelPrivate::itemsRemoved( - const QVector &removes, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, - QHash > *movedItems) -{ - int cacheIndex = 0; - int removedCache = 0; - - int removed[Compositor::MaximumGroupCount]; - for (int i = 1; i < m_groupCount; ++i) - removed[i] = 0; - - foreach (const Compositor::Remove &remove, removes) { - for (; cacheIndex < remove.cacheIndex; ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); - - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) { - (*translatedRemoves)[i].append( - QQmlChangeSet::Remove(remove.index[i], remove.count, remove.moveId)); - removed[i] -= remove.count; - } - } - - if (!remove.inCache()) - continue; - - if (movedItems && remove.isMove()) { - movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex, remove.count)); - QList::iterator begin = m_cache.begin() + remove.cacheIndex; - QList::iterator end = begin + remove.count; - m_cache.erase(begin, end); - } else { - for (; cacheIndex < remove.cacheIndex + remove.count - removedCache; ++cacheIndex) { - QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); - if (remove.inGroup(Compositor::Persisted) && cacheItem->objectRef == 0 && cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickPackage *package = qmlobject_cast(object)) - emitDestroyingPackage(package); - else - emitDestroyingItem(object); - cacheItem->scriptRef -= 1; - } - if (!cacheItem->isReferenced()) { - m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); - m_cache.removeAt(cacheIndex); - delete cacheItem; - --cacheIndex; - ++removedCache; - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - } else if (remove.groups() == cacheItem->groups) { - cacheItem->groups = 0; - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) - incubationTask->index[i] = -1; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) - attached->m_currentIndex[i] = -1; - } - } else { - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) - incubationTask->index[i] = remove.index[i]; - } - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) - attached->m_currentIndex[i] = remove.index[i]; - } - } - cacheItem->groups &= ~remove.flags; - } - } - } - } - - for (; cacheIndex < m_cache.count(); ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); -} - -void QQmlDelegateModelPrivate::itemsRemoved(const QVector &removes) -{ - QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); - itemsRemoved(removes, &translatedRemoves); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove(translatedRemoves.at(i)); -} - -void QQmlDelegateModel::_q_itemsRemoved(int index, int count) -{ - Q_D(QQmlDelegateModel); - if (count <= 0|| !d->m_complete) - return; - - d->m_count -= count; - - for (int i = 0, c = d->m_cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = d->m_cache.at(i); - if (item->modelIndex() >= index + count) - item->setModelIndex(item->modelIndex() - count); - else if (item->modelIndex() >= index) - item->setModelIndex(-1); - } - - QVector removes; - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, index, count, &removes); - d->itemsRemoved(removes); - - d->emitChanges(); -} - -void QQmlDelegateModelPrivate::itemsMoved( - const QVector &removes, const QVector &inserts) -{ - QHash > movedItems; - - QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); - itemsRemoved(removes, &translatedRemoves, &movedItems); - - QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); - itemsInserted(inserts, &translatedInserts, &movedItems); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - Q_ASSERT(movedItems.isEmpty()); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.move( - translatedRemoves.at(i), - translatedInserts.at(i)); - } -} - -void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count) -{ - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - const int minimum = qMin(from, to); - const int maximum = qMax(from, to) + count; - const int difference = from > to ? count : -count; - - for (int i = 0, c = d->m_cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = d->m_cache.at(i); - if (item->modelIndex() >= from && item->modelIndex() < from + count) - item->setModelIndex(item->modelIndex() - from + to); - else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) - item->setModelIndex(item->modelIndex() + difference); - } - - QVector removes; - QVector inserts; - d->m_compositor.listItemsMoved(&d->m_adaptorModel, from, to, count, &removes, &inserts); - d->itemsMoved(removes, inserts); - d->emitChanges(); -} - -template v8::Local -QQmlDelegateModelPrivate::buildChangeList(const QVector &changes) -{ - v8::Local indexes = v8::Array::New(changes.count()); - v8::Local indexKey = v8::String::New("index"); - v8::Local countKey = v8::String::New("count"); - v8::Local moveIdKey = v8::String::New("moveId"); - - for (int i = 0; i < changes.count(); ++i) { - v8::Local object = v8::Object::New(); - object->Set(indexKey, v8::Integer::New(changes.at(i).index)); - object->Set(countKey, v8::Integer::New(changes.at(i).count)); - object->Set(moveIdKey, changes.at(i).moveId != -1 ? v8::Integer::New(changes.at(i).count) : v8::Undefined()); - indexes->Set(i, object); - } - return indexes; -} - -void QQmlDelegateModelPrivate::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) -{ - Q_Q(QQmlDelegateModel); - emit q->modelUpdated(changeSet, reset); - if (changeSet.difference() != 0) - emit q->countChanged(); -} - -void QQmlDelegateModelPrivate::emitChanges() -{ - if (m_transaction || !m_complete || !m_context->isValid()) - return; - - m_transaction = true; - QV8Engine *engine = QQmlEnginePrivate::getV8Engine(m_context->engine()); - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitChanges(engine); - m_transaction = false; - - const bool reset = m_reset; - m_reset = false; - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); - - foreach (QQmlDelegateModelItem *cacheItem, m_cache) { - if (cacheItem->attached) - cacheItem->attached->emitChanges(); - } -} - -void QQmlDelegateModel::_q_modelReset() -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate) - return; - - int oldCount = d->m_count; - d->m_adaptorModel.rootIndex = QModelIndex(); - - if (d->m_complete) { - d->m_count = d->m_adaptorModel.count(); - - for (int i = 0, c = d->m_cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = d->m_cache.at(i); - if (item->modelIndex() != -1) - item->setModelIndex(-1); - } - - QVector removes; - QVector inserts; - if (oldCount) - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); - if (d->m_count) - d->m_compositor.listItemsInserted(&d->m_adaptorModel, 0, d->m_count, &inserts); - d->itemsMoved(removes, inserts); - d->m_reset = true; - - if (d->m_adaptorModel.canFetchMore()) - d->m_adaptorModel.fetchMore(); - - d->emitChanges(); - } - emit rootIndexChanged(); -} - -void QQmlDelegateModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (parent == d->m_adaptorModel.rootIndex) - _q_itemsInserted(begin, end - begin + 1); -} - -void QQmlDelegateModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (!d->m_adaptorModel.rootIndex.isValid()) - return; - const QModelIndex index = d->m_adaptorModel.rootIndex; - if (index.parent() == parent && index.row() >= begin && index.row() <= end) { - const int oldCount = d->m_count; - d->m_count = 0; - d->m_adaptorModel.invalidateModel(this); - - if (d->m_complete && oldCount > 0) { - QVector removes; - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); - d->itemsRemoved(removes); - d->emitChanges(); - } - } -} - -void QQmlDelegateModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (parent == d->m_adaptorModel.rootIndex) - _q_itemsRemoved(begin, end - begin + 1); -} - -void QQmlDelegateModel::_q_rowsMoved( - const QModelIndex &sourceParent, int sourceStart, int sourceEnd, - const QModelIndex &destinationParent, int destinationRow) -{ - Q_D(QQmlDelegateModel); - const int count = sourceEnd - sourceStart + 1; - if (destinationParent == d->m_adaptorModel.rootIndex && sourceParent == d->m_adaptorModel.rootIndex) { - _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow - count, count); - } else if (sourceParent == d->m_adaptorModel.rootIndex) { - _q_itemsRemoved(sourceStart, count); - } else if (destinationParent == d->m_adaptorModel.rootIndex) { - _q_itemsInserted(destinationRow, count); - } -} - -void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector &roles) -{ - Q_D(QQmlDelegateModel); - if (begin.parent() == d->m_adaptorModel.rootIndex) - _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, roles); -} - -void QQmlDelegateModel::_q_layoutChanged() -{ - Q_D(QQmlDelegateModel); - _q_itemsChanged(0, d->m_count, QVector()); -} - -QQmlDelegateModelAttached *QQmlDelegateModel::qmlAttachedProperties(QObject *obj) -{ - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(obj)) { - if (cacheItem->object == obj) { // Don't create attached item for child objects. - cacheItem->attached = new QQmlDelegateModelAttached(cacheItem, obj); - return cacheItem->attached; - } - } - return new QQmlDelegateModelAttached(obj); -} - -bool QQmlDelegateModelPrivate::insert( - Compositor::insert_iterator &before, const v8::Local &object, int groups) -{ - if (!m_context->isValid()) - return false; - - QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), -1); - if (!cacheItem) - return false; - - v8::Local propertyNames = object->GetPropertyNames(); - for (uint i = 0; i < propertyNames->Length(); ++i) { - v8::Local propertyName = propertyNames->Get(i)->ToString(); - cacheItem->setValue( - m_cacheMetaType->v8Engine->toString(propertyName), - m_cacheMetaType->v8Engine->toVariant(object->Get(propertyName), QVariant::Invalid)); - } - - cacheItem->groups = groups | Compositor::UnresolvedFlag | Compositor::CacheFlag; - - // Must be before the new object is inserted into the cache or its indexes will be adjusted too. - itemsInserted(QVector() << Compositor::Insert(before, 1, cacheItem->groups & ~Compositor::CacheFlag)); - - before = m_compositor.insert(before, 0, 0, 1, cacheItem->groups); - m_cache.insert(before.cacheIndex, cacheItem); - - return true; -} - -//============================================================================ - -QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType( - QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames) - : model(model) - , groupCount(groupNames.count() + 1) - , v8Engine(engine) - , metaObject(0) - , groupNames(groupNames) -{ -} - -QQmlDelegateModelItemMetaType::~QQmlDelegateModelItemMetaType() -{ - if (metaObject) - metaObject->release(); - qPersistentDispose(constructor); -} - -void QQmlDelegateModelItemMetaType::initializeMetaObject() -{ - QMetaObjectBuilder builder; - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - builder.setClassName(QQmlDelegateModelAttached::staticMetaObject.className()); - builder.setSuperClass(&QQmlDelegateModelAttached::staticMetaObject); - - int notifierId = 0; - for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - QString propertyName = QStringLiteral("in") + groupNames.at(i); - propertyName.replace(2, 1, propertyName.at(2).toUpper()); - builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); - QMetaPropertyBuilder propertyBuilder = builder.addProperty( - propertyName.toUtf8(), "bool", notifierId); - propertyBuilder.setWritable(true); - } - for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); - builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); - QMetaPropertyBuilder propertyBuilder = builder.addProperty( - propertyName.toUtf8(), "int", notifierId); - propertyBuilder.setWritable(true); - } - - metaObject = new QQmlDelegateModelAttachedMetaObject(this, builder.toMetaObject()); -} - -void QQmlDelegateModelItemMetaType::initializeConstructor() -{ - v8::HandleScope handleScope; - v8::Context::Scope contextScope(v8Engine->context()); - - QQmlDelegateModelEngineData *data = engineData(v8Engine); - - constructor = qPersistentNew(v8::ObjectTemplate::New()); - - constructor->SetHasExternalResource(true); - constructor->SetAccessor(data->model(), get_model); - constructor->SetAccessor(data->groups(), get_groups, set_groups); - constructor->SetAccessor(data->isUnresolved(), get_member, 0, v8::Int32::New(30)); - constructor->SetAccessor(data->inItems(), get_member, set_member, v8::Int32::New(1)); - constructor->SetAccessor(data->inPersistedItems(), get_member, set_member, v8::Int32::New(2)); - constructor->SetAccessor(data->itemsIndex(), get_index, 0, v8::Int32::New(1)); - constructor->SetAccessor(data->persistedItemsIndex(), get_index, 0, v8::Int32::New(2)); - - for (int i = 2; i < groupNames.count(); ++i) { - QString propertyName = QStringLiteral("in") + groupNames.at(i); - propertyName.replace(2, 1, propertyName.at(2).toUpper()); - constructor->SetAccessor( - v8Engine->toString(propertyName), get_member, set_member, v8::Int32::New(i + 1)); - } - for (int i = 2; i < groupNames.count(); ++i) { - const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); - constructor->SetAccessor( - v8Engine->toString(propertyName), get_index, 0, v8::Int32::New(i + 1)); - } -} - -int QQmlDelegateModelItemMetaType::parseGroups(const QStringList &groups) const -{ - int groupFlags = 0; - foreach (const QString &groupName, groups) { - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } - return groupFlags; -} - -int QQmlDelegateModelItemMetaType::parseGroups(const v8::Local &groups) const -{ - int groupFlags = 0; - if (groups->IsString()) { - const QString groupName = v8Engine->toString(groups); - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } else if (groups->IsArray()) { - v8::Local array = v8::Local::Cast(groups); - for (uint i = 0; i < array->Length(); ++i) { - const QString groupName = v8Engine->toString(array->Get(i)); - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } - } - return groupFlags; -} - -v8::Handle QQmlDelegateModelItemMetaType::get_model( - v8::Local, const v8::AccessorInfo &info) -{ - QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); - if (!cacheItem->metaType->model) - return v8::Undefined(); - - return cacheItem->get(); -} - -v8::Handle QQmlDelegateModelItemMetaType::get_groups( - v8::Local, const v8::AccessorInfo &info) -{ - QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); - - QStringList groups; - for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { - if (cacheItem->groups & (1 << i)) - groups.append(cacheItem->metaType->groupNames.at(i - 1)); - } - - return cacheItem->engine->fromVariant(groups); -} - -void QQmlDelegateModelItemMetaType::set_groups( - v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object"); - - if (!cacheItem->metaType->model) - return; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model); - - const int groupFlags = model->m_cacheMetaType->parseGroups(value); - const int cacheIndex = model->m_cache.indexOf(cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - model->setGroups(it, 1, Compositor::Cache, groupFlags); -} - -v8::Handle QQmlDelegateModelItemMetaType::get_member( - v8::Local, const v8::AccessorInfo &info) -{ - QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); - - return v8::Boolean::New(cacheItem->groups & (1 << info.Data()->Int32Value())); -} - -void QQmlDelegateModelItemMetaType::set_member( - v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object"); - - if (!cacheItem->metaType->model) - return; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model); - - Compositor::Group group = Compositor::Group(info.Data()->Int32Value()); - const bool member = value->BooleanValue(); - const int groupFlag = (1 << group); - if (member == ((cacheItem->groups & groupFlag) != 0)) - return; - - const int cacheIndex = model->m_cache.indexOf(cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - if (member) - model->addGroups(it, 1, Compositor::Cache, groupFlag); - else - model->removeGroups(it, 1, Compositor::Cache, groupFlag); -} - -v8::Handle QQmlDelegateModelItemMetaType::get_index( - v8::Local, const v8::AccessorInfo &info) -{ - QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); - - return v8::Integer::New(cacheItem->groupIndex(Compositor::Group(info.Data()->Int32Value()))); -} - - -//--------------------------------------------------------------------------- - -QQmlDelegateModelItem::QQmlDelegateModelItem( - QQmlDelegateModelItemMetaType *metaType, int modelIndex) - : QV8ObjectResource(metaType->v8Engine) - , metaType(metaType) - , contextData(0) - , object(0) - , attached(0) - , incubationTask(0) - , objectRef(0) - , scriptRef(0) - , groups(0) - , index(modelIndex) -{ - metaType->addref(); -} - -QQmlDelegateModelItem::~QQmlDelegateModelItem() -{ - Q_ASSERT(scriptRef == 0); - Q_ASSERT(objectRef == 0); - Q_ASSERT(!object); - - if (incubationTask && metaType->model) - QQmlDelegateModelPrivate::get(metaType->model)->releaseIncubator(incubationTask); - - metaType->release(); - -} - -void QQmlDelegateModelItem::Dispose() -{ - --scriptRef; - if (isReferenced()) - return; - - if (metaType->model) { - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); - model->removeCacheItem(this); - } - delete this; -} - -/* - This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData - arguments instead of QQmlContext which means we don't have to construct the rather weighty - wrapper class for every delegate item. -*/ -void QQmlDelegateModelItem::incubateObject( - QQmlComponent *component, - QQmlEngine *engine, - QQmlContextData *context, - QQmlContextData *forContext) -{ - QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubationTask); - QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); - QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component); - - incubatorPriv->compiledData = componentPriv->cc; - incubatorPriv->compiledData->addref(); - incubatorPriv->vme.init( - context, - componentPriv->cc, - componentPriv->start, - componentPriv->creationContext); - - enginePriv->incubate(*incubationTask, forContext); -} - -void QQmlDelegateModelItem::destroyObject() -{ - Q_ASSERT(object); - Q_ASSERT(contextData); - - QObjectPrivate *p = QObjectPrivate::get(object); - Q_ASSERT(p->declarativeData); - QQmlData *data = static_cast(p->declarativeData); - if (data->ownContext && data->context) - data->context->clearContext(); - object->deleteLater(); - - if (attached) { - attached->m_cacheItem = 0; - attached = 0; - } - - contextData->destroy(); - contextData = 0; - object = 0; -} - -QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object) -{ - QObjectPrivate *p = QObjectPrivate::get(object); - QQmlContextData *context = p->declarativeData - ? static_cast(p->declarativeData)->context - : 0; - for (context = context ? context->parent : 0; context; context = context->parent) { - if (QQmlDelegateModelItem *cacheItem = qobject_cast( - context->contextObject)) { - return cacheItem; - } - } - return 0; -} - -int QQmlDelegateModelItem::groupIndex(Compositor::Group group) -{ - if (QQmlDelegateModelPrivate * const model = metaType->model - ? QQmlDelegateModelPrivate::get(metaType->model) - : 0) { - return model->m_compositor.find(Compositor::Cache, model->m_cache.indexOf(this)).index[group]; - } - return -1; -} - -//--------------------------------------------------------------------------- - -QQmlDelegateModelAttachedMetaObject::QQmlDelegateModelAttachedMetaObject( - QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject) - : metaType(metaType) - , metaObject(metaObject) - , memberPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount()) - , indexPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount() + metaType->groupNames.count()) -{ - // Don't reference count the meta-type here as that would create a circular reference. - // Instead we rely the fact that the meta-type's reference count can't reach 0 without first - // destroying all delegates with attached objects. - *static_cast(this) = *metaObject; -} - -QQmlDelegateModelAttachedMetaObject::~QQmlDelegateModelAttachedMetaObject() -{ - ::free(metaObject); -} - -void QQmlDelegateModelAttachedMetaObject::objectDestroyed(QObject *) -{ - release(); -} - -int QQmlDelegateModelAttachedMetaObject::metaCall(QObject *object, QMetaObject::Call call, int _id, void **arguments) -{ - QQmlDelegateModelAttached *attached = static_cast(object); - if (call == QMetaObject::ReadProperty) { - if (_id >= indexPropertyOffset) { - Compositor::Group group = Compositor::Group(_id - indexPropertyOffset + 1); - *static_cast(arguments[0]) = attached->m_currentIndex[group]; - return -1; - } else if (_id >= memberPropertyOffset) { - Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); - *static_cast(arguments[0]) = attached->m_cacheItem->groups & (1 << group); - return -1; - } - } else if (call == QMetaObject::WriteProperty) { - if (_id >= memberPropertyOffset) { - if (!metaType->model) - return -1; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); - Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); - const int groupFlag = 1 << group; - const bool member = attached->m_cacheItem->groups & groupFlag; - if (member && !*static_cast(arguments[0])) { - Compositor::iterator it = model->m_compositor.find( - group, attached->m_currentIndex[group]); - model->removeGroups(it, 1, group, groupFlag); - } else if (!member && *static_cast(arguments[0])) { - for (int i = 1; i < metaType->groupCount; ++i) { - if (attached->m_cacheItem->groups & (1 << i)) { - Compositor::iterator it = model->m_compositor.find( - Compositor::Group(i), attached->m_currentIndex[i]); - model->addGroups(it, 1, Compositor::Group(i), groupFlag); - break; - } - } - } - return -1; - } - } - return attached->qt_metacall(call, _id, arguments); -} - -QQmlDelegateModelAttached::QQmlDelegateModelAttached(QObject *parent) - : m_cacheItem(0) - , m_previousGroups(0) -{ - QQml_setParent_noEvent(this, parent); -} - -QQmlDelegateModelAttached::QQmlDelegateModelAttached( - QQmlDelegateModelItem *cacheItem, QObject *parent) - : m_cacheItem(cacheItem) - , m_previousGroups(cacheItem->groups) -{ - QQml_setParent_noEvent(this, parent); - if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) { - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) - m_currentIndex[i] = m_previousIndex[i] = incubationTask->index[i]; - } else { - QQmlDelegateModelPrivate * const model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); - Compositor::iterator it = model->m_compositor.find( - Compositor::Cache, model->m_cache.indexOf(m_cacheItem)); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) - m_currentIndex[i] = m_previousIndex[i] = it.index[i]; - } - - if (!cacheItem->metaType->metaObject) - cacheItem->metaType->initializeMetaObject(); - - QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject; - cacheItem->metaType->metaObject->addref(); -} - -/*! - \qmlattachedproperty int QtQml.Models2::DelegateModel::model - - This attached property holds the visual data model this delegate instance belongs to. - - It is attached to each instance of the delegate. -*/ - -QQmlDelegateModel *QQmlDelegateModelAttached::model() const -{ - return m_cacheItem ? m_cacheItem->metaType->model : 0; -} - -/*! - \qmlattachedproperty stringlist QtQml.Models2::DelegateModel::groups - - This attached property holds the name of DelegateModelGroups the item belongs to. - - It is attached to each instance of the delegate. -*/ - -QStringList QQmlDelegateModelAttached::groups() const -{ - QStringList groups; - - if (!m_cacheItem) - return groups; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { - if (m_cacheItem->groups & (1 << i)) - groups.append(m_cacheItem->metaType->groupNames.at(i - 1)); - } - return groups; -} - -void QQmlDelegateModelAttached::setGroups(const QStringList &groups) -{ - if (!m_cacheItem) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); - - const int groupFlags = model->m_cacheMetaType->parseGroups(groups); - const int cacheIndex = model->m_cache.indexOf(m_cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - model->setGroups(it, 1, Compositor::Cache, groupFlags); -} - -/*! - \qmlattachedproperty bool QtQml.Models2::DelegateModel::isUnresolved - - This attached property holds whether the visual item is bound to a data model index. - Returns true if the item is not bound to the model, and false if it is. - - An unresolved item can be bound to the data model using the DelegateModelGroup::resolve() - function. - - It is attached to each instance of the delegate. -*/ - -bool QQmlDelegateModelAttached::isUnresolved() const -{ - if (!m_cacheItem) - return false; - - return m_cacheItem->groups & Compositor::UnresolvedFlag; -} - -/*! - \qmlattachedproperty int QtQml.Models2::DelegateModel::inItems - - This attached property holds whether the item belongs to the default \l items DelegateModelGroup. - - Changing this property will add or remove the item from the items group. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models2::DelegateModel::itemsIndex - - This attached property holds the index of the item in the default \l items DelegateModelGroup. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models2::DelegateModel::inPersistedItems - - This attached property holds whether the item belongs to the \l persistedItems DelegateModelGroup. - - Changing this property will add or remove the item from the items group. Change with caution - as removing an item from the persistedItems group will destroy the current instance if it is - not referenced by a model. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models2::DelegateModel::persistedItemsIndex - - This attached property holds the index of the item in the \l persistedItems DelegateModelGroup. - - It is attached to each instance of the delegate. -*/ - -void QQmlDelegateModelAttached::emitChanges() -{ - const int groupChanges = m_previousGroups ^ m_cacheItem->groups; - m_previousGroups = m_cacheItem->groups; - - int indexChanges = 0; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { - if (m_previousIndex[i] != m_currentIndex[i]) { - m_previousIndex[i] = m_currentIndex[i]; - indexChanges |= (1 << i); - } - } - - int notifierId = 0; - const QMetaObject *meta = metaObject(); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { - if (groupChanges & (1 << i)) - QMetaObject::activate(this, meta, notifierId, 0); - } - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { - if (indexChanges & (1 << i)) - QMetaObject::activate(this, meta, notifierId, 0); - } - - if (groupChanges) - emit groupsChanged(); -} - -//============================================================================ - -void QQmlDelegateModelGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::Group g) -{ - Q_ASSERT(!model); - model = m; - group = g; -} - -bool QQmlDelegateModelGroupPrivate::isChangedConnected() -{ - Q_Q(QQmlDelegateModelGroup); - IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QQmlV8Handle &,const QQmlV8Handle &)); -} - -void QQmlDelegateModelGroupPrivate::emitChanges(QV8Engine *engine) -{ - Q_Q(QQmlDelegateModelGroup); - if (isChangedConnected() && !changeSet.isEmpty()) { - v8::HandleScope handleScope; - v8::Context::Scope contextScope(engine->context()); - v8::Local removed = engineData(engine)->array(engine, changeSet.removes()); - v8::Local inserted = engineData(engine)->array(engine, changeSet.inserts()); - emit q->changed(QQmlV8Handle::fromHandle(removed), QQmlV8Handle::fromHandle(inserted)); - } - if (changeSet.difference() != 0) - emit q->countChanged(); -} - -void QQmlDelegateModelGroupPrivate::emitModelUpdated(bool reset) -{ - for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->emitModelUpdated(changeSet, reset); - changeSet.clear(); -} - -void QQmlDelegateModelGroupPrivate::createdPackage(int index, QQuickPackage *package) -{ - for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->createdPackage(index, package); -} - -void QQmlDelegateModelGroupPrivate::initPackage(int index, QQuickPackage *package) -{ - for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->initPackage(index, package); -} - -void QQmlDelegateModelGroupPrivate::destroyingPackage(QQuickPackage *package) -{ - for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->destroyingPackage(package); -} - -/*! - \qmltype DelegateModelGroup - \instantiates QQmlDelegateModelGroup - \inqmlmodule QtQuick 2 - \ingroup qtquick-models - \brief Encapsulates a filtered set of visual data items - - The DelegateModelGroup type provides a means to address the model data of a DelegateModel's - delegate items, as well as sort and filter these delegate items. - - The initial set of instantiable delegate items in a DelegateModel is represented - by its \l {QtQml.Models2::DelegateModel::items}{items} group, which normally directly reflects - the contents of the model assigned to DelegateModel::model. This set can be changed to - the contents of any other member of DelegateModel::groups by assigning the \l name of that - DelegateModelGroup to the DelegateModel::filterOnGroup property. - - The data of an item in a DelegateModelGroup can be accessed using the get() function, which returns - information about group membership and indexes as well as model data. In combination - with the move() function this can be used to implement view sorting, with remove() to filter - items out of a view, or with setGroups() and \l Package delegates to categorize items into - different views. - - Data from models can be supplemented by inserting data directly into a DelegateModelGroup - with the insert() function. This can be used to introduce mock items into a view, or - placeholder items that are later \l {resolve()}{resolved} to real model data when it becomes - available. - - Delegate items can also be be instantiated directly from a DelegateModelGroup using the - create() function, making it possible to use DelegateModel without an accompanying view - type or to cherry-pick specific items that should be instantiated irregardless of whether - they're currently within a view's visible area. - - \sa {QML Dynamic View Ordering Tutorial} -*/ -/*! - \qmltype DelegateModelGroup - \instantiates QQmlDelegateModelGroup - \inqmlmodule QtQml.Models 2 - \brief Encapsulates a filtered set of visual data items - - The DelegateModelGroup type provides a means to address the model data of a DelegateModel's - delegate items, as well as sort and filter these delegate items. - - This element is also available as DelegateModelGroup in the QtQuick module. For full details, - see the \l DelegateModelGroup documentation. - - \sa {QtQuick::DelegateModelGroup} -*/ - - -QQmlDelegateModelGroup::QQmlDelegateModelGroup(QObject *parent) - : QObject(*new QQmlDelegateModelGroupPrivate, parent) -{ -} - -QQmlDelegateModelGroup::QQmlDelegateModelGroup( - const QString &name, QQmlDelegateModel *model, int index, QObject *parent) - : QObject(*new QQmlDelegateModelGroupPrivate, parent) -{ - Q_D(QQmlDelegateModelGroup); - d->name = name; - d->setModel(model, Compositor::Group(index)); -} - -QQmlDelegateModelGroup::~QQmlDelegateModelGroup() -{ -} - -/*! - \qmlproperty string QtQml.Models2::DelegateModelGroup::name - - This property holds the name of the group. - - Each group in a model must have a unique name starting with a lower case letter. -*/ - -QString QQmlDelegateModelGroup::name() const -{ - Q_D(const QQmlDelegateModelGroup); - return d->name; -} - -void QQmlDelegateModelGroup::setName(const QString &name) -{ - Q_D(QQmlDelegateModelGroup); - if (d->model) - return; - if (d->name != name) { - d->name = name; - emit nameChanged(); - } -} - -/*! - \qmlproperty int QtQml.Models2::DelegateModelGroup::count - - This property holds the number of items in the group. -*/ - -int QQmlDelegateModelGroup::count() const -{ - Q_D(const QQmlDelegateModelGroup); - if (!d->model) - return 0; - return QQmlDelegateModelPrivate::get(d->model)->m_compositor.count(d->group); -} - -/*! - \qmlproperty bool QtQml.Models2::DelegateModelGroup::includeByDefault - - This property holds whether new items are assigned to this group by default. -*/ - -bool QQmlDelegateModelGroup::defaultInclude() const -{ - Q_D(const QQmlDelegateModelGroup); - return d->defaultInclude; -} - -void QQmlDelegateModelGroup::setDefaultInclude(bool include) -{ - Q_D(QQmlDelegateModelGroup); - if (d->defaultInclude != include) { - d->defaultInclude = include; - - if (d->model) { - if (include) - QQmlDelegateModelPrivate::get(d->model)->m_compositor.setDefaultGroup(d->group); - else - QQmlDelegateModelPrivate::get(d->model)->m_compositor.clearDefaultGroup(d->group); - } - emit defaultIncludeChanged(); - } -} - -/*! - \qmlmethod object QtQml.Models2::DelegateModelGroup::get(int index) - - Returns a javascript object describing the item at \a index in the group. - - The returned object contains the same information that is available to a delegate from the - DelegateModel attached as well as the model for that item. It has the properties: - - \list - \li \b model The model data of the item. This is the same as the model context property in - a delegate - \li \b groups A list the of names of groups the item is a member of. This property can be - written to change the item's membership. - \li \b inItems Whether the item belongs to the \l {QtQml.Models2::DelegateModel::items}{items} group. - Writing to this property will add or remove the item from the group. - \li \b itemsIndex The index of the item within the \l {QtQml.Models2::DelegateModel::items}{items} group. - \li \b {in} Whether the item belongs to the dynamic group \e groupName. Writing to - this property will add or remove the item from the group. - \li \b {Index} The index of the item within the dynamic group \e groupName. - \li \b isUnresolved Whether the item is bound to an index in the model assigned to - DelegateModel::model. Returns true if the item is not bound to the model, and false if it is. - \endlist -*/ - -QQmlV8Handle QQmlDelegateModelGroup::get(int index) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return QQmlV8Handle::fromHandle(v8::Undefined());; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (!model->m_context->isValid()) { - return QQmlV8Handle::fromHandle(v8::Undefined()); - } else if (index < 0 || index >= model->m_compositor.count(d->group)) { - qmlInfo(this) << tr("get: index out of range"); - return QQmlV8Handle::fromHandle(v8::Undefined()); - } - - Compositor::iterator it = model->m_compositor.find(d->group, index); - QQmlDelegateModelItem *cacheItem = it->inCache() - ? model->m_cache.at(it.cacheIndex) - : 0; - - if (!cacheItem) { - cacheItem = model->m_adaptorModel.createItem( - model->m_cacheMetaType, model->m_context->engine(), it.modelIndex()); - if (!cacheItem) - return QQmlV8Handle::fromHandle(v8::Undefined()); - cacheItem->groups = it->flags; - - model->m_cache.insert(it.cacheIndex, cacheItem); - model->m_compositor.setFlags(it, 1, Compositor::CacheFlag); - } - - if (model->m_cacheMetaType->constructor.IsEmpty()) - model->m_cacheMetaType->initializeConstructor(); - v8::Local handle = model->m_cacheMetaType->constructor->NewInstance(); - handle->SetExternalResource(cacheItem); - ++cacheItem->scriptRef; - - return QQmlV8Handle::fromHandle(handle); -} - -bool QQmlDelegateModelGroupPrivate::parseIndex( - const v8::Local &value, int *index, Compositor::Group *group) const -{ - if (value->IsInt32()) { - *index = value->Int32Value(); - return true; - } else if (value->IsObject()) { - v8::Local object = value->ToObject(); - QQmlDelegateModelItem * const cacheItem = v8_resource_cast(object); - if (QQmlDelegateModelPrivate *model = cacheItem && cacheItem->metaType->model - ? QQmlDelegateModelPrivate::get(cacheItem->metaType->model) - : 0) { - *index = model->m_cache.indexOf(cacheItem); - *group = Compositor::Cache; - return true; - } - } - return false; -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::insert(int index, jsdict data, array groups = undefined) - \qmlmethod QtQml.Models2::DelegateModelGroup::insert(jsdict data, var groups = undefined) - - Creates a new entry at \a index in a DelegateModel with the values from \a data that - correspond to roles in the model assigned to DelegateModel::model. - - If no index is supplied the data is appended to the model. - - The optional \a groups parameter identifies the groups the new entry should belong to, - if unspecified this is equal to the group insert was called on. - - Data inserted into a DelegateModel can later be merged with an existing entry in - DelegateModel::model using the \l resolve() function. This can be used to create placeholder - items that are later replaced by actual data. -*/ - -void QQmlDelegateModelGroup::insert(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - int index = model->m_compositor.count(d->group); - Compositor::Group group = d->group; - - if (args->Length() == 0) - return; - - int i = 0; - v8::Local v = (*args)[i]; - if (d->parseIndex(v, &index, &group)) { - if (index < 0 || index > model->m_compositor.count(group)) { - qmlInfo(this) << tr("insert: index out of range"); - return; - } - if (++i == args->Length()) - return; - v = (*args)[i]; - } - - Compositor::insert_iterator before = index < model->m_compositor.count(group) - ? model->m_compositor.findInsertPosition(group, index) - : model->m_compositor.end(); - - int groups = 1 << d->group; - if (++i < args->Length()) - groups |= model->m_cacheMetaType->parseGroups((*args)[i]); - - if (v->IsArray()) { - return; - } else if (v->IsObject()) { - model->insert(before, v->ToObject(), groups); - model->emitChanges(); - } -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::create(int index) - \qmlmethod QtQml.Models2::DelegateModelGroup::create(int index, jsdict data, array groups = undefined) - \qmlmethod QtQml.Models2::DelegateModelGroup::create(jsdict data, array groups = undefined) - - Returns a reference to the instantiated item at \a index in the group. - - If a \a data object is provided it will be \l {insert}{inserted} at \a index and an item - referencing this new entry will be returned. The optional \a groups parameter identifies - the groups the new entry should belong to, if unspecified this is equal to the group create() - was called on. - - All items returned by create are added to the - \l {QtQml.Models2::DelegateModel::persistedItems}{persistedItems} group. Items in this - group remain instantiated when not referenced by any view. -*/ - -void QQmlDelegateModelGroup::create(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - - if (args->Length() == 0) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - int index = model->m_compositor.count(d->group); - Compositor::Group group = d->group; - - int i = 0; - v8::Local v = (*args)[i]; - if (d->parseIndex(v, &index, &group)) - ++i; - - if (i < args->Length() && index >= 0 && index <= model->m_compositor.count(group)) { - v = (*args)[i]; - if (v->IsObject()) { - int groups = 1 << d->group; - if (++i < args->Length()) - groups |= model->m_cacheMetaType->parseGroups((*args)[i]); - - Compositor::insert_iterator before = index < model->m_compositor.count(group) - ? model->m_compositor.findInsertPosition(group, index) - : model->m_compositor.end(); - - index = before.index[d->group]; - group = d->group; - - if (!model->insert(before, v->ToObject(), groups)) { - return; - } - } - } - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("create: index out of range"); - return; - } - - QObject *object = model->object(group, index, false); - if (object) { - QVector inserts; - Compositor::iterator it = model->m_compositor.find(group, index); - model->m_compositor.setFlags(it, 1, d->group, Compositor::PersistedFlag, &inserts); - model->itemsInserted(inserts); - model->m_cache.at(it.cacheIndex)->releaseObject(); - } - - args->returnValue(args->engine()->newQObject(object)); - model->emitChanges(); -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::resolve(int from, int to) - - Binds an unresolved item at \a from to an item in DelegateModel::model at index \a to. - - Unresolved items are entries whose data has been \l {insert()}{inserted} into a DelegateModelGroup - instead of being derived from a DelegateModel::model index. Resolving an item will replace - the item at the target index with the unresolved item. A resolved an item will reflect the data - of the source model at its bound index and will move when that index moves like any other item. - - If a new item is replaced in the DelegateModelGroup onChanged() handler its insertion and - replacement will be communicated to views as an atomic operation, creating the appearance - that the model contents have not changed, or if the unresolved and model item are not adjacent - that the previously unresolved item has simply moved. - -*/ -void QQmlDelegateModelGroup::resolve(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - if (args->Length() < 2) - return; - - int from = -1; - int to = -1; - Compositor::Group fromGroup = d->group; - Compositor::Group toGroup = d->group; - - v8::Local v = (*args)[0]; - if (d->parseIndex(v, &from, &fromGroup)) { - if (from < 0 || from >= model->m_compositor.count(fromGroup)) { - qmlInfo(this) << tr("resolve: from index out of range"); - return; - } - } else { - qmlInfo(this) << tr("resolve: from index invalid"); - return; - } - - v = (*args)[1]; - if (d->parseIndex(v, &to, &toGroup)) { - if (to < 0 || to >= model->m_compositor.count(toGroup)) { - qmlInfo(this) << tr("resolve: to index out of range"); - return; - } - } else { - qmlInfo(this) << tr("resolve: to index invalid"); - return; - } - - Compositor::iterator fromIt = model->m_compositor.find(fromGroup, from); - Compositor::iterator toIt = model->m_compositor.find(toGroup, to); - - if (!fromIt->isUnresolved()) { - qmlInfo(this) << tr("resolve: from is not an unresolved item"); - return; - } - if (!toIt->list) { - qmlInfo(this) << tr("resolve: to is not a model item"); - return; - } - - const int unresolvedFlags = fromIt->flags; - const int resolvedFlags = toIt->flags; - const int resolvedIndex = toIt.modelIndex(); - void * const resolvedList = toIt->list; - - QQmlDelegateModelItem *cacheItem = model->m_cache.at(fromIt.cacheIndex); - cacheItem->groups &= ~Compositor::UnresolvedFlag; - - if (toIt.cacheIndex > fromIt.cacheIndex) - toIt.decrementIndexes(1, unresolvedFlags); - if (!toIt->inGroup(fromGroup) || toIt.index[fromGroup] > from) - from += 1; - - model->itemsMoved( - QVector() << Compositor::Remove(fromIt, 1, unresolvedFlags, 0), - QVector() << Compositor::Insert(toIt, 1, unresolvedFlags, 0)); - model->itemsInserted( - QVector() << Compositor::Insert(toIt, 1, (resolvedFlags & ~unresolvedFlags) | Compositor::CacheFlag)); - toIt.incrementIndexes(1, resolvedFlags | unresolvedFlags); - model->itemsRemoved(QVector() << Compositor::Remove(toIt, 1, resolvedFlags)); - - model->m_compositor.setFlags(toGroup, to, 1, unresolvedFlags & ~Compositor::UnresolvedFlag); - model->m_compositor.clearFlags(fromGroup, from, 1, unresolvedFlags); - - if (resolvedFlags & Compositor::CacheFlag) - model->m_compositor.insert(Compositor::Cache, toIt.cacheIndex, resolvedList, resolvedIndex, 1, Compositor::CacheFlag); - - Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); - - if (!cacheItem->isReferenced()) { - Q_ASSERT(toIt.cacheIndex == model->m_cache.indexOf(cacheItem)); - model->m_cache.removeAt(toIt.cacheIndex); - model->m_compositor.clearFlags(Compositor::Cache, toIt.cacheIndex, 1, Compositor::CacheFlag); - delete cacheItem; - Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); - } else { - cacheItem->resolveIndex(model->m_adaptorModel, resolvedIndex); - if (cacheItem->attached) - cacheItem->attached->emitUnresolvedChanged(); - } - - model->emitChanges(); -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::remove(int index, int count) - - Removes \a count items starting at \a index from the group. -*/ - -void QQmlDelegateModelGroup::remove(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - Compositor::Group group = d->group; - int index = -1; - int count = 1; - - if (args->Length() == 0) - return; - - int i = 0; - v8::Local v = (*args)[i]; - if (!d->parseIndex(v, &index, &group)) { - qmlInfo(this) << tr("remove: invalid index"); - return; - } - - if (++i < args->Length()) { - v = (*args)[i]; - if (v->IsInt32()) - count = v->Int32Value(); - } - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("remove: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlInfo(this) << tr("remove: invalid count"); - } else { - model->removeGroups(it, count, d->group, 1 << d->group); - } - } -} - -bool QQmlDelegateModelGroupPrivate::parseGroupArgs( - QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const -{ - if (!model || !QQmlDelegateModelPrivate::get(model)->m_cacheMetaType) - return false; - - if (args->Length() < 2) - return false; - - int i = 0; - v8::Local v = (*args)[i]; - if (!parseIndex(v, index, group)) - return false; - - v = (*args)[++i]; - if (v->IsInt32()) { - *count = v->Int32Value(); - - if (++i == args->Length()) - return false; - v = (*args)[i]; - } - - *groups = QQmlDelegateModelPrivate::get(model)->m_cacheMetaType->parseGroups(v); - - return true; -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::addGroups(int index, int count, stringlist groups) - - Adds \a count items starting at \a index to \a groups. -*/ - -void QQmlDelegateModelGroup::addGroups(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("addGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlInfo(this) << tr("addGroups: invalid count"); - } else { - model->addGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::removeGroups(int index, int count, stringlist groups) - - Removes \a count items starting at \a index from \a groups. -*/ - -void QQmlDelegateModelGroup::removeGroups(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("removeGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlInfo(this) << tr("removeGroups: invalid count"); - } else { - model->removeGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::setGroups(int index, int count, stringlist groups) - - Sets the \a groups \a count items starting at \a index belong to. -*/ - -void QQmlDelegateModelGroup::setGroups(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("setGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlInfo(this) << tr("setGroups: invalid count"); - } else { - model->setGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::setGroups(int index, int count, stringlist groups) - - Sets the \a groups \a count items starting at \a index belong to. -*/ - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::move(var from, var to, int count) - - Moves \a count at \a from in a group \a to a new position. -*/ - -void QQmlDelegateModelGroup::move(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - - if (args->Length() < 2) - return; - - Compositor::Group fromGroup = d->group; - Compositor::Group toGroup = d->group; - int from = -1; - int to = -1; - int count = 1; - - if (!d->parseIndex((*args)[0], &from, &fromGroup)) { - qmlInfo(this) << tr("move: invalid from index"); - return; - } - - if (!d->parseIndex((*args)[1], &to, &toGroup)) { - qmlInfo(this) << tr("move: invalid to index"); - return; - } - - if (args->Length() > 2) { - v8::Local v = (*args)[2]; - if (v->IsInt32()) - count = v->Int32Value(); - } - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - if (count < 0) { - qmlInfo(this) << tr("move: invalid count"); - } else if (from < 0 || from + count > model->m_compositor.count(fromGroup)) { - qmlInfo(this) << tr("move: from index out of range"); - } else if (!model->m_compositor.verifyMoveTo(fromGroup, from, toGroup, to, count, d->group)) { - qmlInfo(this) << tr("move: to index out of range"); - } else if (count > 0) { - QVector removes; - QVector inserts; - - model->m_compositor.move(fromGroup, from, toGroup, to, count, d->group, &removes, &inserts); - model->itemsMoved(removes, inserts); - model->emitChanges(); - } - -} - -/*! - \qmlsignal QtQml.Models2::DelegateModelGroup::onChanged(array removed, array inserted) - - This handler is called when items have been removed from or inserted into the group. - - Each object in the \a removed and \a inserted arrays has two values; the \e index of the first - item inserted or removed and a \e count of the number of consecutive items inserted or removed. - - Each index is adjusted for previous changes with all removed items preceding any inserted - items. -*/ - -//============================================================================ - -QQmlPartsModel::QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent) - : QQmlInstanceModel(*new QObjectPrivate, parent) - , m_model(model) - , m_part(part) - , m_compositorGroup(Compositor::Cache) - , m_inheritGroup(true) -{ - QQmlDelegateModelPrivate *d = QQmlDelegateModelPrivate::get(m_model); - if (d->m_cacheMetaType) { - QQmlDelegateModelGroupPrivate::get(d->m_groups[1])->emitters.insert(this); - m_compositorGroup = Compositor::Default; - } else { - d->m_pendingParts.insert(this); - } -} - -QQmlPartsModel::~QQmlPartsModel() -{ -} - -QString QQmlPartsModel::filterGroup() const -{ - if (m_inheritGroup) - return m_model->filterGroup(); - return m_filterGroup; -} - -void QQmlPartsModel::setFilterGroup(const QString &group) -{ - if (QQmlDelegateModelPrivate::get(m_model)->m_transaction) { - qmlInfo(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); - return; - } - - if (m_filterGroup != group || m_inheritGroup) { - m_filterGroup = group; - m_inheritGroup = false; - updateFilterGroup(); - - emit filterGroupChanged(); - } -} - -void QQmlPartsModel::resetFilterGroup() -{ - if (!m_inheritGroup) { - m_inheritGroup = true; - updateFilterGroup(); - emit filterGroupChanged(); - } -} - -void QQmlPartsModel::updateFilterGroup() -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - if (!model->m_cacheMetaType) - return; - - if (m_inheritGroup) { - if (m_filterGroup == model->m_filterGroup) - return; - m_filterGroup = model->m_filterGroup; - } - - QQmlListCompositor::Group previousGroup = m_compositorGroup; - m_compositorGroup = Compositor::Default; - QQmlDelegateModelGroupPrivate::get(model->m_groups[Compositor::Default])->emitters.insert(this); - for (int i = 1; i < model->m_groupCount; ++i) { - if (m_filterGroup == model->m_cacheMetaType->groupNames.at(i - 1)) { - m_compositorGroup = Compositor::Group(i); - break; - } - } - - QQmlDelegateModelGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this); - if (m_compositorGroup != previousGroup) { - QVector removes; - QVector inserts; - model->m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); - - QQmlChangeSet changeSet; - changeSet.move(removes, inserts); - if (!changeSet.isEmpty()) - emit modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit countChanged(); - } -} - -void QQmlPartsModel::updateFilterGroup( - Compositor::Group group, const QQmlChangeSet &changeSet) -{ - if (!m_inheritGroup) - return; - - m_compositorGroup = group; - QQmlDelegateModelGroupPrivate::get(QQmlDelegateModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this); - - if (!changeSet.isEmpty()) - emit modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit countChanged(); - - emit filterGroupChanged(); -} - -int QQmlPartsModel::count() const -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - return model->m_delegate - ? model->m_compositor.count(m_compositorGroup) - : 0; -} - -bool QQmlPartsModel::isValid() const -{ - return m_model->isValid(); -} - -QObject *QQmlPartsModel::object(int index, bool asynchronous) -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - - if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) { - qWarning() << "DelegateModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup); - return 0; - } - - QObject *object = model->object(m_compositorGroup, index, asynchronous); - - if (QQuickPackage *package = qmlobject_cast(object)) { - QObject *part = package->part(m_part); - if (!part) - return 0; - m_packaged.insertMulti(part, package); - return part; - } - - model->release(object); - if (!model->m_delegateValidated) { - if (object) - qmlInfo(model->m_delegate) << tr("Delegate component must be Package type."); - model->m_delegateValidated = true; - } - - return 0; -} - -QQmlInstanceModel::ReleaseFlags QQmlPartsModel::release(QObject *item) -{ - QQmlInstanceModel::ReleaseFlags flags = 0; - - QHash::iterator it = m_packaged.find(item); - if (it != m_packaged.end()) { - QQuickPackage *package = *it; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - flags = model->release(package); - m_packaged.erase(it); - if (!m_packaged.contains(item)) - flags &= ~Referenced; - if (flags & Destroyed) - QQmlDelegateModelPrivate::get(m_model)->emitDestroyingPackage(package); - } - return flags; -} - -QString QQmlPartsModel::stringValue(int index, const QString &role) -{ - return QQmlDelegateModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role); -} - -void QQmlPartsModel::setWatchedRoles(QList roles) -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - model->m_adaptorModel.replaceWatchedRoles(m_watchedRoles, roles); - m_watchedRoles = roles; -} - -int QQmlPartsModel::indexOf(QObject *item, QObject *) const -{ - QHash::const_iterator it = m_packaged.find(item); - if (it != m_packaged.end()) { - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(*it)) - return cacheItem->groupIndex(m_compositorGroup); - } - return -1; -} - -void QQmlPartsModel::createdPackage(int index, QQuickPackage *package) -{ - emit createdItem(index, package->part(m_part)); -} - -void QQmlPartsModel::initPackage(int index, QQuickPackage *package) -{ - emit initItem(index, package->part(m_part)); -} - -void QQmlPartsModel::destroyingPackage(QQuickPackage *package) -{ - QObject *item = package->part(m_part); - Q_ASSERT(!m_packaged.contains(item)); - emit destroyingItem(item); -} - -void QQmlPartsModel::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) -{ - emit modelUpdated(changeSet, reset); - if (changeSet.difference() != 0) - emit countChanged(); -} - -//============================================================================ - -v8::Handle get_change_index(v8::Local, const v8::AccessorInfo &info) -{ - return info.This()->GetInternalField(0); -} - -v8::Handle get_change_count(v8::Local, const v8::AccessorInfo &info) -{ - return info.This()->GetInternalField(1); -} - -v8::Handle get_change_moveId(v8::Local, const v8::AccessorInfo &info) -{ - return info.This()->GetInternalField(2); -} - -class QQmlDelegateModelGroupChangeArray : public QV8ObjectResource -{ - V8_RESOURCE_TYPE(ChangeSetArrayType) -public: - QQmlDelegateModelGroupChangeArray(QV8Engine *engine) - : QV8ObjectResource(engine) - { - } - - virtual quint32 count() const = 0; - virtual const QQmlChangeSet::Change &at(int index) const = 0; - - static v8::Handle get_change(quint32 index, const v8::AccessorInfo &info) - { - QQmlDelegateModelGroupChangeArray *array = v8_resource_cast(info.This()); - V8ASSERT_TYPE(array, "Not a valid change array"); - - if (index >= array->count()) - return v8::Undefined(); - - const QQmlChangeSet::Change &change = array->at(index); - - v8::Local object = engineData(array->engine)->constructorChange->NewInstance(); - object->SetInternalField(0, v8::Int32::New(change.index)); - object->SetInternalField(1, v8::Int32::New(change.count)); - if (change.isMove()) - object->SetInternalField(2, v8::Int32::New(change.moveId)); - - return object; - } - - static v8::Handle get_length(v8::Local, const v8::AccessorInfo &info) - { - QQmlDelegateModelGroupChangeArray *array = v8_resource_cast(info.This()); - V8ASSERT_TYPE(array, "Not a valid change array"); - - return v8::Integer::New(array->count()); - } - - static v8::Local constructor() - { - v8::Local changeArray = v8::FunctionTemplate::New(); - changeArray->InstanceTemplate()->SetHasExternalResource(true); - changeArray->InstanceTemplate()->SetIndexedPropertyHandler(get_change); - changeArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), get_length); - return changeArray->GetFunction(); - } -}; - -class QQmlDelegateModelGroupRemoveArray : public QQmlDelegateModelGroupChangeArray -{ -public: - QQmlDelegateModelGroupRemoveArray(QV8Engine *engine, const QVector &changes) - : QQmlDelegateModelGroupChangeArray(engine) - , changes(changes) - { - } - - quint32 count() const { return changes.count(); } - const QQmlChangeSet::Change &at(int index) const { return changes.at(index); } - -private: - QVector changes; -}; - -class QQmlDelegateModelGroupInsertArray : public QQmlDelegateModelGroupChangeArray -{ -public: - QQmlDelegateModelGroupInsertArray(QV8Engine *engine, const QVector &changes) - : QQmlDelegateModelGroupChangeArray(engine) - , changes(changes) - { - } - - quint32 count() const { return changes.count(); } - const QQmlChangeSet::Change &at(int index) const { return changes.at(index); } - -private: - QVector changes; -}; - -QQmlDelegateModelEngineData::QQmlDelegateModelEngineData(QV8Engine *) -{ - strings = qPersistentNew(v8::Array::New(StringCount)); - strings->Set(Model, v8::String::New("model")); - strings->Set(Groups, v8::String::New("groups")); - strings->Set(IsUnresolved, v8::String::New("isUnresolved")); - strings->Set(ItemsIndex, v8::String::New("itemsIndex")); - strings->Set(PersistedItemsIndex, v8::String::New("persistedItemsIndex")); - strings->Set(InItems, v8::String::New("inItems")); - strings->Set(InPersistedItems, v8::String::New("inPersistedItems")); - - v8::Local change = v8::FunctionTemplate::New(); - change->InstanceTemplate()->SetAccessor(v8::String::New("index"), get_change_index); - change->InstanceTemplate()->SetAccessor(v8::String::New("count"), get_change_count); - change->InstanceTemplate()->SetAccessor(v8::String::New("moveId"), get_change_moveId); - change->InstanceTemplate()->SetInternalFieldCount(3); - constructorChange = qPersistentNew(change->GetFunction()); - constructorChangeArray = qPersistentNew(QQmlDelegateModelGroupChangeArray::constructor()); -} - -QQmlDelegateModelEngineData::~QQmlDelegateModelEngineData() -{ - qPersistentDispose(strings); - qPersistentDispose(constructorChange); - qPersistentDispose(constructorChangeArray); -} - -v8::Local QQmlDelegateModelEngineData::array( - QV8Engine *engine, const QVector &changes) -{ - v8::Local array = constructorChangeArray->NewInstance(); - array->SetExternalResource(new QQmlDelegateModelGroupRemoveArray(engine, changes)); - return array; -} - -v8::Local QQmlDelegateModelEngineData::array( - QV8Engine *engine, const QVector &changes) -{ - v8::Local array = constructorChangeArray->NewInstance(); - array->SetExternalResource(new QQmlDelegateModelGroupInsertArray(engine, changes)); - return array; -} - -QT_END_NAMESPACE - diff --git a/src/qml/items/qqmldelegatemodel_p.h b/src/qml/items/qqmldelegatemodel_p.h deleted file mode 100644 index 5702c59787..0000000000 --- a/src/qml/items/qqmldelegatemodel_p.h +++ /dev/null @@ -1,234 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLDATAMODEL_P_H -#define QQMLDATAMODEL_P_H - -#include -#include -#include - -#include -#include - -#include -#include - -Q_DECLARE_METATYPE(QModelIndex) - -QT_BEGIN_NAMESPACE - -class QQmlChangeSet; -class QQmlComponent; -class QQuickPackage; -class QQmlV8Function; -class QQmlDelegateModelGroup; -class QQmlDelegateModelAttached; -class QQmlDelegateModelPrivate; - - -class Q_QML_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public QQmlParserStatus -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlDelegateModel) - - Q_PROPERTY(QVariant model READ model WRITE setModel) - Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate) - Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) - Q_PROPERTY(QQmlDelegateModelGroup *items READ items CONSTANT) //TODO : worth renaming? - Q_PROPERTY(QQmlDelegateModelGroup *persistedItems READ persistedItems CONSTANT) - Q_PROPERTY(QQmlListProperty groups READ groups CONSTANT) - Q_PROPERTY(QObject *parts READ parts CONSTANT) - Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) - Q_CLASSINFO("DefaultProperty", "delegate") - Q_INTERFACES(QQmlParserStatus) -public: - QQmlDelegateModel(); - QQmlDelegateModel(QQmlContext *, QObject *parent=0); - virtual ~QQmlDelegateModel(); - - void classBegin(); - void componentComplete(); - - QVariant model() const; - void setModel(const QVariant &); - - QQmlComponent *delegate() const; - void setDelegate(QQmlComponent *); - - QVariant rootIndex() const; - void setRootIndex(const QVariant &root); - - Q_INVOKABLE QVariant modelIndex(int idx) const; - Q_INVOKABLE QVariant parentModelIndex() const; - - int count() const; - bool isValid() const { return delegate() != 0; } - QObject *object(int index, bool asynchronous=false); - ReleaseFlags release(QObject *object); - void cancel(int index); - virtual QString stringValue(int index, const QString &role); - virtual void setWatchedRoles(QList roles); - - int indexOf(QObject *object, QObject *objectContext) const; - - QString filterGroup() const; - void setFilterGroup(const QString &group); - void resetFilterGroup(); - - QQmlDelegateModelGroup *items(); - QQmlDelegateModelGroup *persistedItems(); - QQmlListProperty groups(); - QObject *parts(); - - bool event(QEvent *); - - static QQmlDelegateModelAttached *qmlAttachedProperties(QObject *obj); - -Q_SIGNALS: - void filterGroupChanged(); - void defaultGroupsChanged(); - void rootIndexChanged(); - -private Q_SLOTS: - void _q_itemsChanged(int index, int count, const QVector &roles); - void _q_itemsInserted(int index, int count); - void _q_itemsRemoved(int index, int count); - void _q_itemsMoved(int from, int to, int count); - void _q_modelReset(); - void _q_rowsInserted(const QModelIndex &,int,int); - void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end); - void _q_rowsRemoved(const QModelIndex &,int,int); - void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int); - void _q_dataChanged(const QModelIndex&,const QModelIndex&,const QVector &); - void _q_layoutChanged(); - -private: - Q_DISABLE_COPY(QQmlDelegateModel) -}; - -class QQmlDelegateModelGroupPrivate; -class Q_QML_PRIVATE_EXPORT QQmlDelegateModelGroup : public QObject -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(bool includeByDefault READ defaultInclude WRITE setDefaultInclude NOTIFY defaultIncludeChanged) -public: - QQmlDelegateModelGroup(QObject *parent = 0); - QQmlDelegateModelGroup(const QString &name, QQmlDelegateModel *model, int compositorType, QObject *parent = 0); - ~QQmlDelegateModelGroup(); - - QString name() const; - void setName(const QString &name); - - int count() const; - - bool defaultInclude() const; - void setDefaultInclude(bool include); - - Q_INVOKABLE QQmlV8Handle get(int index); - -public Q_SLOTS: - void insert(QQmlV8Function *); - void create(QQmlV8Function *); - void resolve(QQmlV8Function *); - void remove(QQmlV8Function *); - void addGroups(QQmlV8Function *); - void removeGroups(QQmlV8Function *); - void setGroups(QQmlV8Function *); - void move(QQmlV8Function *); - -Q_SIGNALS: - void countChanged(); - void nameChanged(); - void defaultIncludeChanged(); - void changed(const QQmlV8Handle &removed, const QQmlV8Handle &inserted); -private: - Q_DECLARE_PRIVATE(QQmlDelegateModelGroup) -}; - -class QQmlDelegateModelItem; -class QQmlDelegateModelAttachedMetaObject; -class QQmlDelegateModelAttached : public QObject -{ - Q_OBJECT - Q_PROPERTY(QQmlDelegateModel *model READ model CONSTANT) - Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged) - Q_PROPERTY(bool isUnresolved READ isUnresolved NOTIFY unresolvedChanged) -public: - QQmlDelegateModelAttached(QObject *parent); - QQmlDelegateModelAttached(QQmlDelegateModelItem *cacheItem, QObject *parent); - ~QQmlDelegateModelAttached() {} - - void setCacheItem(QQmlDelegateModelItem *item); - - QQmlDelegateModel *model() const; - - QStringList groups() const; - void setGroups(const QStringList &groups); - - bool isUnresolved() const; - - void emitChanges(); - - void emitUnresolvedChanged() { emit unresolvedChanged(); } - -Q_SIGNALS: - void groupsChanged(); - void unresolvedChanged(); - -public: - QQmlDelegateModelItem *m_cacheItem; - int m_previousGroups; - int m_currentIndex[QQmlListCompositor::MaximumGroupCount]; - int m_previousIndex[QQmlListCompositor::MaximumGroupCount]; - - friend class QQmlDelegateModelAttachedMetaObject; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlDelegateModel) -QML_DECLARE_TYPEINFO(QQmlDelegateModel, QML_HAS_ATTACHED_PROPERTIES) -QML_DECLARE_TYPE(QQmlDelegateModelGroup) - -#endif // QQMLDATAMODEL_P_H diff --git a/src/qml/items/qqmldelegatemodel_p_p.h b/src/qml/items/qqmldelegatemodel_p_p.h deleted file mode 100644 index 68242f433d..0000000000 --- a/src/qml/items/qqmldelegatemodel_p_p.h +++ /dev/null @@ -1,411 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLDATAMODEL_P_P_H -#define QQMLDATAMODEL_P_P_H - -#include "qqmldelegatemodel_p.h" - - -#include -#include - -#include -#include - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -QT_BEGIN_NAMESPACE - -typedef QQmlListCompositor Compositor; - -class QQmlDelegateModelAttachedMetaObject; - -class QQmlDelegateModelItemMetaType : public QQmlRefCount -{ -public: - QQmlDelegateModelItemMetaType(QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames); - ~QQmlDelegateModelItemMetaType(); - - void initializeMetaObject(); - void initializeConstructor(); - - int parseGroups(const QStringList &groupNames) const; - int parseGroups(const v8::Local &groupNames) const; - - static void release_index(v8::Persistent object, void *parameter); - static void release_model(v8::Persistent object, void *parameter); - - static v8::Handle get_model(v8::Local, const v8::AccessorInfo &info); - static v8::Handle get_groups(v8::Local, const v8::AccessorInfo &info); - static void set_groups( - v8::Local, v8::Local value, const v8::AccessorInfo &info); - static v8::Handle get_member(v8::Local, const v8::AccessorInfo &info); - static void set_member( - v8::Local, v8::Local value, const v8::AccessorInfo &info); - static v8::Handle get_index(v8::Local, const v8::AccessorInfo &info); - - QQmlGuard model; - const int groupCount; - QV8Engine * const v8Engine; - QQmlDelegateModelAttachedMetaObject *metaObject; - const QStringList groupNames; - v8::Persistent constructor; -}; - -class QQmlAdaptorModel; -class QQDMIncubationTask; - -class QQmlDelegateModelItem : public QObject, public QV8ObjectResource -{ - Q_OBJECT - Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged) - Q_PROPERTY(QObject *model READ modelObject CONSTANT) - V8_RESOURCE_TYPE(VisualDataItemType) -public: - QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex); - ~QQmlDelegateModelItem(); - - void referenceObject() { ++objectRef; } - bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } - bool isObjectReferenced() const { return objectRef != 0 || (groups & Compositor::PersistedFlag); } - - bool isReferenced() const { - return scriptRef - || incubationTask - || ((groups & Compositor::UnresolvedFlag) && (groups & Compositor::GroupMask)); - } - - void Dispose(); - - QObject *modelObject() { return this; } - - void incubateObject( - QQmlComponent *component, - QQmlEngine *engine, - QQmlContextData *context, - QQmlContextData *forContext); - void destroyObject(); - - static QQmlDelegateModelItem *dataForObject(QObject *object); - - int groupIndex(Compositor::Group group); - - int modelIndex() const { return index; } - void setModelIndex(int idx) { index = idx; emit modelIndexChanged(); } - - virtual v8::Handle get() { return engine->newQObject(this); } - - virtual void setValue(const QString &role, const QVariant &value) { Q_UNUSED(role); Q_UNUSED(value); } - virtual bool resolveIndex(const QQmlAdaptorModel &, int) { return false; } - - QQmlDelegateModelItemMetaType * const metaType; - QQmlContextData *contextData; - QObject *object; - QQmlDelegateModelAttached *attached; - QQDMIncubationTask *incubationTask; - int objectRef; - int scriptRef; - int groups; - int index; - - -Q_SIGNALS: - void modelIndexChanged(); - -protected: - void objectDestroyed(QObject *); -}; - - -class QQmlDelegateModelPrivate; -class QQDMIncubationTask : public QQmlIncubator -{ -public: - QQDMIncubationTask(QQmlDelegateModelPrivate *l, IncubationMode mode) - : QQmlIncubator(mode) - , incubating(0) - , vdm(l) {} - - virtual void statusChanged(Status); - virtual void setInitialState(QObject *); - - QQmlDelegateModelItem *incubating; - QQmlDelegateModelPrivate *vdm; - int index[QQmlListCompositor::MaximumGroupCount]; -}; - - -class QQmlDelegateModelGroupEmitter -{ -public: - virtual ~QQmlDelegateModelGroupEmitter() {} - virtual void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) = 0; - virtual void createdPackage(int, QQuickPackage *) {} - virtual void initPackage(int, QQuickPackage *) {} - virtual void destroyingPackage(QQuickPackage *) {} - - QIntrusiveListNode emitterNode; -}; - -typedef QIntrusiveList QQmlDelegateModelGroupEmitterList; - -class QQmlDelegateModelGroupPrivate : public QObjectPrivate -{ -public: - Q_DECLARE_PUBLIC(QQmlDelegateModelGroup) - - QQmlDelegateModelGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {} - - static QQmlDelegateModelGroupPrivate *get(QQmlDelegateModelGroup *group) { - return static_cast(QObjectPrivate::get(group)); } - - void setModel(QQmlDelegateModel *model, Compositor::Group group); - bool isChangedConnected(); - void emitChanges(QV8Engine *engine); - void emitModelUpdated(bool reset); - - void createdPackage(int index, QQuickPackage *package); - void initPackage(int index, QQuickPackage *package); - void destroyingPackage(QQuickPackage *package); - - bool parseIndex(const v8::Local &value, int *index, Compositor::Group *group) const; - bool parseGroupArgs( - QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const; - - Compositor::Group group; - QQmlGuard model; - QQmlDelegateModelGroupEmitterList emitters; - QQmlChangeSet changeSet; - QString name; - bool defaultInclude; -}; - -class QQmlDelegateModelParts; - -class QQmlDelegateModelPrivate : public QObjectPrivate, public QQmlDelegateModelGroupEmitter -{ - Q_DECLARE_PUBLIC(QQmlDelegateModel) -public: - QQmlDelegateModelPrivate(QQmlContext *); - ~QQmlDelegateModelPrivate(); - - static QQmlDelegateModelPrivate *get(QQmlDelegateModel *m) { - return static_cast(QObjectPrivate::get(m)); - } - - void init(); - void connectModel(QQmlAdaptorModel *model); - - QObject *object(Compositor::Group group, int index, bool asynchronous); - QQmlDelegateModel::ReleaseFlags release(QObject *object); - QString stringValue(Compositor::Group group, int index, const QString &name); - void emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); - void emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); - void emitCreatedItem(QQDMIncubationTask *incubationTask, QObject *item) { - emit q_func()->createdItem(incubationTask->index[m_compositorGroup], item); } - void emitInitItem(QQDMIncubationTask *incubationTask, QObject *item) { - emit q_func()->initItem(incubationTask->index[m_compositorGroup], item); } - void emitDestroyingPackage(QQuickPackage *package); - void emitDestroyingItem(QObject *item) { emit q_func()->destroyingItem(item); } - void removeCacheItem(QQmlDelegateModelItem *cacheItem); - - void updateFilterGroup(); - - void addGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - void removeGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - void setGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - - void itemsInserted( - const QVector &inserts, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, - QHash > *movedItems = 0); - void itemsInserted(const QVector &inserts); - void itemsRemoved( - const QVector &removes, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, - QHash > *movedItems = 0); - void itemsRemoved(const QVector &removes); - void itemsMoved( - const QVector &removes, const QVector &inserts); - void itemsChanged(const QVector &changes); - template static v8::Local buildChangeList(const QVector &changes); - void emitChanges(); - void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset); - - bool insert(Compositor::insert_iterator &before, const v8::Local &object, int groups); - - static void group_append(QQmlListProperty *property, QQmlDelegateModelGroup *group); - static int group_count(QQmlListProperty *property); - static QQmlDelegateModelGroup *group_at(QQmlListProperty *property, int index); - - void releaseIncubator(QQDMIncubationTask *incubationTask); - void incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status); - void setInitialState(QQDMIncubationTask *incubationTask, QObject *o); - - QQmlAdaptorModel m_adaptorModel; - QQmlListCompositor m_compositor; - QQmlComponent *m_delegate; - QQmlDelegateModelItemMetaType *m_cacheMetaType; - QQmlContext *m_context; - QQmlDelegateModelParts *m_parts; - QQmlDelegateModelGroupEmitterList m_pendingParts; - - QList m_cache; - QList m_finishedIncubating; - QList m_watchedRoles; - - QString m_filterGroup; - - int m_count; - int m_groupCount; - - QQmlListCompositor::Group m_compositorGroup; - bool m_complete : 1; - bool m_delegateValidated : 1; - bool m_reset : 1; - bool m_transaction : 1; - bool m_incubatorCleanupScheduled : 1; - - union { - struct { - QQmlDelegateModelGroup *m_cacheItems; - QQmlDelegateModelGroup *m_items; - QQmlDelegateModelGroup *m_persistedItems; - }; - QQmlDelegateModelGroup *m_groups[Compositor::MaximumGroupCount]; - }; -}; - -class QQmlPartsModel : public QQmlInstanceModel, public QQmlDelegateModelGroupEmitter -{ - Q_OBJECT - Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) -public: - QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent = 0); - ~QQmlPartsModel(); - - QString filterGroup() const; - void setFilterGroup(const QString &group); - void resetFilterGroup(); - void updateFilterGroup(); - void updateFilterGroup(Compositor::Group group, const QQmlChangeSet &changeSet); - - int count() const; - bool isValid() const; - QObject *object(int index, bool asynchronous=false); - ReleaseFlags release(QObject *item); - QString stringValue(int index, const QString &role); - QList watchedRoles() const { return m_watchedRoles; } - void setWatchedRoles(QList roles); - - int indexOf(QObject *item, QObject *objectContext) const; - - void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset); - - void createdPackage(int index, QQuickPackage *package); - void initPackage(int index, QQuickPackage *package); - void destroyingPackage(QQuickPackage *package); - -Q_SIGNALS: - void filterGroupChanged(); - -private: - QQmlDelegateModel *m_model; - QHash m_packaged; - QString m_part; - QString m_filterGroup; - QList m_watchedRoles; - Compositor::Group m_compositorGroup; - bool m_inheritGroup; -}; - -class QMetaPropertyBuilder; - -class QQmlDelegateModelPartsMetaObject : public QQmlOpenMetaObject -{ -public: - QQmlDelegateModelPartsMetaObject(QObject *parent) - : QQmlOpenMetaObject(parent) {} - - virtual void propertyCreated(int, QMetaPropertyBuilder &); - virtual QVariant initialValue(int); -}; - -class QQmlDelegateModelParts : public QObject -{ -Q_OBJECT -public: - QQmlDelegateModelParts(QQmlDelegateModel *parent); - - QQmlDelegateModel *model; - QList models; -}; - -class QQmlDelegateModelAttachedMetaObject : public QAbstractDynamicMetaObject, public QQmlRefCount -{ -public: - QQmlDelegateModelAttachedMetaObject( - QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject); - ~QQmlDelegateModelAttachedMetaObject(); - - void objectDestroyed(QObject *); - int metaCall(QObject *, QMetaObject::Call, int _id, void **); - -private: - QQmlDelegateModelItemMetaType * const metaType; - QMetaObject * const metaObject; - const int memberPropertyOffset; - const int indexPropertyOffset; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/items/qqmlmodelsmodule.cpp b/src/qml/items/qqmlmodelsmodule.cpp deleted file mode 100644 index 4f6b0a5580..0000000000 --- a/src/qml/items/qqmlmodelsmodule.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Research In Motion. -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlmodelsmodule_p.h" -#include -#include -#include - -QT_BEGIN_NAMESPACE - -void QQmlModelsModule::defineModule() -{ - const char uri[] = "QtQml.Models"; - - qmlRegisterType(uri, 2, 1, "ListElement"); - qmlRegisterCustomType(uri, 2, 1, "ListModel", new QQmlListModelParser); - qmlRegisterType(uri, 2, 1, "DelegateModel"); - qmlRegisterType(uri, 2, 1, "DelegateModelGroup"); - qmlRegisterType(uri, 2, 1, "ObjectModel"); -} - -QT_END_NAMESPACE diff --git a/src/qml/items/qqmlmodelsmodule_p.h b/src/qml/items/qqmlmodelsmodule_p.h deleted file mode 100644 index 6e72dadf8b..0000000000 --- a/src/qml/items/qqmlmodelsmodule_p.h +++ /dev/null @@ -1,57 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Research In Motion. -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLMODELSMODULE_H -#define QQMLMODELSMODULE_H - -#include - -QT_BEGIN_NAMESPACE - -class Q_QML_PRIVATE_EXPORT QQmlModelsModule -{ -public: - static void defineModule(); -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/items/qqmlobjectmodel.cpp b/src/qml/items/qqmlobjectmodel.cpp deleted file mode 100644 index 7f7bf92fa3..0000000000 --- a/src/qml/items/qqmlobjectmodel.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlobjectmodel_p.h" - -#include -#include -#include - -#include -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -QHash QQmlObjectModelAttached::attachedProperties; - - -class QQmlObjectModelPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQmlObjectModel) -public: - class Item { - public: - Item(QObject *i) : item(i), ref(0) {} - - void addRef() { ++ref; } - bool deref() { return --ref == 0; } - - QObject *item; - int ref; - }; - - QQmlObjectModelPrivate() : QObjectPrivate() {} - - static void children_append(QQmlListProperty *prop, QObject *item) { - static_cast(prop->data)->children.append(Item(item)); - static_cast(prop->data)->itemAppended(); - static_cast(prop->data)->emitChildrenChanged(); - } - - static int children_count(QQmlListProperty *prop) { - return static_cast(prop->data)->children.count(); - } - - static QObject *children_at(QQmlListProperty *prop, int index) { - return static_cast(prop->data)->children.at(index).item; - } - - static void children_clear(QQmlListProperty *prop) { - static_cast(prop->data)->itemCleared(static_cast(prop->data)->children); - static_cast(prop->data)->children.clear(); - static_cast(prop->data)->emitChildrenChanged(); - } - - void itemAppended() { - Q_Q(QQmlObjectModel); - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.last().item); - attached->setIndex(children.count()-1); - QQmlChangeSet changeSet; - changeSet.insert(children.count() - 1, 1); - emit q->modelUpdated(changeSet, false); - emit q->countChanged(); - } - - void itemCleared(const QList &children) { - Q_Q(QQmlObjectModel); - foreach (const Item &child, children) - emit q->destroyingItem(child.item); - emit q->countChanged(); - } - - void emitChildrenChanged() { - Q_Q(QQmlObjectModel); - emit q->childrenChanged(); - } - - int indexOf(QObject *item) const { - for (int i = 0; i < children.count(); ++i) - if (children.at(i).item == item) - return i; - return -1; - } - - - QList children; -}; - - -/*! - \qmltype ObjectModel - \instantiates QQmlObjectModel - \inqmlmodule QtQml.Models 2 - \ingroup qtquick-models - \brief Defines a set of items to be used as a model - - A ObjectModel contains the visual items to be used in a view. - When a ObjectModel is used in a view, the view does not require - a delegate since the ObjectModel already contains the visual - delegate (items). - - An item can determine its index within the - model via the \l{ObjectModel::index}{index} attached property. - - The example below places three colored rectangles in a ListView. - \code - import QtQuick 2.0 - - Rectangle { - ObjectModel { - id: itemModel - Rectangle { height: 30; width: 80; color: "red" } - Rectangle { height: 30; width: 80; color: "green" } - Rectangle { height: 30; width: 80; color: "blue" } - } - - ListView { - anchors.fill: parent - model: itemModel - } - } - \endcode - - \image visualitemmodel.png - - \sa {quick/views/objectmodel}{ObjectModel example} -*/ -/*! - \qmltype VisualItemModel - \instantiates QQmlObjectModel - \inqmlmodule QtQuick 2 - \brief Defines a set of objects to be used as a model - - The VisualItemModel type encapsulates contains the objects to be used - as a model. - - This element is now primarily available as ObjectModel in the QtQml.Models module. - VisualItemModel continues to be provided, with the same implementation, in QtQuick for - compatibility reasons. - - For full details about the type, see the \l ObjectModel documentation. - - \sa {QtQml.Models2::ObjectModel} -*/ - -QQmlObjectModel::QQmlObjectModel(QObject *parent) - : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent) -{ -} - -/*! - \qmlattachedproperty int QtQml.Models2::ObjectModel::index - This attached property holds the index of this delegate's item within the model. - - It is attached to each instance of the delegate. -*/ - -QQmlListProperty QQmlObjectModel::children() -{ - Q_D(QQmlObjectModel); - return QQmlListProperty(this, - d, - d->children_append, - d->children_count, - d->children_at, - d->children_clear); -} - -/*! - \qmlproperty int QtQml.Models2::ObjectModel::count - - The number of items in the model. This property is readonly. -*/ -int QQmlObjectModel::count() const -{ - Q_D(const QQmlObjectModel); - return d->children.count(); -} - -bool QQmlObjectModel::isValid() const -{ - return true; -} - -QObject *QQmlObjectModel::object(int index, bool) -{ - Q_D(QQmlObjectModel); - QQmlObjectModelPrivate::Item &item = d->children[index]; - item.addRef(); - if (item.ref == 1) { - emit initItem(index, item.item); - emit createdItem(index, item.item); - } - return item.item; -} - -QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item) -{ - Q_D(QQmlObjectModel); - int idx = d->indexOf(item); - if (idx >= 0) { - if (!d->children[idx].deref()) - return QQmlInstanceModel::Referenced; - } - return 0; -} - -QString QQmlObjectModel::stringValue(int index, const QString &name) -{ - Q_D(QQmlObjectModel); - if (index < 0 || index >= d->children.count()) - return QString(); - return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString(); -} - -int QQmlObjectModel::indexOf(QObject *item, QObject *) const -{ - Q_D(const QQmlObjectModel); - return d->indexOf(item); -} - -QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj) -{ - return QQmlObjectModelAttached::properties(obj); -} - -QT_END_NAMESPACE - diff --git a/src/qml/items/qqmlobjectmodel_p.h b/src/qml/items/qqmlobjectmodel_p.h deleted file mode 100644 index 59a4a551a7..0000000000 --- a/src/qml/items/qqmlobjectmodel_p.h +++ /dev/null @@ -1,170 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLINSTANCEMODEL_P_H -#define QQMLINSTANCEMODEL_P_H - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QObject; -class QQmlChangeSet; - -class Q_QML_PRIVATE_EXPORT QQmlInstanceModel : public QObject -{ - Q_OBJECT - - Q_PROPERTY(int count READ count NOTIFY countChanged) - -public: - virtual ~QQmlInstanceModel() {} - - enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 }; - Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag) - - virtual int count() const = 0; - virtual bool isValid() const = 0; - virtual QObject *object(int index, bool asynchronous=false) = 0; - virtual ReleaseFlags release(QObject *object) = 0; - virtual void cancel(int) {} - virtual QString stringValue(int, const QString &) = 0; - virtual void setWatchedRoles(QList roles) = 0; - - virtual int indexOf(QObject *object, QObject *objectContext) const = 0; - -Q_SIGNALS: - void countChanged(); - void modelUpdated(const QQmlChangeSet &changeSet, bool reset); - void createdItem(int index, QObject *object); - void initItem(int index, QObject *object); - void destroyingItem(QObject *object); - -protected: - QQmlInstanceModel(QObjectPrivate &dd, QObject *parent = 0) - : QObject(dd, parent) {} - -private: - Q_DISABLE_COPY(QQmlInstanceModel) -}; - -class QQmlObjectModelAttached; -class QQmlObjectModelPrivate; -class Q_QML_PRIVATE_EXPORT QQmlObjectModel : public QQmlInstanceModel -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlObjectModel) - - Q_PROPERTY(QQmlListProperty children READ children NOTIFY childrenChanged DESIGNABLE false) - Q_CLASSINFO("DefaultProperty", "children") - -public: - QQmlObjectModel(QObject *parent=0); - virtual ~QQmlObjectModel() {} - - virtual int count() const; - virtual bool isValid() const; - virtual QObject *object(int index, bool asynchronous=false); - virtual ReleaseFlags release(QObject *object); - virtual QString stringValue(int index, const QString &role); - virtual void setWatchedRoles(QList) {} - - virtual int indexOf(QObject *object, QObject *objectContext) const; - - QQmlListProperty children(); - - static QQmlObjectModelAttached *qmlAttachedProperties(QObject *obj); - -Q_SIGNALS: - void childrenChanged(); - -private: - Q_DISABLE_COPY(QQmlObjectModel) -}; - -class QQmlObjectModelAttached : public QObject -{ - Q_OBJECT - -public: - QQmlObjectModelAttached(QObject *parent) - : QObject(parent), m_index(0) {} - ~QQmlObjectModelAttached() { - attachedProperties.remove(parent()); - } - - Q_PROPERTY(int index READ index NOTIFY indexChanged) - int index() const { return m_index; } - void setIndex(int idx) { - if (m_index != idx) { - m_index = idx; - emit indexChanged(); - } - } - - static QQmlObjectModelAttached *properties(QObject *obj) { - QQmlObjectModelAttached *rv = attachedProperties.value(obj); - if (!rv) { - rv = new QQmlObjectModelAttached(obj); - attachedProperties.insert(obj, rv); - } - return rv; - } - -Q_SIGNALS: - void indexChanged(); - -public: - int m_index; - - static QHash attachedProperties; -}; - - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlInstanceModel) -QML_DECLARE_TYPE(QQmlObjectModel) -QML_DECLARE_TYPEINFO(QQmlObjectModel, QML_HAS_ATTACHED_PROPERTIES) - -#endif // QQMLINSTANCEMODEL_P_H diff --git a/src/qml/items/qquickpackage.cpp b/src/qml/items/qquickpackage.cpp deleted file mode 100644 index e885524b27..0000000000 --- a/src/qml/items/qquickpackage.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickpackage_p.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -/*! - \qmltype Package - \instantiates QQuickPackage - \inqmlmodule QtQuick 2 - \ingroup qtquick-views - \brief Specifies a collection of named items - - The Package class is used in conjunction with - VisualDataModel to enable delegates with a shared context - to be provided to multiple views. - - Any item within a Package may be assigned a name via the - \l{Package::name}{Package.name} attached property. - - The example below creates a Package containing two named items; - \e list and \e grid. The third item in the package (the \l Rectangle) is parented to whichever - delegate it should appear in. This allows an item to move - between views. - - \snippet quick/views/package/Delegate.qml 0 - - These named items are used as the delegates by the two views who - reference the special \l{VisualDataModel::parts} property to select - a model which provides the chosen delegate. - - \snippet quick/views/package/view.qml 0 - - \sa {quick/views/package}{Package example}, {quick/demos/photoviewer}{Photo Viewer example}, QtQml -*/ - -/*! - \qmlattachedproperty string QtQuick2::Package::name - This attached property holds the name of an item within a Package. -*/ - - -class QQuickPackagePrivate : public QObjectPrivate -{ -public: - QQuickPackagePrivate() {} - - struct DataGuard : public QQmlGuard - { - DataGuard(QObject *obj, QList *l) : list(l) { (QQmlGuard&)*this = obj; } - QList *list; - void objectDestroyed(QObject *) { - // we assume priv will always be destroyed after objectDestroyed calls - list->removeOne(*this); - } - }; - - QList dataList; - static void data_append(QQmlListProperty *prop, QObject *o) { - QList *list = static_cast *>(prop->data); - list->append(DataGuard(o, list)); - } - static void data_clear(QQmlListProperty *prop) { - QList *list = static_cast *>(prop->data); - list->clear(); - } - static QObject *data_at(QQmlListProperty *prop, int index) { - QList *list = static_cast *>(prop->data); - return list->at(index); - } - static int data_count(QQmlListProperty *prop) { - QList *list = static_cast *>(prop->data); - return list->count(); - } -}; - -QHash QQuickPackageAttached::attached; - -QQuickPackageAttached::QQuickPackageAttached(QObject *parent) -: QObject(parent) -{ - attached.insert(parent, this); -} - -QQuickPackageAttached::~QQuickPackageAttached() -{ - attached.remove(parent()); -} - -QString QQuickPackageAttached::name() const -{ - return _name; -} - -void QQuickPackageAttached::setName(const QString &n) -{ - _name = n; -} - -QQuickPackage::QQuickPackage(QObject *parent) - : QObject(*(new QQuickPackagePrivate), parent) -{ -} - -QQuickPackage::~QQuickPackage() -{ -} - -QQmlListProperty QQuickPackage::data() -{ - Q_D(QQuickPackage); - return QQmlListProperty(this, &d->dataList, QQuickPackagePrivate::data_append, - QQuickPackagePrivate::data_count, - QQuickPackagePrivate::data_at, - QQuickPackagePrivate::data_clear); -} - -bool QQuickPackage::hasPart(const QString &name) -{ - Q_D(QQuickPackage); - for (int ii = 0; ii < d->dataList.count(); ++ii) { - QObject *obj = d->dataList.at(ii); - QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); - if (a && a->name() == name) - return true; - } - return false; -} - -QObject *QQuickPackage::part(const QString &name) -{ - Q_D(QQuickPackage); - if (name.isEmpty() && !d->dataList.isEmpty()) - return d->dataList.at(0); - - for (int ii = 0; ii < d->dataList.count(); ++ii) { - QObject *obj = d->dataList.at(ii); - QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); - if (a && a->name() == name) - return obj; - } - - if (name == QLatin1String("default") && !d->dataList.isEmpty()) - return d->dataList.at(0); - - return 0; -} - -QQuickPackageAttached *QQuickPackage::qmlAttachedProperties(QObject *o) -{ - return new QQuickPackageAttached(o); -} - - - -QT_END_NAMESPACE diff --git a/src/qml/items/qquickpackage_p.h b/src/qml/items/qquickpackage_p.h deleted file mode 100644 index 9427c886a8..0000000000 --- a/src/qml/items/qquickpackage_p.h +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKPACKAGE_H -#define QQUICKPACKAGE_H - -#include - -QT_BEGIN_NAMESPACE - -class QQuickPackagePrivate; -class QQuickPackageAttached; -class Q_AUTOTEST_EXPORT QQuickPackage : public QObject -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQuickPackage) - - Q_CLASSINFO("DefaultProperty", "data") - Q_PROPERTY(QQmlListProperty data READ data) - -public: - QQuickPackage(QObject *parent=0); - virtual ~QQuickPackage(); - - QQmlListProperty data(); - - QObject *part(const QString & = QString()); - bool hasPart(const QString &); - - static QQuickPackageAttached *qmlAttachedProperties(QObject *); -}; - -class QQuickPackageAttached : public QObject -{ -Q_OBJECT -Q_PROPERTY(QString name READ name WRITE setName) -public: - QQuickPackageAttached(QObject *parent); - virtual ~QQuickPackageAttached(); - - QString name() const; - void setName(const QString &n); - - static QHash attached; -private: - QString _name; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickPackage) -QML_DECLARE_TYPEINFO(QQuickPackage, QML_HAS_ATTACHED_PROPERTIES) - -#endif // QQUICKPACKAGE_H diff --git a/src/qml/qml.pro b/src/qml/qml.pro index 08aa369ac3..8dbe0c65d1 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -25,4 +25,4 @@ include(util/util.pri) include(qml/qml.pri) include(debugger/debugger.pri) include(animations/animations.pri) -include(items/items.pri) +include(types/types.pri) diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index d3abd84fc8..a660abe7d7 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -1,12 +1,9 @@ SOURCES += \ $$PWD/qqmlinstruction.cpp \ - $$PWD/qqmllistmodel.cpp \ - $$PWD/qqmllistmodelworkeragent.cpp \ $$PWD/qqmlopenmetaobject.cpp \ $$PWD/qqmlvmemetaobject.cpp \ $$PWD/qqmlengine.cpp \ $$PWD/qqmlexpression.cpp \ - $$PWD/qqmlbinding.cpp \ $$PWD/qqmlproperty.cpp \ $$PWD/qqmlcomponent.cpp \ $$PWD/qqmlincubator.cpp \ @@ -38,7 +35,6 @@ SOURCES += \ $$PWD/qqmltypenotavailable.cpp \ $$PWD/qqmltypenamecache.cpp \ $$PWD/qqmlscriptstring.cpp \ - $$PWD/qquickworkerscript.cpp \ $$PWD/qqmlnetworkaccessmanagerfactory.cpp \ $$PWD/qqmldirparser.cpp \ $$PWD/qqmlextensionplugin.cpp \ @@ -53,21 +49,15 @@ SOURCES += \ $$PWD/qqmlfile.cpp \ $$PWD/qqmlbundle.cpp \ $$PWD/qqmlmemoryprofiler.cpp \ - $$PWD/qqmlconnections.cpp \ - $$PWD/qqmltimer.cpp \ - $$PWD/qqmlbind.cpp \ - $$PWD/qqmlplatform.cpp + $$PWD/qqmlplatform.cpp \ + $$PWD/qqmlbinding.cpp HEADERS += \ $$PWD/qqmlglobal_p.h \ $$PWD/qqmlinstruction_p.h \ - $$PWD/qqmllistmodel_p.h\ - $$PWD/qqmllistmodel_p_p.h\ - $$PWD/qqmllistmodelworkeragent_p.h \ $$PWD/qqmlopenmetaobject_p.h \ $$PWD/qqmlvmemetaobject_p.h \ $$PWD/qqml.h \ - $$PWD/qqmlbinding_p.h \ $$PWD/qqmlproperty.h \ $$PWD/qqmlcomponent.h \ $$PWD/qqmlcomponent_p.h \ @@ -112,7 +102,6 @@ HEADERS += \ $$PWD/qqmltypenotavailable_p.h \ $$PWD/qqmltypenamecache_p.h \ $$PWD/qqmlscriptstring.h \ - $$PWD/qquickworkerscript_p.h \ $$PWD/qqmlguard_p.h \ $$PWD/qqmlnetworkaccessmanagerfactory.h \ $$PWD/qqmldirparser_p.h \ @@ -130,10 +119,8 @@ HEADERS += \ $$PWD/qqmlfile.h \ $$PWD/qqmlbundle_p.h \ $$PWD/qqmlmemoryprofiler_p.h \ - $$PWD/qqmlconnections_p.h \ - $$PWD/qqmltimer_p.h \ - $$PWD/qqmlbind_p.h \ $$PWD/qqmlplatform_p.h \ + $$PWD/qqmlbinding_p.h \ $$PWD/qqmlextensionplugin_p.h diff --git a/src/qml/qml/qqmlbind.cpp b/src/qml/qml/qqmlbind.cpp deleted file mode 100644 index fcb3079891..0000000000 --- a/src/qml/qml/qqmlbind.cpp +++ /dev/null @@ -1,310 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlbind_p.h" - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -class QQmlBindPrivate : public QObjectPrivate -{ -public: - QQmlBindPrivate() : componentComplete(true), obj(0), prevBind(0) {} - ~QQmlBindPrivate() { if (prevBind) prevBind->destroy(); } - - QQmlNullableValue when; - bool componentComplete; - QQmlGuard obj; - QString propName; - QQmlNullableValue value; - QQmlProperty prop; - QQmlAbstractBinding *prevBind; -}; - - -/*! - \qmltype Binding - \instantiates QQmlBind - \inqmlmodule QtQml 2 - \ingroup qtquick-interceptors - \brief Enables the arbitrary creation of property bindings - - \section1 Binding to an inaccessible property - - Sometimes it is necessary to bind to a property of an object that wasn't - directly instantiated by QML - generally a property of a class exported - to QML by C++. In these cases, regular property binding doesn't work. Binding - allows you to bind any value to any property. - - For example, imagine a C++ application that maps an "app.enteredText" - property into QML. You could use Binding to update the enteredText property - like this. - \code - TextEdit { id: myTextField; text: "Please type here..." } - Binding { target: app; property: "enteredText"; value: myTextField.text } - \endcode - Whenever the text in the TextEdit is updated, the C++ property will be - updated also. - - \section1 "Single-branch" conditional binding - - In some circumstances you may want to control the value of a property - only when a certain condition is true (and relinquish control in all - other circumstances). This often isn't possible to accomplish with a direct - binding, as you need to supply values for all possible branches. - - \code - // produces warning: "Unable to assign [undefined] to double value" - value: if (mouse.pressed) mouse.mouseX - \endcode - - The above example will produce a warning whenever we release the mouse, as the value - of the binding is undefined when the mouse isn't pressed. We can use the Binding - type to rewrite the above code and avoid the warning. - - \qml - Binding on value { - when: mouse.pressed - value: mouse.mouseX - } - \endqml - - The Binding type will also restore any previously set direct bindings on - the property. In that sense, it functions much like a simplified State. - - \qml - // this is equivalent to the above Binding - State { - name: "pressed" - when: mouse.pressed - PropertyChanges { - target: obj - value: mouse.mouseX - } - } - \endqml - - If the binding target or binding property is changed, the bound value is - immediately pushed onto the new target. - - \sa QtQml -*/ -QQmlBind::QQmlBind(QObject *parent) - : QObject(*(new QQmlBindPrivate), parent) -{ -} - -QQmlBind::~QQmlBind() -{ -} - -/*! - \qmlproperty bool QtQml2::Binding::when - - This property holds when the binding is active. - This should be set to an expression that evaluates to true when you want the binding to be active. - - \code - Binding { - target: contactName; property: 'text' - value: name; when: list.ListView.isCurrentItem - } - \endcode - - When the binding becomes inactive again, any direct bindings that were previously - set on the property will be restored. -*/ -bool QQmlBind::when() const -{ - Q_D(const QQmlBind); - return d->when; -} - -void QQmlBind::setWhen(bool v) -{ - Q_D(QQmlBind); - if (!d->when.isNull && d->when == v) - return; - - d->when = v; - eval(); -} - -/*! - \qmlproperty Object QtQml2::Binding::target - - The object to be updated. -*/ -QObject *QQmlBind::object() -{ - Q_D(const QQmlBind); - return d->obj; -} - -void QQmlBind::setObject(QObject *obj) -{ - Q_D(QQmlBind); - if (d->obj && d->when.isValid() && d->when) { - /* if we switch the object at runtime, we need to restore the - previous binding on the old object before continuing */ - d->when = false; - eval(); - d->when = true; - } - d->obj = obj; - if (d->componentComplete) - d->prop = QQmlProperty(d->obj, d->propName); - eval(); -} - -/*! - \qmlproperty string QtQml2::Binding::property - - The property to be updated. -*/ -QString QQmlBind::property() const -{ - Q_D(const QQmlBind); - return d->propName; -} - -void QQmlBind::setProperty(const QString &p) -{ - Q_D(QQmlBind); - if (!d->propName.isEmpty() && d->when.isValid() && d->when) { - /* if we switch the property name at runtime, we need to restore the - previous binding on the old object before continuing */ - d->when = false; - eval(); - d->when = true; - } - d->propName = p; - if (d->componentComplete) - d->prop = QQmlProperty(d->obj, d->propName); - eval(); -} - -/*! - \qmlproperty any QtQml2::Binding::value - - The value to be set on the target object and property. This can be a - constant (which isn't very useful), or a bound expression. -*/ -QVariant QQmlBind::value() const -{ - Q_D(const QQmlBind); - return d->value.value; -} - -void QQmlBind::setValue(const QVariant &v) -{ - Q_D(QQmlBind); - d->value = v; - eval(); -} - -void QQmlBind::setTarget(const QQmlProperty &p) -{ - Q_D(QQmlBind); - d->prop = p; -} - -void QQmlBind::classBegin() -{ - Q_D(QQmlBind); - d->componentComplete = false; -} - -void QQmlBind::componentComplete() -{ - Q_D(QQmlBind); - d->componentComplete = true; - if (!d->prop.isValid()) - d->prop = QQmlProperty(d->obj, d->propName); - eval(); -} - -void QQmlBind::eval() -{ - Q_D(QQmlBind); - if (!d->prop.isValid() || d->value.isNull || !d->componentComplete) - return; - - if (d->when.isValid()) { - if (!d->when) { - //restore any previous binding - if (d->prevBind) { - QQmlAbstractBinding *tmp = d->prevBind; - d->prevBind = 0; - tmp = QQmlPropertyPrivate::setBinding(d->prop, tmp); - if (tmp) //should this ever be true? - tmp->destroy(); - } - return; - } - - //save any set binding for restoration - QQmlAbstractBinding *tmp; - tmp = QQmlPropertyPrivate::setBinding(d->prop, 0); - if (tmp && d->prevBind) - tmp->destroy(); - else if (!d->prevBind) - d->prevBind = tmp; - } - - d->prop.write(d->value.value); -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlbind_p.h b/src/qml/qml/qqmlbind_p.h deleted file mode 100644 index 1e29c257f0..0000000000 --- a/src/qml/qml/qqmlbind_p.h +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLBIND_H -#define QQMLBIND_H - -#include - -#include - -QT_BEGIN_NAMESPACE - -class QQmlBindPrivate; -class Q_AUTOTEST_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlBind) - Q_INTERFACES(QQmlParserStatus) - Q_INTERFACES(QQmlPropertyValueSource) - Q_PROPERTY(QObject *target READ object WRITE setObject) - Q_PROPERTY(QString property READ property WRITE setProperty) - Q_PROPERTY(QVariant value READ value WRITE setValue) - Q_PROPERTY(bool when READ when WRITE setWhen) - -public: - QQmlBind(QObject *parent=0); - ~QQmlBind(); - - bool when() const; - void setWhen(bool); - - QObject *object(); - void setObject(QObject *); - - QString property() const; - void setProperty(const QString &); - - QVariant value() const; - void setValue(const QVariant &); - -protected: - virtual void setTarget(const QQmlProperty &); - virtual void classBegin(); - virtual void componentComplete(); - -private: - void eval(); -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlBind) - -#endif diff --git a/src/qml/qml/qqmlconnections.cpp b/src/qml/qml/qqmlconnections.cpp deleted file mode 100644 index 286933e557..0000000000 --- a/src/qml/qml/qqmlconnections.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlconnections_p.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -class QQmlConnectionsPrivate : public QObjectPrivate -{ -public: - QQmlConnectionsPrivate() : target(0), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {} - - QList boundsignals; - QObject *target; - - bool targetSet; - bool ignoreUnknownSignals; - bool componentcomplete; - - QByteArray data; -}; - -/*! - \qmltype Connections - \instantiates QQmlConnections - \inqmlmodule QtQml 2 - \ingroup qtquick-interceptors - \brief Describes generalized connections to signals - - A Connections object creates a connection to a QML signal. - - When connecting to signals in QML, the usual way is to create an - "on" handler that reacts when a signal is received, like this: - - \qml - MouseArea { - onClicked: { foo(parameters) } - } - \endqml - - However, it is not possible to connect to a signal in this way in some - cases, such as when: - - \list - \li Multiple connections to the same signal are required - \li Creating connections outside the scope of the signal sender - \li Connecting to targets not defined in QML - \endlist - - When any of these are needed, the Connections type can be used instead. - - For example, the above code can be changed to use a Connections object, - like this: - - \qml - MouseArea { - Connections { - onClicked: foo(parameters) - } - } - \endqml - - More generally, the Connections object can be a child of some object other than - the sender of the signal: - - \qml - MouseArea { - id: area - } - // ... - \endqml - \qml - Connections { - target: area - onClicked: foo(parameters) - } - \endqml - - \sa QtQml -*/ -QQmlConnections::QQmlConnections(QObject *parent) : - QObject(*(new QQmlConnectionsPrivate), parent) -{ -} - -QQmlConnections::~QQmlConnections() -{ -} - -/*! - \qmlproperty Object QtQml2::Connections::target - This property holds the object that sends the signal. - - If this property is not set, the \c target defaults to the parent of the Connection. - - If set to null, no connection is made and any signal handlers are ignored - until the target is not null. -*/ -QObject *QQmlConnections::target() const -{ - Q_D(const QQmlConnections); - return d->targetSet ? d->target : parent(); -} - -class QQmlBoundSignalDeleter : public QObject -{ -public: - QQmlBoundSignalDeleter(QQmlBoundSignal *signal) : m_signal(signal) { m_signal->removeFromObject(); } - ~QQmlBoundSignalDeleter() { delete m_signal; } - -private: - QQmlBoundSignal *m_signal; -}; - -void QQmlConnections::setTarget(QObject *obj) -{ - Q_D(QQmlConnections); - d->targetSet = true; // even if setting to 0, it is *set* - if (d->target == obj) - return; - foreach (QQmlBoundSignal *s, d->boundsignals) { - // It is possible that target is being changed due to one of our signal - // handlers -> use deleteLater(). - if (s->isEvaluating()) - (new QQmlBoundSignalDeleter(s))->deleteLater(); - else - delete s; - } - d->boundsignals.clear(); - d->target = obj; - connectSignals(); - emit targetChanged(); -} - -/*! - \qmlproperty bool QtQml2::Connections::ignoreUnknownSignals - - Normally, a connection to a non-existent signal produces runtime errors. - - If this property is set to \c true, such errors are ignored. - This is useful if you intend to connect to different types of objects, handling - a different set of signals for each object. -*/ -bool QQmlConnections::ignoreUnknownSignals() const -{ - Q_D(const QQmlConnections); - return d->ignoreUnknownSignals; -} - -void QQmlConnections::setIgnoreUnknownSignals(bool ignore) -{ - Q_D(QQmlConnections); - d->ignoreUnknownSignals = ignore; -} - - - -QByteArray -QQmlConnectionsParser::compile(const QList &props) -{ - QByteArray rv; - QDataStream ds(&rv, QIODevice::WriteOnly); - - for(int ii = 0; ii < props.count(); ++ii) - { - QString propName = props.at(ii).name(); - int propLine = props.at(ii).location().line; - int propColumn = props.at(ii).location().column; - - if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) { - error(props.at(ii), QQmlConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); - return QByteArray(); - } - - QList values = props.at(ii).assignedValues(); - - for (int i = 0; i < values.count(); ++i) { - const QVariant &value = values.at(i); - - if (value.userType() == qMetaTypeId()) { - error(props.at(ii), QQmlConnections::tr("Connections: nested objects not allowed")); - return QByteArray(); - } else if (value.userType() == qMetaTypeId()) { - error(props.at(ii), QQmlConnections::tr("Connections: syntax error")); - return QByteArray(); - } else { - QQmlScript::Variant v = qvariant_cast(value); - if (v.isScript()) { - ds << propName; - ds << rewriteSignalHandler(v, propName).toUtf8(); - ds << propLine; - ds << propColumn; - } else { - error(props.at(ii), QQmlConnections::tr("Connections: script expected")); - return QByteArray(); - } - } - } - } - - return rv; -} - -void QQmlConnectionsParser::setCustomData(QObject *object, - const QByteArray &data) -{ - QQmlConnectionsPrivate *p = - static_cast(QObjectPrivate::get(object)); - p->data = data; -} - - -void QQmlConnections::connectSignals() -{ - Q_D(QQmlConnections); - if (!d->componentcomplete || (d->targetSet && !target())) - return; - - QDataStream ds(d->data); - while (!ds.atEnd()) { - QString propName; - ds >> propName; - QByteArray script; - ds >> script; - int line; - ds >> line; - int column; - ds >> column; - - QQmlProperty prop(target(), propName); - if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) { - int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex(); - QQmlBoundSignal *signal = - new QQmlBoundSignal(target(), signalIndex, this, qmlEngine(this)); - - QString location; - QQmlContextData *ctxtdata = 0; - QQmlData *ddata = QQmlData::get(this); - if (ddata) { - ctxtdata = ddata->outerContext; - if (ctxtdata && !ctxtdata->url.isEmpty()) - location = ddata->outerContext->urlString; - } - - QQmlBoundSignalExpression *expression = ctxtdata ? - new QQmlBoundSignalExpression(target(), signalIndex, - ctxtdata, this, script, - true, location, line, column) : 0; - signal->takeExpression(expression); - d->boundsignals += signal; - } else { - if (!d->ignoreUnknownSignals) - qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName); - } - } -} - -void QQmlConnections::classBegin() -{ - Q_D(QQmlConnections); - d->componentcomplete=false; -} - -void QQmlConnections::componentComplete() -{ - Q_D(QQmlConnections); - d->componentcomplete=true; - connectSignals(); -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlconnections_p.h b/src/qml/qml/qqmlconnections_p.h deleted file mode 100644 index 9bc668e5f4..0000000000 --- a/src/qml/qml/qqmlconnections_p.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLCONNECTIONS_H -#define QQMLCONNECTIONS_H - -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -class QQmlBoundSignal; -class QQmlContext; -class QQmlConnectionsPrivate; -class Q_AUTOTEST_EXPORT QQmlConnections : public QObject, public QQmlParserStatus -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlConnections) - - Q_INTERFACES(QQmlParserStatus) - Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) - Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals) - -public: - QQmlConnections(QObject *parent=0); - ~QQmlConnections(); - - QObject *target() const; - void setTarget(QObject *); - - bool ignoreUnknownSignals() const; - void setIgnoreUnknownSignals(bool ignore); - -Q_SIGNALS: - void targetChanged(); - -private: - void connectSignals(); - void classBegin(); - void componentComplete(); -}; - -class QQmlConnectionsParser : public QQmlCustomParser -{ -public: - virtual QByteArray compile(const QList &); - virtual void setCustomData(QObject *, const QByteArray &); -}; - - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlConnections) - -#endif diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 5bb9ac7df6..23d1cf785e 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -55,7 +55,6 @@ #include "qqmlxmlhttprequest_p.h" #include "qqmlscriptstring.h" #include "qqmlglobal_p.h" -#include "qquickworkerscript_p.h" #include "qqmlcomponent_p.h" #include "qqmlnetworkaccessmanagerfactory.h" #include "qqmldirparser_p.h" @@ -96,6 +95,7 @@ #include #include #include +#include #ifdef Q_OS_WIN // for %APPDATA% #include diff --git a/src/qml/qml/qqmllistmodel.cpp b/src/qml/qml/qqmllistmodel.cpp deleted file mode 100644 index 254eb58962..0000000000 --- a/src/qml/qml/qqmllistmodel.cpp +++ /dev/null @@ -1,2588 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmllistmodel_p_p.h" -#include "qqmllistmodelworkeragent_p.h" -#include "qqmlopenmetaobject_p.h" -#include -#include - - -#include -#include -#include -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models. -enum { MIN_LISTMODEL_UID = 1024 }; - -static QAtomicInt uidCounter(MIN_LISTMODEL_UID); - -template -static bool isMemoryUsed(const char *mem) -{ - for (size_t i=0 ; i < sizeof(T) ; ++i) { - if (mem[i] != 0) - return true; - } - - return false; -} - -static QString roleTypeName(ListLayout::Role::DataType t) -{ - QString result; - const char *roleTypeNames[] = { "String", "Number", "Bool", "List", "QObject", "VariantMap", "DateTime" }; - - if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType) - result = QString::fromLatin1(roleTypeNames[t]); - - return result; -} - -const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type) -{ - QStringHash::Node *node = roleHash.findNode(key); - if (node) { - const Role &r = *node->value; - if (type != r.type) - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); - return r; - } - - return createRole(key, type); -} - -const ListLayout::Role &ListLayout::getRoleOrCreate(v8::Handle key, Role::DataType type) -{ - QHashedV8String hashedKey(key); - QStringHash::Node *node = roleHash.findNode(hashedKey); - if (node) { - const Role &r = *node->value; - if (type != r.type) - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); - return r; - } - - QString qkey; - qkey.resize(key->Length()); - key->Write(reinterpret_cast(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(ListModel *), sizeof(QQmlGuard), sizeof(QVariantMap), sizeof(QDateTime) }; - const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime) }; - - 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) -{ - 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; -} - -ListLayout::~ListLayout() -{ - for (int i=0 ; i < roles.count() ; ++i) { - delete roles[i]; - } -} - -void ListLayout::sync(ListLayout *src, ListLayout *target) -{ - 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); - } - - target->currentBlockOffset = src->currentBlockOffset; - target->currentBlock = src->currentBlock; -} - -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; -} - -ListLayout::Role::~Role() -{ - delete subLayout; -} - -const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QVariant &data) -{ - 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; - case QVariant::Map: type = Role::VariantMap; break; - default: type = Role::Invalid; break; - } - - if (type == Role::Invalid) { - qmlInfo(0) << "Can't create role for unsupported data type"; - return 0; - } - - return &getRoleOrCreate(key, type); -} - -const ListLayout::Role *ListLayout::getExistingRole(const QString &key) -{ - Role *r = 0; - QStringHash::Node *node = roleHash.findNode(key); - if (node) - r = node->value; - return r; -} - -const ListLayout::Role *ListLayout::getExistingRole(v8::Handle key) -{ - Role *r = 0; - QHashedV8String hashedKey(key); - QStringHash::Node *node = roleHash.findNode(hashedKey); - if (node) - r = node->value; - return r; -} - -ModelObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex) -{ - ListElement *e = elements[elementIndex]; - if (e->m_objectCache == 0) { - e->m_objectCache = new ModelObject(model, elementIndex); - } - return e->m_objectCache; -} - -void ListModel::sync(ListModel *src, ListModel *target, QHash *targetModelHash) -{ - // Sanity check - target->m_uid = src->m_uid; - if (targetModelHash) - targetModelHash->insert(target->m_uid, target); - - // Build hash of elements <-> uid for each of the lists - QHash 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::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::iterator it = elementHash.begin(); - QHash::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; - } - - // 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); - } - - target->updateCacheIndices(); - - // 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(); - } -} - -ListModel::ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid) : m_layout(layout), m_modelCache(modelCache) -{ - if (uid == -1) - uid = uidCounter.fetchAndAddOrdered(1); - 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; -} - -void ListModel::insertElement(int index) -{ - newElement(index); - updateCacheIndices(); -} - -void ListModel::move(int from, int to, int 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; - } - - QPODVector 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]; - - updateCacheIndices(); -} - -void ListModel::newElement(int index) -{ - ListElement *e = new ListElement; - elements.insert(index, e); -} - -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 QQmlListModel *owner, QV8Engine *eng) -{ - ListElement *e = elements[elementIndex]; - const ListLayout::Role &r = m_layout->getExistingRole(roleIndex); - return e->getProperty(r, owner, eng); -} - -ListModel *ListModel::getListProperty(int elementIndex, const ListLayout::Role &role) -{ - ListElement *e = elements[elementIndex]; - return e->getListProperty(role); -} - -void ListModel::set(int elementIndex, v8::Handle object, QVector *roles, QV8Engine *eng) -{ - ListElement *e = elements[elementIndex]; - - v8::Local propertyNames = object->GetPropertyNames(); - int propertyCount = propertyNames->Length(); - - for (int i=0 ; i < propertyCount ; ++i) { - v8::Local propertyName = propertyNames->Get(i)->ToString(); - v8::Local 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 jsString = propertyValue->ToString(); - QString qstr; - qstr.resize(jsString->Length()); - jsString->Write(reinterpret_cast(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 subArray = v8::Handle::Cast(propertyValue); - int arrayLength = subArray->Length(); - for (int j=0 ; j < arrayLength ; ++j) { - v8::Handle subObject = subArray->Get(j)->ToObject(); - subModel->append(subObject, eng); - } - - roleIndex = e->setListProperty(r, subModel); - } else if (propertyValue->IsBoolean()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); - roleIndex = e->setBoolProperty(r, propertyValue->BooleanValue()); - } else if (propertyValue->IsDate()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); - QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(propertyValue)->NumberValue()); - roleIndex = e->setDateTimeProperty(r, dt); - } 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) - roleIndex = e->setQObjectProperty(role, o); - } else { - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); - if (role.type == ListLayout::Role::VariantMap) - roleIndex = e->setVariantMapProperty(role, propertyValue->ToObject(), eng); - } - } 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 object, QV8Engine *eng) -{ - ListElement *e = elements[elementIndex]; - - v8::Local propertyNames = object->GetPropertyNames(); - int propertyCount = propertyNames->Length(); - - for (int i=0 ; i < propertyCount ; ++i) { - v8::Local propertyName = propertyNames->Get(i)->ToString(); - v8::Local 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 jsString = propertyValue->ToString(); - QString qstr; - qstr.resize(jsString->Length()); - jsString->Write(reinterpret_cast(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 subArray = v8::Handle::Cast(propertyValue); - int arrayLength = subArray->Length(); - for (int j=0 ; j < arrayLength ; ++j) { - v8::Handle subObject = subArray->Get(j)->ToObject(); - subModel->append(subObject, eng); - } - - e->setListPropertyFast(r, subModel); - } - } 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->IsDate()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); - if (r.type == ListLayout::Role::DateTime) { - QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(propertyValue)->NumberValue()); - e->setDateTimePropertyFast(r, dt); - } - } 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 { - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); - if (role.type == ListLayout::Role::VariantMap) - e->setVariantMapFast(role, propertyValue->ToObject(), eng); - } - } 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, int count) -{ - for (int i=0 ; i < count ; ++i) { - elements[index+i]->destroy(m_layout); - delete elements[index+i]; - } - elements.remove(index, count); - updateCacheIndices(); -} - -void ListModel::insert(int elementIndex, v8::Handle object, QV8Engine *eng) -{ - insertElement(elementIndex); - set(elementIndex, object, eng); -} - -int ListModel::append(v8::Handle object, QV8Engine *eng) -{ - int elementIndex = appendElement(); - set(elementIndex, object, eng); - 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) { - QVector roles; - roles << roleIndex; - e->m_objectCache->updateValues(roles); - } - } - } - - return roleIndex; -} - -int ListModel::setExistingProperty(int elementIndex, const QString &key, v8::Handle data, QV8Engine *eng) -{ - 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, eng); - } - - 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(mem); - return s->data_ptr() ? s : 0; -} - -QObject *ListElement::getQObjectProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - QQmlGuard *o = reinterpret_cast *>(mem); - return o->data(); -} - -QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role) -{ - QVariantMap *map = 0; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) - map = reinterpret_cast(mem); - - return map; -} - -QDateTime *ListElement::getDateTimeProperty(const ListLayout::Role &role) -{ - QDateTime *dt = 0; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) - dt = reinterpret_cast(mem); - - return dt; -} - -QQmlGuard *ListElement::getGuardProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - - bool existingGuard = false; - for (size_t i=0 ; i < sizeof(QQmlGuard) ; ++i) { - if (mem[i] != 0) { - existingGuard = true; - break; - } - } - - QQmlGuard *o = 0; - - if (existingGuard) - o = reinterpret_cast *>(mem); - - return o; -} - -ListModel *ListElement::getListProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - ListModel **value = reinterpret_cast(mem); - return *value; -} - -QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV8Engine *eng) -{ - char *mem = getPropertyMemory(role); - - QVariant data; - - switch (role.type) { - case ListLayout::Role::Number: - { - double *value = reinterpret_cast(mem); - data = *value; - } - break; - case ListLayout::Role::String: - { - QString *value = reinterpret_cast(mem); - if (value->data_ptr() != 0) - data = *value; - } - break; - case ListLayout::Role::Bool: - { - bool *value = reinterpret_cast(mem); - data = *value; - } - break; - case ListLayout::Role::List: - { - ListModel **value = reinterpret_cast(mem); - ListModel *model = *value; - - if (model) { - if (model->m_modelCache == 0) { - model->m_modelCache = new QQmlListModel(owner, model, eng); - QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner)); - } - - QObject *object = model->m_modelCache; - data = QVariant::fromValue(object); - } - } - break; - case ListLayout::Role::QObject: - { - QQmlGuard *guard = reinterpret_cast *>(mem); - QObject *object = guard->data(); - if (object) - data = QVariant::fromValue(object); - } - break; - case ListLayout::Role::VariantMap: - { - if (isMemoryUsed(mem)) { - QVariantMap *map = reinterpret_cast(mem); - data = *map; - } - } - break; - case ListLayout::Role::DateTime: - { - if (isMemoryUsed(mem)) { - QDateTime *dt = reinterpret_cast(mem); - data = *dt; - } - } - 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(mem); - bool changed; - if (c->data_ptr() == 0) { - new (mem) QString(s); - changed = true; - } else { - changed = c->compare(s) != 0; - *c = s; - } - if (changed) - roleIndex = role.index; - } - - 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; - } - - 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; - } - - 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; - } - - return roleIndex; -} - -int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::QObject) { - char *mem = getPropertyMemory(role); - QQmlGuard *g = reinterpret_cast *>(mem); - bool existingGuard = false; - for (size_t i=0 ; i < sizeof(QQmlGuard) ; ++i) { - if (mem[i] != 0) { - existingGuard = true; - break; - } - } - bool changed; - if (existingGuard) { - changed = g->data() != o; - g->~QQmlGuard(); - } else { - changed = true; - } - new (mem) QQmlGuard(o); - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setVariantMapProperty(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::VariantMap) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) { - QVariantMap *map = reinterpret_cast(mem); - map->~QMap(); - } - new (mem) QVariantMap(eng->variantMapFromJS(o)); - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::VariantMap) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) { - QVariantMap *map = reinterpret_cast(mem); - map->~QMap(); - } - if (m) - new (mem) QVariantMap(*m); - else - new (mem) QVariantMap; - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::DateTime) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) { - QDateTime *dt = reinterpret_cast(mem); - dt->~QDateTime(); - } - new (mem) QDateTime(dt); - roleIndex = role.index; - } - - 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) QQmlGuard(o); -} - -void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m) -{ - char *mem = getPropertyMemory(role); - ListModel **value = new (mem) ListModel *; - *value = m; -} - -void ListElement::setVariantMapFast(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng) -{ - char *mem = getPropertyMemory(role); - QVariantMap *map = new (mem) QVariantMap; - *map = eng->variantMapFromJS(o); -} - -void ListElement::setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt) -{ - char *mem = getPropertyMemory(role); - new (mem) QDateTime(dt); -} - -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; - case ListLayout::Role::DateTime: - setDateTimeProperty(role, QDateTime()); - break; - case ListLayout::Role::VariantMap: - setVariantMapProperty(role, 0); - break; - default: - break; - } -} - -ListElement::ListElement() -{ - m_objectCache = 0; - uid = uidCounter.fetchAndAddOrdered(1); - next = 0; - memset(data, 0, sizeof(data)); -} - -ListElement::ListElement(int existingUid) -{ - m_objectCache = 0; - uid = existingUid; - next = 0; - memset(data, 0, sizeof(data)); -} - -ListElement::~ListElement() -{ - delete next; -} - -void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash *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: - case ListLayout::Role::DateTime: - { - QVariant v = src->getProperty(srcRole, 0, 0); - target->setVariantProperty(targetRole, v); - } - case ListLayout::Role::VariantMap: - { - QVariantMap *map = src->getVariantMapProperty(srcRole); - target->setVariantMapProperty(targetRole, map); - } - break; - 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: - { - QQmlGuard *guard = getGuardProperty(r); - if (guard) - guard->~QQmlGuard(); - } - break; - case ListLayout::Role::VariantMap: - { - QVariantMap *map = getVariantMapProperty(r); - if (map) - map->~QMap(); - } - break; - case ListLayout::Role::DateTime: - { - QDateTime *dt = getDateTimeProperty(r); - if (dt) - dt->~QDateTime(); - } - 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()); - break; - case ListLayout::Role::VariantMap: { - QVariantMap map = d.toMap(); - roleIndex = setVariantMapProperty(role, &map); - } - break; - case ListLayout::Role::DateTime: - roleIndex = setDateTimeProperty(role, d.toDateTime()); - break; - default: - break; - } - - return roleIndex; -} - -int ListElement::setJsProperty(const ListLayout::Role &role, v8::Handle d, QV8Engine *eng) -{ - // Check if this key exists yet - int roleIndex = -1; - - // Add the value now - if (d->IsString()) { - v8::Handle jsString = d->ToString(); - QString qstr; - qstr.resize(jsString->Length()); - jsString->Write(reinterpret_cast(qstr.data())); - roleIndex = setStringProperty(role, qstr); - } else if (d->IsNumber()) { - roleIndex = setDoubleProperty(role, d->NumberValue()); - } else if (d->IsArray()) { - if (role.type == ListLayout::Role::List) { - ListModel *subModel = new ListModel(role.subLayout, 0, -1); - v8::Handle subArray = v8::Handle::Cast(d); - int arrayLength = subArray->Length(); - for (int j=0 ; j < arrayLength ; ++j) { - v8::Handle subObject = subArray->Get(j)->ToObject(); - subModel->append(subObject, eng); - } - roleIndex = setListProperty(role, subModel); - } else { - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List)); - } - } else if (d->IsBoolean()) { - roleIndex = setBoolProperty(role, d->BooleanValue()); - } else if (d->IsDate()) { - QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(d)->NumberValue()); - roleIndex = setDateTimeProperty(role, dt); - } 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 (role.type == ListLayout::Role::VariantMap) { - roleIndex = setVariantMapProperty(role, d->ToObject(), eng); - } - } else if (d.IsEmpty() || d->IsUndefined() || d->IsNull()) { - clearProperty(role); - } - - return roleIndex; -} - -ModelObject::ModelObject(QQmlListModel *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 QVector &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) -: QQmlOpenMetaObject(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 v = eng->fromVariant(value); - - int roleIndex = m_obj->m_model->m_listModel->setExistingProperty(m_obj->m_elementIndex, propName, v, eng); - if (roleIndex != -1) { - QVector roles; - roles << roleIndex; - m_obj->m_model->emitItemsChanged(m_obj->m_elementIndex, 1, roles); - } -} - -DynamicRoleModelNode::DynamicRoleModelNode(QQmlListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this)) -{ - setNodeUpdatesEnabled(true); -} - -DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QQmlListModel *owner) -{ - DynamicRoleModelNode *object = new DynamicRoleModelNode(owner, uidCounter.fetchAndAddOrdered(1)); - QVector roles; - object->updateValues(obj, roles); - return object; -} - -void DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash *targetModelHash) -{ - for (int i=0 ; i < src->m_meta->count() ; ++i) { - const QByteArray &name = src->m_meta->name(i); - QVariant value = src->m_meta->value(i); - - QQmlListModel *srcModel = qobject_cast(value.value()); - QQmlListModel *targetModel = qobject_cast(target->m_meta->value(i).value()); - - if (srcModel) { - if (targetModel == 0) - targetModel = QQmlListModel::createWithOwner(target->m_owner); - - QQmlListModel::sync(srcModel, targetModel, targetModelHash); - - QObject *targetModelObject = targetModel; - value = QVariant::fromValue(targetModelObject); - } else if (targetModel) { - delete targetModel; - } - - target->setValue(name, value); - } -} - -void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector &roles) -{ - const QList &keys = object.keys(); - - QList::const_iterator it = keys.begin(); - QList::const_iterator end = keys.end(); - - while (it != end) { - const QString &key = *it; - - int roleIndex = m_owner->m_roles.indexOf(key); - if (roleIndex == -1) { - roleIndex = m_owner->m_roles.count(); - m_owner->m_roles.append(key); - } - - QVariant value = object[key]; - - if (value.type() == QVariant::List) { - QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner); - - QVariantList subArray = value.toList(); - QVariantList::const_iterator subIt = subArray.begin(); - QVariantList::const_iterator subEnd = subArray.end(); - while (subIt != subEnd) { - const QVariantMap &subObject = subIt->toMap(); - subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); - ++subIt; - } - - QObject *subModelObject = subModel; - value = QVariant::fromValue(subModelObject); - } - - const QByteArray &keyUtf8 = key.toUtf8(); - - QQmlListModel *existingModel = qobject_cast(m_meta->value(keyUtf8).value()); - if (existingModel) - delete existingModel; - - if (m_meta->setValue(keyUtf8, value)) - roles << roleIndex; - - ++it; - } -} - -DynamicRoleModelNodeMetaObject::DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object) - : QQmlOpenMetaObject(object), m_enabled(false), m_owner(object) -{ -} - -DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject() -{ - for (int i=0 ; i < count() ; ++i) { - QQmlListModel *subModel = qobject_cast(value(i).value()); - if (subModel) - delete subModel; - } -} - -void DynamicRoleModelNodeMetaObject::propertyWrite(int index) -{ - if (!m_enabled) - return; - - QVariant v = value(index); - QQmlListModel *model = qobject_cast(v.value()); - if (model) - delete model; -} - -void DynamicRoleModelNodeMetaObject::propertyWritten(int index) -{ - if (!m_enabled) - return; - - QQmlListModel *parentModel = m_owner->m_owner; - - QVariant v = value(index); - if (v.type() == QVariant::List) { - QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel); - - QVariantList subArray = v.toList(); - QVariantList::const_iterator subIt = subArray.begin(); - QVariantList::const_iterator subEnd = subArray.end(); - while (subIt != subEnd) { - const QVariantMap &subObject = subIt->toMap(); - subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); - ++subIt; - } - - QObject *subModelObject = subModel; - v = QVariant::fromValue(subModelObject); - - setValue(index, v); - } - - int elementIndex = parentModel->m_modelObjects.indexOf(m_owner); - int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData())); - - if (elementIndex != -1 && roleIndex != -1) { - - QVector roles; - roles << roleIndex; - - parentModel->emitItemsChanged(elementIndex, 1, roles); - } -} - -QQmlListModelParser::ListInstruction *QQmlListModelParser::ListModelData::instructions() const -{ - return (QQmlListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData)); -} - -/*! - \qmltype ListModel - \instantiates QQmlListModel - \inqmlmodule QtQml.Models 2 - \brief Defines a free-form list data source - - The ListModel is a simple container of ListElement definitions, each containing data roles. - The contents can be defined dynamically, or explicitly in QML. - - This type is also available in the QtQuick 2 import. For full documentation, see \l QtQuick2::ListModel -*/ -/*! - \qmltype ListModel - \instantiates QQmlListModel - \inqmlmodule QtQuick 2 - \brief Defines a free-form list data source - \ingroup qtquick-models - - The ListModel is a simple container of ListElement definitions, each containing data roles. - The contents can be defined dynamically, or explicitly in QML. - - The number of elements in the model can be obtained from its \l count property. - A number of familiar methods are also provided to manipulate the contents of the - model, including append(), insert(), move(), remove() and set(). These methods - accept dictionaries as their arguments; these are translated to ListElement objects - by the model. - - Elements can be manipulated via the model using the setProperty() method, which - allows the roles of the specified element to be set and changed. - - \section1 Example Usage - - The following example shows a ListModel containing three elements, with the roles - "name" and "cost". - - \div {class="float-right"} - \inlineimage listmodel.png - \enddiv - - \snippet qml/listmodel/listmodel.qml 0 - - Roles (properties) in each element must begin with a lower-case letter and - should be common to all elements in a model. The ListElement documentation - provides more guidelines for how elements should be defined. - - Since the example model contains an \c id property, it can be referenced - by views, such as the ListView in this example: - - \snippet qml/listmodel/listmodel-simple.qml 0 - \dots 8 - \snippet qml/listmodel/listmodel-simple.qml 1 - - It is possible for roles to contain list data. In the following example we - create a list of fruit attributes: - - \snippet qml/listmodel/listmodel-nested.qml model - - The delegate displays all the fruit attributes: - - \div {class="float-right"} - \inlineimage listmodel-nested.png - \enddiv - - \snippet qml/listmodel/listmodel-nested.qml delegate - - \section1 Modifying List Models - - The content of a ListModel may be created and modified using the clear(), - append(), set(), insert() and setProperty() methods. For example: - - \snippet qml/listmodel/listmodel-modify.qml delegate - - Note that when creating content dynamically the set of available properties - cannot be changed once set. Whatever properties are first added to the model - are the only permitted properties in the model. - - \section1 Using Threaded List Models with WorkerScript - - ListModel can be used together with WorkerScript access a list model - from multiple threads. This is useful if list modifications are - synchronous and take some time: the list operations can be moved to a - different thread to avoid blocking of the main GUI thread. - - Here is an example that uses WorkerScript to periodically append the - current time to a list model: - - \snippet quick/threading/threadedlistmodel/timedisplay.qml 0 - - The included file, \tt dataloader.js, looks like this: - - \snippet quick/threading/threadedlistmodel/dataloader.js 0 - - The timer in the main example sends messages to the worker script by calling - \l WorkerScript::sendMessage(). When this message is received, - \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js, - which appends the current time to the list model. - - Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()} - 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. - - \sa {qml-data-models}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtQml -*/ - -QQmlListModel::QQmlListModel(QObject *parent) -: QAbstractListModel(parent) -{ - m_mainThread = true; - m_primary = true; - m_agent = 0; - m_uid = uidCounter.fetchAndAddOrdered(1); - m_dynamicRoles = false; - - m_layout = new ListLayout; - m_listModel = new ListModel(m_layout, this, -1); - - m_engine = 0; -} - -QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent) -: QAbstractListModel(parent) -{ - m_mainThread = owner->m_mainThread; - m_primary = false; - m_agent = owner->m_agent; - - Q_ASSERT(owner->m_dynamicRoles == false); - m_dynamicRoles = false; - m_layout = 0; - m_listModel = data; - - m_engine = eng; -} - -QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent) -: QAbstractListModel(agent) -{ - m_mainThread = false; - m_primary = true; - m_agent = agent; - m_dynamicRoles = orig->m_dynamicRoles; - - m_layout = new ListLayout(orig->m_layout); - m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid()); - - if (m_dynamicRoles) - sync(orig, this, 0); - else - ListModel::sync(orig->m_listModel, m_listModel, 0); - - m_engine = 0; -} - -QQmlListModel::~QQmlListModel() -{ - for (int i=0 ; i < m_modelObjects.count() ; ++i) - delete m_modelObjects[i]; - - if (m_primary) { - m_listModel->destroy(); - delete m_listModel; - - if (m_mainThread && m_agent) { - m_agent->modelDestroyed(); - m_agent->release(); - } - } - - m_listModel = 0; - - delete m_layout; - m_layout = 0; -} - -QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner) -{ - QQmlListModel *model = new QQmlListModel; - - model->m_mainThread = newOwner->m_mainThread; - model->m_engine = newOwner->m_engine; - model->m_agent = newOwner->m_agent; - model->m_dynamicRoles = newOwner->m_dynamicRoles; - - if (model->m_mainThread && model->m_agent) - model->m_agent->addref(); - - QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner)); - - return model; -} - -QV8Engine *QQmlListModel::engine() const -{ - if (m_engine == 0) { - m_engine = QQmlEnginePrivate::getV8Engine(qmlEngine(this)); - } - - return m_engine; -} - -void QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target, QHash *targetModelHash) -{ - Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles); - - target->m_uid = src->m_uid; - if (targetModelHash) - targetModelHash->insert(target->m_uid, target); - target->m_roles = src->m_roles; - - // Build hash of elements <-> uid for each of the lists - QHash elementHash; - for (int i=0 ; i < target->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *e = target->m_modelObjects.at(i); - int uid = e->getUid(); - ElementSync sync; - sync.target = e; - elementHash.insert(uid, sync); - } - for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *e = src->m_modelObjects.at(i); - int uid = e->getUid(); - - QHash::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::iterator it = elementHash.begin(); - QHash::iterator end = elementHash.end(); - while (it != end) { - const ElementSync &s = it.value(); - if (s.src == 0) { - int targetIndex = target->m_modelObjects.indexOf(s.target); - target->m_modelObjects.remove(targetIndex, 1); - delete s.target; - } - ++it; - } - - // Clear the target list, and append in correct order from the source - target->m_modelObjects.clear(); - for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *srcElement = src->m_modelObjects.at(i); - it = elementHash.find(srcElement->getUid()); - const ElementSync &s = it.value(); - DynamicRoleModelNode *targetElement = s.target; - if (targetElement == 0) { - targetElement = new DynamicRoleModelNode(target, srcElement->getUid()); - } - DynamicRoleModelNode::sync(srcElement, targetElement, targetModelHash); - target->m_modelObjects.append(targetElement); - } -} - -void QQmlListModel::emitItemsChanged(int index, int count, const QVector &roles) -{ - if (count <= 0) - return; - - if (m_mainThread) { - emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);; - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - m_agent->data.changedChange(uid, index, count, roles); - } -} - -void QQmlListModel::emitItemsRemoved(int index, int count) -{ - if (count <= 0) - return; - - if (m_mainThread) { - beginRemoveRows(QModelIndex(), index, index + count - 1); - endRemoveRows(); - emit countChanged(); - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - if (index == 0 && count == this->count()) - m_agent->data.clearChange(uid); - m_agent->data.removeChange(uid, index, count); - } -} - -void QQmlListModel::emitItemsInserted(int index, int count) -{ - if (count <= 0) - return; - - if (m_mainThread) { - beginInsertRows(QModelIndex(), index, index + count - 1); - endInsertRows(); - emit countChanged(); - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - m_agent->data.insertChange(uid, index, count); - } -} - -void QQmlListModel::emitItemsMoved(int from, int to, int n) -{ - if (n <= 0) - return; - - if (m_mainThread) { - beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to); - endMoveRows(); - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - m_agent->data.moveChange(uid, from, n, to); - } -} - -QQmlListModelWorkerAgent *QQmlListModel::agent() -{ - if (m_agent) - return m_agent; - - m_agent = new QQmlListModelWorkerAgent(this); - return m_agent; -} - -QModelIndex QQmlListModel::index(int row, int column, const QModelIndex &parent) const -{ - return row >= 0 && row < count() && column == 0 && !parent.isValid() - ? createIndex(row, column) - : QModelIndex(); -} - -int QQmlListModel::rowCount(const QModelIndex &parent) const -{ - return !parent.isValid() ? count() : 0; -} - -QVariant QQmlListModel::data(const QModelIndex &index, int role) const -{ - return data(index.row(), role); -} - -QVariant QQmlListModel::data(int index, int role) const -{ - QVariant v; - - if (index >= count() || index < 0) - return v; - - if (m_dynamicRoles) - v = m_modelObjects[index]->getValue(m_roles[role]); - else - v = m_listModel->getProperty(index, role, this, engine()); - - return v; -} - -QHash QQmlListModel::roleNames() const -{ - QHash roleNames; - - if (m_dynamicRoles) { - for (int i = 0 ; i < m_roles.count() ; ++i) - roleNames.insert(i, m_roles.at(i).toUtf8()); - } else { - for (int i = 0 ; i < m_listModel->roleCount() ; ++i) { - const ListLayout::Role &r = m_listModel->getExistingRole(i); - roleNames.insert(i, r.name.toUtf8()); - } - } - - return roleNames; -} - -/*! - \qmlproperty bool QtQml2::ListModel::dynamicRoles - - By default, the type of a role is fixed the first time - the role is used. For example, if you create a role called - "data" and assign a number to it, you can no longer assign - a string to the "data" role. However, when the dynamicRoles - property is enabled, the type of a given role is not fixed - and can be different between elements. - - The dynamicRoles property must be set before any data is - added to the ListModel, and must be set from the main - thread. - - A ListModel that has data statically defined (via the - ListElement QML syntax) cannot have the dynamicRoles - property enabled. - - There is a significant performance cost to using a - ListModel with dynamic roles enabled. The cost varies - from platform to platform but is typically somewhere - between 4-6x slower than using static role types. - - Due to the performance cost of using dynamic roles, - they are disabled by default. -*/ -void QQmlListModel::setDynamicRoles(bool enableDynamicRoles) -{ - if (m_mainThread && m_agent == 0) { - if (enableDynamicRoles) { - if (m_layout->roleCount()) - qmlInfo(this) << tr("unable to enable dynamic roles as this model is not empty!"); - else - m_dynamicRoles = true; - } else { - if (m_roles.count()) { - qmlInfo(this) << tr("unable to enable static roles as this model is not empty!"); - } else { - m_dynamicRoles = false; - } - } - } else { - qmlInfo(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created"); - } -} - -/*! - \qmlproperty int QtQml2::ListModel::count - The number of data entries in the model. -*/ -int QQmlListModel::count() const -{ - int count; - - if (m_dynamicRoles) - count = m_modelObjects.count(); - else { - count = m_listModel->elementCount(); - } - - return count; -} - -/*! - \qmlmethod QtQml2::ListModel::clear() - - Deletes all content from the model. - - \sa append(), remove() -*/ -void QQmlListModel::clear() -{ - int cleared = count(); - - if (m_dynamicRoles) { - for (int i=0 ; i < m_modelObjects.count() ; ++i) - delete m_modelObjects[i]; - m_modelObjects.clear(); - } else { - m_listModel->clear(); - } - - emitItemsRemoved(0, cleared); -} - -/*! - \qmlmethod QtQml2::ListModel::remove(int index, int count = 1) - - Deletes the content at \a index from the model. - - \sa clear() -*/ -void QQmlListModel::remove(QQmlV8Function *args) -{ - int argLength = args->Length(); - - if (argLength == 1 || argLength == 2) { - int index = (*args)[0]->Int32Value(); - int removeCount = (argLength == 2 ? ((*args)[1]->Int32Value()) : 1); - - if (index < 0 || index+removeCount > count() || removeCount <= 0) { - qmlInfo(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count()); - return; - } - - if (m_dynamicRoles) { - for (int i=0 ; i < removeCount ; ++i) - delete m_modelObjects[index+i]; - m_modelObjects.remove(index, removeCount); - } else { - m_listModel->remove(index, removeCount); - } - - emitItemsRemoved(index, removeCount); - } else { - qmlInfo(this) << tr("remove: incorrect number of arguments"); - } -} - -/*! - \qmlmethod QtQml2::ListModel::insert(int index, jsobject 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 - - The \a index must be to an existing item in the list, or one past - the end of the list (equivalent to append). - - \sa set(), append() -*/ - -void QQmlListModel::insert(QQmlV8Function *args) -{ - if (args->Length() == 2) { - - v8::Handle arg0 = (*args)[0]; - int index = arg0->Int32Value(); - - if (index < 0 || index > count()) { - qmlInfo(this) << tr("insert: index %1 out of range").arg(index); - return; - } - - v8::Handle arg1 = (*args)[1]; - - if (arg1->IsArray()) { - v8::Handle objectArray = v8::Handle::Cast(arg1); - int objectArrayLength = objectArray->Length(); - for (int i=0 ; i < objectArrayLength ; ++i) { - v8::Handle argObject = objectArray->Get(i)->ToObject(); - - if (m_dynamicRoles) { - m_modelObjects.insert(index+i, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); - } else { - m_listModel->insert(index+i, argObject, args->engine()); - } - } - emitItemsInserted(index, objectArrayLength); - } else if (arg1->IsObject()) { - v8::Handle argObject = arg1->ToObject(); - - if (m_dynamicRoles) { - m_modelObjects.insert(index, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); - } else { - m_listModel->insert(index, argObject, args->engine()); - } - - emitItemsInserted(index, 1); - } else { - qmlInfo(this) << tr("insert: value is not an object"); - } - } else { - qmlInfo(this) << tr("insert: value is not an object"); - } -} - -/*! - \qmlmethod QtQml2::ListModel::move(int from, int to, int 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 QQmlListModel::move(int from, int to, int n) -{ - if (n==0 || from==to) - return; - if (!canMove(from, to, n)) { - qmlInfo(this) << tr("move: out of range"); - return; - } - - if (m_dynamicRoles) { - - int realFrom = from; - int realTo = to; - int realN = n; - - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - realFrom = tto; - realTo = tto+n; - realN = tfrom-tto; - } - - QPODVector store; - for (int i=0 ; i < (realTo-realFrom) ; ++i) - store.append(m_modelObjects[realFrom+realN+i]); - for (int i=0 ; i < realN ; ++i) - store.append(m_modelObjects[realFrom+i]); - for (int i=0 ; i < store.count() ; ++i) - m_modelObjects[realFrom+i] = store[i]; - - } else { - m_listModel->move(from, to, n); - } - - emitItemsMoved(from, to, n); -} - -/*! - \qmlmethod QtQml2::ListModel::append(jsobject 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 QQmlListModel::append(QQmlV8Function *args) -{ - if (args->Length() == 1) { - v8::Handle arg = (*args)[0]; - - if (arg->IsArray()) { - v8::Handle objectArray = v8::Handle::Cast(arg); - int objectArrayLength = objectArray->Length(); - - int index = count(); - for (int i=0 ; i < objectArrayLength ; ++i) { - v8::Handle argObject = objectArray->Get(i)->ToObject(); - - if (m_dynamicRoles) { - m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); - } else { - m_listModel->append(argObject, args->engine()); - } - } - - emitItemsInserted(index, objectArrayLength); - } else if (arg->IsObject()) { - v8::Handle argObject = arg->ToObject(); - - int index; - - if (m_dynamicRoles) { - index = m_modelObjects.count(); - m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); - } else { - index = m_listModel->append(argObject, args->engine()); - } - - emitItemsInserted(index, 1); - } else { - qmlInfo(this) << tr("append: value is not an object"); - } - } else { - qmlInfo(this) << tr("append: value is not an object"); - } -} - -/*! - \qmlmethod object QtQml2::ListModel::get(int index) - - Returns the item at \a index in the list model. This allows the item - data to be accessed or modified from JavaScript: - - \code - Component.onCompleted: { - fruitModel.append({"cost": 5.95, "name":"Jackfruit"}); - console.log(fruitModel.get(0).cost); - fruitModel.get(0).cost = 10.95; - } - \endcode - - The \a index must be an element in the list. - - Note that properties of the returned object that are themselves objects - will also be models, and this get() method is used to access elements: - - \code - fruitModel.append(..., "attributes": - [{"name":"spikes","value":"7mm"}, - {"name":"color","value":"green"}]); - fruitModel.get(0).attributes.get(1).value; // == "green" - \endcode - - \warning The returned object is not guaranteed to remain valid. It - should not be used in \l{Property Binding}{property bindings}. - - \sa append() -*/ -QQmlV8Handle QQmlListModel::get(int index) const -{ - v8::Handle result = v8::Undefined(); - - if (index >= 0 && index < count()) { - QV8Engine *v8engine = engine(); - - if (m_dynamicRoles) { - DynamicRoleModelNode *object = m_modelObjects[index]; - result = v8engine->newQObject(object); - } else { - ModelObject *object = m_listModel->getOrCreateModelObject(const_cast(this), index); - result = v8engine->newQObject(object); - } - } - - return QQmlV8Handle::fromHandle(result); -} - -/*! - \qmlmethod QtQml2::ListModel::set(int index, jsobject dict) - - Changes the item at \a index in the list model with the - values in \a dict. Properties not appearing in \a dict - are left unchanged. - - \code - fruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) - \endcode - - If \a index is equal to count() then a new item is appended to the - list. Otherwise, \a index must be an element in the list. - - \sa append() -*/ -void QQmlListModel::set(int index, const QQmlV8Handle &handle) -{ - v8::Handle valuemap = handle.toHandle(); - - if (!valuemap->IsObject() || valuemap->IsArray()) { - qmlInfo(this) << tr("set: value is not an object"); - return; - } - if (index > count() || index < 0) { - qmlInfo(this) << tr("set: index %1 out of range").arg(index); - return; - } - - v8::Handle object = valuemap->ToObject(); - - if (index == count()) { - - if (m_dynamicRoles) { - m_modelObjects.append(DynamicRoleModelNode::create(engine()->variantMapFromJS(object), this)); - } else { - m_listModel->insert(index, object, engine()); - } - - emitItemsInserted(index, 1); - } else { - - QVector roles; - - if (m_dynamicRoles) { - m_modelObjects[index]->updateValues(engine()->variantMapFromJS(object), roles); - } else { - m_listModel->set(index, object, &roles, engine()); - } - - if (roles.count()) - emitItemsChanged(index, 1, roles); - } -} - -/*! - \qmlmethod QtQml2::ListModel::setProperty(int index, string property, variant value) - - Changes the \a property of the item at \a index in the list model to \a value. - - \code - fruitModel.setProperty(3, "cost", 5.95) - \endcode - - The \a index must be an element in the list. - - \sa append() -*/ -void QQmlListModel::setProperty(int index, const QString& property, const QVariant& value) -{ - if (count() == 0 || index >= count() || index < 0) { - qmlInfo(this) << tr("set: index %1 out of range").arg(index); - return; - } - - if (m_dynamicRoles) { - int roleIndex = m_roles.indexOf(property); - if (roleIndex == -1) { - roleIndex = m_roles.count(); - m_roles.append(property); - } - if (m_modelObjects[index]->setValue(property.toUtf8(), value)) { - QVector roles; - roles << roleIndex; - emitItemsChanged(index, 1, roles); - } - } else { - int roleIndex = m_listModel->setOrCreateProperty(index, property, value); - if (roleIndex != -1) { - - QVector roles; - roles << roleIndex; - - emitItemsChanged(index, 1, roles); - } - } -} - -/*! - \qmlmethod QtQml2::ListModel::sync() - - Writes any unsaved changes to the list model after it has been modified - from a worker script. -*/ -void QQmlListModel::sync() -{ - // This is just a dummy method to make it look like sync() exists in - // ListModel (and not just QQmlListModelWorkerAgent) and to let - // us document sync(). - qmlInfo(this) << "List sync() can only be called from a WorkerScript"; -} - -bool QQmlListModelParser::compileProperty(const QQmlCustomParserProperty &prop, QList &instr, QByteArray &data) -{ - QList values = prop.assignedValues(); - for(int ii = 0; ii < values.count(); ++ii) { - const QVariant &value = values.at(ii); - - if(value.userType() == qMetaTypeId()) { - QQmlCustomParserNode node = - qvariant_cast(value); - - if (node.name() != listElementTypeName) { - const QMetaObject *mo = resolveType(node.name()); - if (mo != &QQmlListElement::staticMetaObject) { - error(node, QQmlListModel::tr("ListElement: cannot contain nested elements")); - return false; - } - listElementTypeName = node.name(); // cache right name for next time - } - - { - ListInstruction li; - li.type = ListInstruction::Push; - li.dataIdx = -1; - instr << li; - } - - QList props = node.properties(); - for(int jj = 0; jj < props.count(); ++jj) { - const QQmlCustomParserProperty &nodeProp = props.at(jj); - if (nodeProp.name().isEmpty()) { - error(nodeProp, QQmlListModel::tr("ListElement: cannot contain nested elements")); - return false; - } - if (nodeProp.name() == QStringLiteral("id")) { - error(nodeProp, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property")); - return false; - } - - ListInstruction li; - int ref = data.count(); - data.append(nodeProp.name().toUtf8()); - data.append('\0'); - li.type = ListInstruction::Set; - li.dataIdx = ref; - instr << li; - - if(!compileProperty(nodeProp, instr, data)) - return false; - - li.type = ListInstruction::Pop; - li.dataIdx = -1; - instr << li; - } - - { - ListInstruction li; - li.type = ListInstruction::Pop; - li.dataIdx = -1; - instr << li; - } - - } else { - - QQmlScript::Variant variant = - qvariant_cast(value); - - int ref = data.count(); - - QByteArray d; - d += char(variant.type()); // type tag - if (variant.isString()) { - d += variant.asString().toUtf8(); - } else if (variant.isNumber()) { - d += QByteArray::number(variant.asNumber(),'g',20); - } else if (variant.isBoolean()) { - d += char(variant.asBoolean()); - } else if (variant.isScript()) { - if (definesEmptyList(variant.asScript())) { - d[0] = char(QQmlScript::Variant::Invalid); // marks empty list - } else { - QByteArray script = variant.asScript().toUtf8(); - bool ok; - int v = evaluateEnum(script, &ok); - if (!ok) { - using namespace QQmlJS; - AST::Node *node = variant.asAST(); - AST::StringLiteral *literal = 0; - if (AST::CallExpression *callExpr = AST::cast(node)) { - if (AST::IdentifierExpression *idExpr = AST::cast(callExpr->base)) { - if (idExpr->name == QLatin1String("QT_TR_NOOP") || idExpr->name == QLatin1String("QT_TRID_NOOP")) { - if (callExpr->arguments && !callExpr->arguments->next) - literal = AST::cast(callExpr->arguments->expression); - if (!literal) { - error(prop, QQmlListModel::tr("ListElement: improperly specified %1").arg(idExpr->name.toString())); - return false; - } - } else if (idExpr->name == QLatin1String("QT_TRANSLATE_NOOP")) { - if (callExpr->arguments && callExpr->arguments->next && !callExpr->arguments->next->next) - literal = AST::cast(callExpr->arguments->next->expression); - if (!literal) { - error(prop, QQmlListModel::tr("ListElement: improperly specified QT_TRANSLATE_NOOP")); - return false; - } - } - } - } - - if (literal) { - d[0] = char(QQmlScript::Variant::String); - d += literal->value.toUtf8(); - } else { - error(prop, QQmlListModel::tr("ListElement: cannot use script for property value")); - return false; - } - } else { - d[0] = char(QQmlScript::Variant::Number); - d += QByteArray::number(v); - } - } - } - d.append('\0'); - data.append(d); - - ListInstruction li; - li.type = ListInstruction::Value; - li.dataIdx = ref; - instr << li; - } - } - - return true; -} - -QByteArray QQmlListModelParser::compile(const QList &customProps) -{ - QList instr; - QByteArray data; - listElementTypeName = QString(); // unknown - - for(int ii = 0; ii < customProps.count(); ++ii) { - const QQmlCustomParserProperty &prop = customProps.at(ii); - if(!prop.name().isEmpty()) { // isn't default property - error(prop, QQmlListModel::tr("ListModel: undefined property '%1'").arg(prop.name())); - return QByteArray(); - } - - if(!compileProperty(prop, instr, data)) { - return QByteArray(); - } - } - - int size = sizeof(ListModelData) + - instr.count() * sizeof(ListInstruction) + - data.count(); - - QByteArray rv; - rv.resize(size); - - ListModelData *lmd = (ListModelData *)rv.data(); - lmd->dataOffset = sizeof(ListModelData) + - instr.count() * sizeof(ListInstruction); - lmd->instrCount = instr.count(); - for (int ii = 0; ii < instr.count(); ++ii) - lmd->instructions()[ii] = instr.at(ii); - ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count()); - - return rv; -} - -void QQmlListModelParser::setCustomData(QObject *obj, const QByteArray &d) -{ - QQmlListModel *rv = static_cast(obj); - - QV8Engine *engine = QQmlEnginePrivate::getV8Engine(qmlEngine(rv)); - rv->m_engine = engine; - - const ListModelData *lmd = (const ListModelData *)d.constData(); - const char *data = ((const char *)lmd) + lmd->dataOffset; - - bool setRoles = false; - - QStack stack; - - for (int ii = 0; ii < lmd->instrCount; ++ii) { - const ListInstruction &instr = lmd->instructions()[ii]; - - switch(instr.type) { - case ListInstruction::Push: - { - Q_ASSERT(!rv->m_dynamicRoles); - - 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: - stack.pop(); - break; - - case ListInstruction::Value: - { - const DataStackElement &e0 = stack.at(stack.size() - 1); - DataStackElement &e1 = stack[stack.size() - 2]; - - QString name = e0.name; - QVariant value; - - switch (QQmlScript::Variant::Type(data[instr.dataIdx])) { - case QQmlScript::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 QQmlScript::Variant::Boolean: - value = bool(data[1 + instr.dataIdx]); - break; - case QQmlScript::Variant::Number: - value = QByteArray(data + 1 + instr.dataIdx).toDouble(); - break; - case QQmlScript::Variant::String: - value = QString::fromUtf8(data + 1 + instr.dataIdx); - break; - default: - Q_ASSERT("Format error in ListInstruction"); - } - - e1.model->setOrCreateProperty(e1.elementIndex, name, value); - setRoles = true; - } - break; - - case ListInstruction::Set: - { - DataStackElement e; - e.name = QString::fromUtf8(data + instr.dataIdx); - stack.push(e); - } - break; - } - } - - if (setRoles == false) - qmlInfo(obj) << "All ListElement declarations are empty, no roles can be created unless dynamicRoles is set."; -} - -bool QQmlListModelParser::definesEmptyList(const QString &s) -{ - if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { - for (int i=1; i -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - - -class QQmlListModelWorkerAgent; -class ListModel; -class ListLayout; - -class Q_QML_PRIVATE_EXPORT QQmlListModel : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(bool dynamicRoles READ dynamicRoles WRITE setDynamicRoles) - -public: - QQmlListModel(QObject *parent=0); - ~QQmlListModel(); - - QModelIndex index(int row, int column, const QModelIndex &parent) const; - int rowCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - QHash roleNames() const; - - QVariant data(int index, int role) const; - int count() const; - - Q_INVOKABLE void clear(); - Q_INVOKABLE void remove(QQmlV8Function *args); - Q_INVOKABLE void append(QQmlV8Function *args); - Q_INVOKABLE void insert(QQmlV8Function *args); - Q_INVOKABLE QQmlV8Handle get(int index) const; - Q_INVOKABLE void set(int index, const QQmlV8Handle &); - Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); - Q_INVOKABLE void move(int from, int to, int count); - Q_INVOKABLE void sync(); - - QQmlListModelWorkerAgent *agent(); - - bool dynamicRoles() const { return m_dynamicRoles; } - void setDynamicRoles(bool enableDynamicRoles); - -Q_SIGNALS: - void countChanged(); - -private: - friend class QQmlListModelParser; - friend class QQmlListModelWorkerAgent; - friend class ModelObject; - friend class ModelNodeMetaObject; - friend class ListModel; - friend class ListElement; - friend class DynamicRoleModelNode; - friend class DynamicRoleModelNodeMetaObject; - - // Constructs a flat list model for a worker agent - QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent); - QQmlListModel(const QQmlListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent=0); - - QV8Engine *engine() const; - - inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); } - - QQmlListModelWorkerAgent *m_agent; - mutable QV8Engine *m_engine; - bool m_mainThread; - bool m_primary; - - bool m_dynamicRoles; - - ListLayout *m_layout; - ListModel *m_listModel; - - QVector m_modelObjects; - QVector m_roles; - int m_uid; - - struct ElementSync - { - ElementSync() : src(0), target(0) {} - - DynamicRoleModelNode *src; - DynamicRoleModelNode *target; - }; - - int getUid() const { return m_uid; } - - static void sync(QQmlListModel *src, QQmlListModel *target, QHash *targetModelHash); - static QQmlListModel *createWithOwner(QQmlListModel *newOwner); - - void emitItemsChanged(int index, int count, const QVector &roles); - void emitItemsRemoved(int index, int count); - void emitItemsInserted(int index, int count); - void emitItemsMoved(int from, int to, int n); -}; - -// ### FIXME -class QQmlListElement : public QObject -{ -Q_OBJECT -}; - -class QQmlListModelParser : public QQmlCustomParser -{ -public: - QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} - QByteArray compile(const QList &); - void setCustomData(QObject *, const QByteArray &); - -private: - struct ListInstruction - { - enum { Push, Pop, Value, Set } type; - int dataIdx; - }; - struct ListModelData - { - int dataOffset; - int instrCount; - ListInstruction *instructions() const; - }; - bool compileProperty(const QQmlCustomParserProperty &prop, QList &instr, QByteArray &data); - - bool definesEmptyList(const QString &); - - QString listElementTypeName; - - struct DataStackElement - { - DataStackElement() : model(0), elementIndex(0) {} - - QString name; - ListModel *model; - int elementIndex; - }; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlListModel) -QML_DECLARE_TYPE(QQmlListElement) - -#endif // QQMLLISTMODEL_H diff --git a/src/qml/qml/qqmllistmodel_p_p.h b/src/qml/qml/qqmllistmodel_p_p.h deleted file mode 100644 index f2720c6c39..0000000000 --- a/src/qml/qml/qqmllistmodel_p_p.h +++ /dev/null @@ -1,378 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLLISTMODEL_P_P_H -#define QQMLLISTMODEL_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qqmllistmodel_p.h" -#include -#include "qqmlopenmetaobject_p.h" -#include - -QT_BEGIN_NAMESPACE - - -class DynamicRoleModelNode; - -class DynamicRoleModelNodeMetaObject : public QQmlOpenMetaObject -{ -public: - DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object); - ~DynamicRoleModelNodeMetaObject(); - - bool m_enabled; - -protected: - void propertyWrite(int index); - void propertyWritten(int index); - -private: - DynamicRoleModelNode *m_owner; -}; - -class DynamicRoleModelNode : public QObject -{ - Q_OBJECT -public: - DynamicRoleModelNode(QQmlListModel *owner, int uid); - - static DynamicRoleModelNode *create(const QVariantMap &obj, QQmlListModel *owner); - - void updateValues(const QVariantMap &object, QVector &roles); - - QVariant getValue(const QString &name) - { - return m_meta->value(name.toUtf8()); - } - - bool setValue(const QByteArray &name, const QVariant &val) - { - return m_meta->setValue(name, val); - } - - void setNodeUpdatesEnabled(bool enable) - { - m_meta->m_enabled = enable; - } - - int getUid() const - { - return m_uid; - } - - static void sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash *targetModelHash); - -private: - QQmlListModel *m_owner; - int m_uid; - DynamicRoleModelNodeMetaObject *m_meta; - - friend class DynamicRoleModelNodeMetaObject; -}; - -class ModelObject; - -class ModelNodeMetaObject : public QQmlOpenMetaObject -{ -public: - ModelNodeMetaObject(ModelObject *object); - ~ModelNodeMetaObject(); - - bool m_enabled; - -protected: - void propertyWritten(int index); - -private: - - ModelObject *m_obj; -}; - -class ModelObject : public QObject -{ - Q_OBJECT -public: - ModelObject(QQmlListModel *model, int elementIndex); - - void setValue(const QByteArray &name, const QVariant &val, bool force) - { - if (force) { - QVariant existingValue = m_meta->value(name); - if (existingValue.isValid()) { - (*m_meta)[name] = QVariant(); - } - } - m_meta->setValue(name, val); - } - - void setNodeUpdatesEnabled(bool enable) - { - m_meta->m_enabled = enable; - } - - void updateValues(); - void updateValues(const QVector &roles); - - QQmlListModel *m_model; - int m_elementIndex; - -private: - ModelNodeMetaObject *m_meta; -}; - -class ListLayout -{ -public: - ListLayout() : currentBlock(0), currentBlockOffset(0) {} - ListLayout(const ListLayout *other); - ~ListLayout(); - - class Role - { - public: - - Role() : type(Invalid), blockIndex(-1), blockOffset(-1), index(-1), subLayout(0) {} - explicit Role(const Role *other); - ~Role(); - - // This enum must be kept in sync with the roleTypeNames variable in qdeclarativelistmodel.cpp - enum DataType - { - Invalid = -1, - - String, - Number, - Bool, - List, - QObject, - VariantMap, - DateTime, - - MaxDataType - }; - - QString name; - DataType type; - int blockIndex; - int blockOffset; - int index; - ListLayout *subLayout; - }; - - const Role *getRoleOrCreate(const QString &key, const QVariant &data); - const Role &getRoleOrCreate(v8::Handle key, Role::DataType type); - const Role &getRoleOrCreate(const QString &key, Role::DataType type); - - const Role &getExistingRole(int index) { return *roles.at(index); } - const Role *getExistingRole(const QString &key); - const Role *getExistingRole(v8::Handle key); - - int roleCount() const { return roles.count(); } - - static void sync(ListLayout *src, ListLayout *target); - -private: - const Role &createRole(const QString &key, Role::DataType type); - - int currentBlock; - int currentBlockOffset; - QVector roles; - QStringHash roleHash; -}; - -class ListElement -{ -public: - - ListElement(); - ListElement(int existingUid); - ~ListElement(); - - static void sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash *targetModelHash); - - enum - { - BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelObject *) - }; - -private: - - void destroy(ListLayout *layout); - - int setVariantProperty(const ListLayout::Role &role, const QVariant &d); - - int setJsProperty(const ListLayout::Role &role, v8::Handle d, QV8Engine *eng); - - int setStringProperty(const ListLayout::Role &role, const QString &s); - int setDoubleProperty(const ListLayout::Role &role, double n); - int setBoolProperty(const ListLayout::Role &role, bool b); - int setListProperty(const ListLayout::Role &role, ListModel *m); - int setQObjectProperty(const ListLayout::Role &role, QObject *o); - int setVariantMapProperty(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng); - int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m); - int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt); - - void setStringPropertyFast(const ListLayout::Role &role, const QString &s); - void setDoublePropertyFast(const ListLayout::Role &role, double n); - void setBoolPropertyFast(const ListLayout::Role &role, bool b); - void setQObjectPropertyFast(const ListLayout::Role &role, QObject *o); - void setListPropertyFast(const ListLayout::Role &role, ListModel *m); - void setVariantMapFast(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng); - void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt); - - void clearProperty(const ListLayout::Role &role); - - QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV8Engine *eng); - ListModel *getListProperty(const ListLayout::Role &role); - QString *getStringProperty(const ListLayout::Role &role); - QObject *getQObjectProperty(const ListLayout::Role &role); - QQmlGuard *getGuardProperty(const ListLayout::Role &role); - QVariantMap *getVariantMapProperty(const ListLayout::Role &role); - QDateTime *getDateTimeProperty(const ListLayout::Role &role); - - inline char *getPropertyMemory(const ListLayout::Role &role); - - int getUid() const { return uid; } - - char data[BLOCK_SIZE]; - ListElement *next; - - int uid; - ModelObject *m_objectCache; - - friend class ListModel; -}; - -class ListModel -{ -public: - - ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid); - ~ListModel() {} - - void destroy(); - - int setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data); - int setExistingProperty(int uid, const QString &key, v8::Handle data, QV8Engine *eng); - - QVariant getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV8Engine *eng); - ListModel *getListProperty(int elementIndex, const ListLayout::Role &role); - - int roleCount() const - { - return m_layout->roleCount(); - } - - const ListLayout::Role &getExistingRole(int index) - { - return m_layout->getExistingRole(index); - } - - const ListLayout::Role &getOrCreateListRole(const QString &name) - { - return m_layout->getRoleOrCreate(name, ListLayout::Role::List); - } - - int elementCount() const - { - return elements.count(); - } - - void set(int elementIndex, v8::Handle object, QVector *roles, QV8Engine *eng); - void set(int elementIndex, v8::Handle object, QV8Engine *eng); - - int append(v8::Handle object, QV8Engine *eng); - void insert(int elementIndex, v8::Handle object, QV8Engine *eng); - - void clear(); - void remove(int index, int count); - - int appendElement(); - void insertElement(int index); - - void move(int from, int to, int n); - - int getUid() const { return m_uid; } - - static void sync(ListModel *src, ListModel *target, QHash *srcModelHash); - - ModelObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex); - -private: - QPODVector elements; - ListLayout *m_layout; - int m_uid; - - QQmlListModel *m_modelCache; - - struct ElementSync - { - ElementSync() : src(0), target(0) {} - - ListElement *src; - ListElement *target; - }; - - void newElement(int index); - - void updateCacheIndices(); - - friend class ListElement; - friend class QQmlListModelWorkerAgent; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(ListModel *); - -#endif // QQUICKLISTMODEL_P_P_H - diff --git a/src/qml/qml/qqmllistmodelworkeragent.cpp b/src/qml/qml/qqmllistmodelworkeragent.cpp deleted file mode 100644 index 9554e6d1e5..0000000000 --- a/src/qml/qml/qqmllistmodelworkeragent.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmllistmodelworkeragent_p.h" -#include "qqmllistmodel_p_p.h" -#include -#include -#include - -#include -#include -#include - - -QT_BEGIN_NAMESPACE - - -void QQmlListModelWorkerAgent::Data::clearChange(int uid) -{ - for (int i=0 ; i < changes.count() ; ++i) { - if (changes[i].modelUid == uid) { - changes.removeAt(i); - --i; - } - } -} - -void QQmlListModelWorkerAgent::Data::insertChange(int uid, int index, int count) -{ - Change c = { uid, Change::Inserted, index, count, 0, QVector() }; - changes << c; -} - -void QQmlListModelWorkerAgent::Data::removeChange(int uid, int index, int count) -{ - Change c = { uid, Change::Removed, index, count, 0, QVector() }; - changes << c; -} - -void QQmlListModelWorkerAgent::Data::moveChange(int uid, int index, int count, int to) -{ - Change c = { uid, Change::Moved, index, count, to, QVector() }; - changes << c; -} - -void QQmlListModelWorkerAgent::Data::changedChange(int uid, int index, int count, const QVector &roles) -{ - Change c = { uid, Change::Changed, index, count, 0, roles }; - changes << c; -} - -QQmlListModelWorkerAgent::QQmlListModelWorkerAgent(QQmlListModel *model) -: m_ref(1), m_orig(model), m_copy(new QQmlListModel(model, this)) -{ -} - -QQmlListModelWorkerAgent::~QQmlListModelWorkerAgent() -{ - mutex.lock(); - syncDone.wakeAll(); - mutex.unlock(); -} - -void QQmlListModelWorkerAgent::setV8Engine(QV8Engine *eng) -{ - m_copy->m_engine = eng; -} - -void QQmlListModelWorkerAgent::addref() -{ - m_ref.ref(); -} - -void QQmlListModelWorkerAgent::release() -{ - bool del = !m_ref.deref(); - - if (del) - deleteLater(); -} - -void QQmlListModelWorkerAgent::modelDestroyed() -{ - m_orig = 0; -} - -int QQmlListModelWorkerAgent::count() const -{ - return m_copy->count(); -} - -void QQmlListModelWorkerAgent::clear() -{ - m_copy->clear(); -} - -void QQmlListModelWorkerAgent::remove(QQmlV8Function *args) -{ - m_copy->remove(args); -} - -void QQmlListModelWorkerAgent::append(QQmlV8Function *args) -{ - m_copy->append(args); -} - -void QQmlListModelWorkerAgent::insert(QQmlV8Function *args) -{ - m_copy->insert(args); -} - -QQmlV8Handle QQmlListModelWorkerAgent::get(int index) const -{ - return m_copy->get(index); -} - -void QQmlListModelWorkerAgent::set(int index, const QQmlV8Handle &value) -{ - m_copy->set(index, value); -} - -void QQmlListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value) -{ - m_copy->setProperty(index, property, value); -} - -void QQmlListModelWorkerAgent::move(int from, int to, int count) -{ - m_copy->move(from, to, count); -} - -void QQmlListModelWorkerAgent::sync() -{ - Sync *s = new Sync; - s->data = data; - s->list = m_copy; - data.changes.clear(); - - mutex.lock(); - QCoreApplication::postEvent(this, s); - syncDone.wait(&mutex); - mutex.unlock(); -} - -bool QQmlListModelWorkerAgent::event(QEvent *e) -{ - if (e->type() == QEvent::User) { - bool cc = false; - QMutexLocker locker(&mutex); - if (m_orig) { - Sync *s = static_cast(e); - const QList &changes = s->data.changes; - - cc = m_orig->count() != s->list->count(); - - QHash targetModelDynamicHash; - QHash targetModelStaticHash; - - Q_ASSERT(m_orig->m_dynamicRoles == s->list->m_dynamicRoles); - if (m_orig->m_dynamicRoles) - QQmlListModel::sync(s->list, m_orig, &targetModelDynamicHash); - else - ListModel::sync(s->list->m_listModel, m_orig->m_listModel, &targetModelStaticHash); - - for (int ii = 0; ii < changes.count(); ++ii) { - const Change &change = changes.at(ii); - - QQmlListModel *model = 0; - if (m_orig->m_dynamicRoles) { - model = targetModelDynamicHash.value(change.modelUid); - } else { - ListModel *lm = targetModelStaticHash.value(change.modelUid); - if (lm) - model = lm->m_modelCache; - } - - if (model) { - switch (change.type) { - case Change::Inserted: - model->beginInsertRows( - QModelIndex(), change.index, change.index + change.count - 1); - model->endInsertRows(); - break; - case Change::Removed: - model->beginRemoveRows( - QModelIndex(), change.index, change.index + change.count - 1); - model->endRemoveRows(); - break; - case Change::Moved: - model->beginMoveRows( - QModelIndex(), - change.index, - change.index + change.count - 1, - QModelIndex(), - change.to > change.index ? change.to + change.count : change.to); - model->endMoveRows(); - break; - case Change::Changed: - emit model->dataChanged( - model->createIndex(change.index, 0), - model->createIndex(change.index + change.count - 1, 0), - change.roles); - break; - } - } - } - } - - syncDone.wakeAll(); - locker.unlock(); - - if (cc) - emit m_orig->countChanged(); - return true; - } - - return QObject::event(e); -} - -QT_END_NAMESPACE - diff --git a/src/qml/qml/qqmllistmodelworkeragent_p.h b/src/qml/qml/qqmllistmodelworkeragent_p.h deleted file mode 100644 index 614017069c..0000000000 --- a/src/qml/qml/qqmllistmodelworkeragent_p.h +++ /dev/null @@ -1,156 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKLISTMODELWORKERAGENT_P_H -#define QQUICKLISTMODELWORKERAGENT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -#include -#include - -#include - -QT_BEGIN_NAMESPACE - - -class QQmlListModel; - -class QQmlListModelWorkerAgent : public QObject -{ - Q_OBJECT - Q_PROPERTY(int count READ count) - -public: - QQmlListModelWorkerAgent(QQmlListModel *); - ~QQmlListModelWorkerAgent(); - void setV8Engine(QV8Engine *eng); - - void addref(); - void release(); - - int count() const; - - Q_INVOKABLE void clear(); - Q_INVOKABLE void remove(QQmlV8Function *args); - Q_INVOKABLE void append(QQmlV8Function *args); - Q_INVOKABLE void insert(QQmlV8Function *args); - Q_INVOKABLE QQmlV8Handle get(int index) const; - Q_INVOKABLE void set(int index, const QQmlV8Handle &); - Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); - Q_INVOKABLE void move(int from, int to, int count); - Q_INVOKABLE void sync(); - - struct VariantRef - { - VariantRef() : a(0) {} - VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); } - VariantRef(QQmlListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); } - ~VariantRef() { if (a) a->release(); } - - VariantRef &operator=(const VariantRef &o) { - if (o.a) o.a->addref(); - if (a) a->release(); a = o.a; - return *this; - } - - QQmlListModelWorkerAgent *a; - }; - void modelDestroyed(); -protected: - virtual bool event(QEvent *); - -private: - friend class QQuickWorkerScriptEnginePrivate; - friend class QQmlListModel; - - struct Change - { - int modelUid; - enum { Inserted, Removed, Moved, Changed } type; - int index; // Inserted/Removed/Moved/Changed - int count; // Inserted/Removed/Moved/Changed - int to; // Moved - QVector roles; - }; - - struct Data - { - QList changes; - - void clearChange(int uid); - void insertChange(int uid, int index, int count); - void removeChange(int uid, int index, int count); - void moveChange(int uid, int index, int count, int to); - void changedChange(int uid, int index, int count, const QVector &roles); - }; - Data data; - - struct Sync : public QEvent { - Sync() : QEvent(QEvent::User) {} - Data data; - QQmlListModel *list; - }; - - QAtomicInt m_ref; - QQmlListModel *m_orig; - QQmlListModel *m_copy; - QMutex mutex; - QWaitCondition syncDone; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QQmlListModelWorkerAgent::VariantRef) - -#endif // QQUICKLISTMODELWORKERAGENT_P_H - diff --git a/src/qml/qml/qqmltimer.cpp b/src/qml/qml/qqmltimer.cpp deleted file mode 100644 index a1cb8532f7..0000000000 --- a/src/qml/qml/qqmltimer.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmltimer_p.h" - -#include -#include "private/qpauseanimationjob_p.h" -#include - -#include - -QT_BEGIN_NAMESPACE - - - -class QQmlTimerPrivate : public QObjectPrivate, public QAnimationJobChangeListener -{ - Q_DECLARE_PUBLIC(QQmlTimer) -public: - QQmlTimerPrivate() - : interval(1000), running(false), repeating(false), triggeredOnStart(false) - , classBegun(false), componentComplete(false), firstTick(true) {} - - virtual void animationFinished(QAbstractAnimationJob *); - virtual void animationCurrentLoopChanged(QAbstractAnimationJob *) { Q_Q(QQmlTimer); q->ticked(); } - - int interval; - QPauseAnimationJob pause; - bool running : 1; - bool repeating : 1; - bool triggeredOnStart : 1; - bool classBegun : 1; - bool componentComplete : 1; - bool firstTick : 1; -}; - -/*! - \qmltype Timer - \instantiates QQmlTimer - \inqmlmodule QtQml 2 - \ingroup qtquick-interceptors - \brief Triggers a handler at a specified interval - - A Timer can be used to trigger an action either once, or repeatedly - at a given interval. - - Here is a Timer that shows the current date and time, and updates - the text every 500 milliseconds. It uses the JavaScript \c Date - object to access the current time. - - \qml - import QtQuick 2.0 - - Item { - Timer { - interval: 500; running: true; repeat: true - onTriggered: time.text = Date().toString() - } - - Text { id: time } - } - \endqml - - The Timer type is synchronized with the animation timer. Since the animation - timer is usually set to 60fps, the resolution of Timer will be - at best 16ms. - - If the Timer is running and one of its properties is changed, the - elapsed time will be reset. For example, if a Timer with interval of - 1000ms has its \e repeat property changed 500ms after starting, the - elapsed time will be reset to 0, and the Timer will be triggered - 1000ms later. - - \sa {declarative/toys/clocks}{Clocks example} -*/ - -QQmlTimer::QQmlTimer(QObject *parent) - : QObject(*(new QQmlTimerPrivate), parent) -{ - Q_D(QQmlTimer); - d->pause.addAnimationChangeListener(d, QAbstractAnimationJob::Completion | QAbstractAnimationJob::CurrentLoop); - d->pause.setLoopCount(1); - d->pause.setDuration(d->interval); -} - -/*! - \qmlproperty int QtQml2::Timer::interval - - Sets the \a interval between triggers, in milliseconds. - - The default interval is 1000 milliseconds. -*/ -void QQmlTimer::setInterval(int interval) -{ - Q_D(QQmlTimer); - if (interval != d->interval) { - d->interval = interval; - update(); - emit intervalChanged(); - } -} - -int QQmlTimer::interval() const -{ - Q_D(const QQmlTimer); - return d->interval; -} - -/*! - \qmlproperty bool QtQml2::Timer::running - - If set to true, starts the timer; otherwise stops the timer. - For a non-repeating timer, \a running is set to false after the - timer has been triggered. - - \a running defaults to false. - - \sa repeat -*/ -bool QQmlTimer::isRunning() const -{ - Q_D(const QQmlTimer); - return d->running; -} - -void QQmlTimer::setRunning(bool running) -{ - Q_D(QQmlTimer); - if (d->running != running) { - d->running = running; - d->firstTick = true; - emit runningChanged(); - update(); - } -} - -/*! - \qmlproperty bool QtQml2::Timer::repeat - - If \a repeat is true the timer is triggered repeatedly at the - specified interval; otherwise, the timer will trigger once at the - specified interval and then stop (i.e. running will be set to false). - - \a repeat defaults to false. - - \sa running -*/ -bool QQmlTimer::isRepeating() const -{ - Q_D(const QQmlTimer); - return d->repeating; -} - -void QQmlTimer::setRepeating(bool repeating) -{ - Q_D(QQmlTimer); - if (repeating != d->repeating) { - d->repeating = repeating; - update(); - emit repeatChanged(); - } -} - -/*! - \qmlproperty bool QtQml2::Timer::triggeredOnStart - - When a timer is started, the first trigger is usually after the specified - interval has elapsed. It is sometimes desirable to trigger immediately - when the timer is started; for example, to establish an initial - state. - - If \a triggeredOnStart is true, the timer is triggered immediately - when started, and subsequently at the specified interval. Note that if - \e repeat is set to false, the timer is triggered twice; once on start, - and again at the interval. - - \a triggeredOnStart defaults to false. - - \sa running -*/ -bool QQmlTimer::triggeredOnStart() const -{ - Q_D(const QQmlTimer); - return d->triggeredOnStart; -} - -void QQmlTimer::setTriggeredOnStart(bool triggeredOnStart) -{ - Q_D(QQmlTimer); - if (d->triggeredOnStart != triggeredOnStart) { - d->triggeredOnStart = triggeredOnStart; - update(); - emit triggeredOnStartChanged(); - } -} - -/*! - \qmlmethod QtQml2::Timer::start() - \brief Starts the timer - - If the timer is already running, calling this method has no effect. The - \c running property will be true following a call to \c start(). -*/ -void QQmlTimer::start() -{ - setRunning(true); -} - -/*! - \qmlmethod QtQml2::Timer::stop() - \brief Stops the timer - - If the timer is not running, calling this method has no effect. The - \c running property will be false following a call to \c stop(). -*/ -void QQmlTimer::stop() -{ - setRunning(false); -} - -/*! - \qmlmethod QtQml2::Timer::restart() - \brief Restarts the timer - - If the Timer is not running it will be started, otherwise it will be - stopped, reset to initial state and started. The \c running property - will be true following a call to \c restart(). -*/ -void QQmlTimer::restart() -{ - setRunning(false); - setRunning(true); -} - -void QQmlTimer::update() -{ - Q_D(QQmlTimer); - if (d->classBegun && !d->componentComplete) - return; - d->pause.stop(); - if (d->running) { - d->pause.setCurrentTime(0); - d->pause.setLoopCount(d->repeating ? -1 : 1); - d->pause.setDuration(d->interval); - d->pause.start(); - if (d->triggeredOnStart && d->firstTick) { - QCoreApplication::removePostedEvents(this, QEvent::MetaCall); - QMetaObject::invokeMethod(this, "ticked", Qt::QueuedConnection); - } - } -} - -void QQmlTimer::classBegin() -{ - Q_D(QQmlTimer); - d->classBegun = true; -} - -void QQmlTimer::componentComplete() -{ - Q_D(QQmlTimer); - d->componentComplete = true; - update(); -} - -/*! - \qmlsignal QtQml2::Timer::onTriggered() - - This handler is called when the Timer is triggered. -*/ -void QQmlTimer::ticked() -{ - Q_D(QQmlTimer); - if (d->running && (d->pause.currentTime() > 0 || (d->triggeredOnStart && d->firstTick))) - emit triggered(); - d->firstTick = false; -} - -void QQmlTimerPrivate::animationFinished(QAbstractAnimationJob *) -{ - Q_Q(QQmlTimer); - if (repeating || !running) - return; - running = false; - firstTick = false; - emit q->triggered(); - emit q->runningChanged(); -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltimer_p.h b/src/qml/qml/qqmltimer_p.h deleted file mode 100644 index c625522851..0000000000 --- a/src/qml/qml/qqmltimer_p.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLTIMER_H -#define QQMLTIMER_H - -#include - -#include - -#include - -QT_BEGIN_NAMESPACE - -class QQmlTimerPrivate; -class Q_QML_PRIVATE_EXPORT QQmlTimer : public QObject, public QQmlParserStatus -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlTimer) - Q_INTERFACES(QQmlParserStatus) - Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged) - Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) - Q_PROPERTY(bool repeat READ isRepeating WRITE setRepeating NOTIFY repeatChanged) - Q_PROPERTY(bool triggeredOnStart READ triggeredOnStart WRITE setTriggeredOnStart NOTIFY triggeredOnStartChanged) - Q_PROPERTY(QObject *parent READ parent CONSTANT) - -public: - QQmlTimer(QObject *parent=0); - - void setInterval(int interval); - int interval() const; - - bool isRunning() const; - void setRunning(bool running); - - bool isRepeating() const; - void setRepeating(bool repeating); - - bool triggeredOnStart() const; - void setTriggeredOnStart(bool triggeredOnStart); - -protected: - void classBegin(); - void componentComplete(); - -public Q_SLOTS: - void start(); - void stop(); - void restart(); - -Q_SIGNALS: - void triggered(); - void runningChanged(); - void intervalChanged(); - void repeatChanged(); - void triggeredOnStartChanged(); - -private: - void update(); - -private Q_SLOTS: - void ticked(); -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlTimer) - -#endif diff --git a/src/qml/qml/qquickworkerscript.cpp b/src/qml/qml/qquickworkerscript.cpp deleted file mode 100644 index b9b027f6ad..0000000000 --- a/src/qml/qml/qquickworkerscript.cpp +++ /dev/null @@ -1,740 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickworkerscript_p.h" -#include "qqmllistmodel_p.h" -#include "qqmllistmodelworkeragent_p.h" -#include "qqmlengine_p.h" -#include "qqmlexpression_p.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "qqmlnetworkaccessmanagerfactory.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -class WorkerDataEvent : public QEvent -{ -public: - enum Type { WorkerData = QEvent::User }; - - WorkerDataEvent(int workerId, const QByteArray &data); - virtual ~WorkerDataEvent(); - - int workerId() const; - QByteArray data() const; - -private: - int m_id; - QByteArray m_data; -}; - -class WorkerLoadEvent : public QEvent -{ -public: - enum Type { WorkerLoad = WorkerDataEvent::WorkerData + 1 }; - - WorkerLoadEvent(int workerId, const QUrl &url); - - int workerId() const; - QUrl url() const; - -private: - int m_id; - QUrl m_url; -}; - -class WorkerRemoveEvent : public QEvent -{ -public: - enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 }; - - WorkerRemoveEvent(int workerId); - - int workerId() const; - -private: - int m_id; -}; - -class WorkerErrorEvent : public QEvent -{ -public: - enum Type { WorkerError = WorkerRemoveEvent::WorkerRemove + 1 }; - - WorkerErrorEvent(const QQmlError &error); - - QQmlError error() const; - -private: - QQmlError m_error; -}; - -class QQuickWorkerScriptEnginePrivate : public QObject -{ - Q_OBJECT -public: - enum WorkerEventTypes { - WorkerDestroyEvent = QEvent::User + 100 - }; - - QQuickWorkerScriptEnginePrivate(QQmlEngine *eng); - - class WorkerEngine : public QV8Engine - { - public: - WorkerEngine(QQuickWorkerScriptEnginePrivate *parent); - ~WorkerEngine(); - - void init(); - virtual QNetworkAccessManager *networkAccessManager(); - - QQuickWorkerScriptEnginePrivate *p; - - v8::Local sendFunction(int id); - void callOnMessage(v8::Handle object, v8::Handle arg); - private: - v8::Persistent onmessage; - v8::Persistent createsend; - QNetworkAccessManager *accessManager; - }; - - WorkerEngine *workerEngine; - static QQuickWorkerScriptEnginePrivate *get(QV8Engine *e) { - return static_cast(e)->p; - } - - QQmlEngine *qmlengine; - - QMutex m_lock; - QWaitCondition m_wait; - - struct WorkerScript { - WorkerScript(); - ~WorkerScript(); - - int id; - QUrl source; - bool initialized; - QQuickWorkerScript *owner; - v8::Persistent object; - }; - - QHash workers; - v8::Handle getWorker(WorkerScript *); - - int m_nextId; - - static v8::Handle sendMessage(const v8::Arguments &args); - -signals: - void stopThread(); - -protected: - virtual bool event(QEvent *); - -private: - void processMessage(int, const QByteArray &); - void processLoad(int, const QUrl &); - void reportScriptException(WorkerScript *, const QQmlError &error); -}; - -QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent) -: QV8Engine(0), p(parent), accessManager(0) -{ -} - -QQuickWorkerScriptEnginePrivate::WorkerEngine::~WorkerEngine() -{ - qPersistentDispose(createsend); - qPersistentDispose(onmessage); - delete accessManager; -} - -void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() -{ - initQmlGlobalObject(); -#define CALL_ONMESSAGE_SCRIPT \ - "(function(object, message) { "\ - "var isfunction = false; "\ - "try { "\ - "isfunction = object.WorkerScript.onMessage instanceof Function; "\ - "} catch (e) {}" \ - "if (isfunction) "\ - "object.WorkerScript.onMessage(message); "\ - "})" - -#define SEND_MESSAGE_CREATE_SCRIPT \ - "(function(method, engine) { "\ - "return (function(id) { "\ - "return (function(message) { "\ - "if (arguments.length) method(engine, id, message); "\ - "}); "\ - "}); "\ - "})" - - v8::HandleScope handle_scope; - v8::Context::Scope scope(context()); - - { - v8::Local onmessagescript = v8::Script::New(v8::String::New(CALL_ONMESSAGE_SCRIPT)); - onmessage = qPersistentNew(v8::Handle::Cast(onmessagescript->Run())); - } - { - v8::Local createsendscript = v8::Script::New(v8::String::New(SEND_MESSAGE_CREATE_SCRIPT)); - v8::Local createsendconstructor = v8::Local::Cast(createsendscript->Run()); - - v8::Handle args[] = { - V8FUNCTION(QQuickWorkerScriptEnginePrivate::sendMessage, this) - }; - v8::Local createsendvalue = createsendconstructor->Call(global(), 1, args); - - createsend = qPersistentNew(v8::Handle::Cast(createsendvalue)); - } -} - -// Requires handle and context scope -v8::Local QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(int id) -{ - v8::Handle args[] = { v8::Integer::New(id) }; - return v8::Local::Cast(createsend->Call(global(), 1, args)); -} - -// Requires handle and context scope -void QQuickWorkerScriptEnginePrivate::WorkerEngine::callOnMessage(v8::Handle object, - v8::Handle arg) -{ - v8::Handle args[] = { object, arg }; - onmessage->Call(global(), 2, args); -} - -QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerEngine::networkAccessManager() -{ - if (!accessManager) { - if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) { - accessManager = p->qmlengine->networkAccessManagerFactory()->create(p); - } else { - accessManager = new QNetworkAccessManager(p); - } - } - return accessManager; -} - -QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine) -: workerEngine(0), qmlengine(engine), m_nextId(0) -{ -} - -v8::Handle QQuickWorkerScriptEnginePrivate::sendMessage(const v8::Arguments &args) -{ - WorkerEngine *engine = (WorkerEngine*)V8ENGINE(); - - int id = args[1]->Int32Value(); - - QByteArray data = QV8Worker::serialize(args[2], engine); - - QMutexLocker locker(&engine->p->m_lock); - WorkerScript *script = engine->p->workers.value(id); - if (!script) - return v8::Undefined(); - - if (script->owner) - QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data)); - - return v8::Undefined(); -} - -// Requires handle scope and context scope -v8::Handle QQuickWorkerScriptEnginePrivate::getWorker(WorkerScript *script) -{ - if (!script->initialized) { - script->initialized = true; - - script->object = qPersistentNew(workerEngine->contextWrapper()->urlScope(script->source)); - - workerEngine->contextWrapper()->setReadOnly(script->object, false); - - v8::Local api = v8::Object::New(); - api->Set(v8::String::New("sendMessage"), workerEngine->sendFunction(script->id)); - - script->object->Set(v8::String::New("WorkerScript"), api); - - workerEngine->contextWrapper()->setReadOnly(script->object, true); - } - - return script->object; -} - -bool QQuickWorkerScriptEnginePrivate::event(QEvent *event) -{ - if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { - WorkerDataEvent *workerEvent = static_cast(event); - processMessage(workerEvent->workerId(), workerEvent->data()); - return true; - } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) { - WorkerLoadEvent *workerEvent = static_cast(event); - processLoad(workerEvent->workerId(), workerEvent->url()); - return true; - } else if (event->type() == (QEvent::Type)WorkerDestroyEvent) { - emit stopThread(); - return true; - } else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) { - WorkerRemoveEvent *workerEvent = static_cast(event); - workers.remove(workerEvent->workerId()); - return true; - } else { - return QObject::event(event); - } -} - -void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &data) -{ - WorkerScript *script = workers.value(id); - if (!script) - return; - - v8::HandleScope handle_scope; - v8::Context::Scope scope(workerEngine->context()); - - v8::Handle value = QV8Worker::deserialize(data, workerEngine); - - v8::TryCatch tc; - workerEngine->callOnMessage(script->object, value); - - if (tc.HasCaught()) { - QQmlError error; - QQmlExpressionPrivate::exceptionToError(tc.Message(), error); - reportScriptException(script, error); - } -} - -void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) -{ - if (url.isRelative()) - return; - - QString fileName = QQmlFile::urlToLocalFileOrQrc(url); - - QFile f(fileName); - if (f.open(QIODevice::ReadOnly)) { - QByteArray data = f.readAll(); - QString sourceCode = QString::fromUtf8(data); - QQmlScript::Parser::extractPragmas(sourceCode); - - v8::HandleScope handle_scope; - v8::Context::Scope scope(workerEngine->context()); - - WorkerScript *script = workers.value(id); - if (!script) - return; - script->source = url; - v8::Handle activation = getWorker(script); - if (activation.IsEmpty()) - return; - - // XXX ??? - // workerEngine->baseUrl = url; - - v8::TryCatch tc; - v8::Local program = workerEngine->qmlModeCompile(sourceCode, url.toString()); - - if (!tc.HasCaught()) - program->Run(activation); - - if (tc.HasCaught()) { - QQmlError error; - QQmlExpressionPrivate::exceptionToError(tc.Message(), error); - reportScriptException(script, error); - } - } else { - qWarning().nospace() << "WorkerScript: Cannot find source file " << url.toString(); - } -} - -void QQuickWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script, - const QQmlError &error) -{ - QQuickWorkerScriptEnginePrivate *p = QQuickWorkerScriptEnginePrivate::get(workerEngine); - - QMutexLocker locker(&p->m_lock); - if (script->owner) - QCoreApplication::postEvent(script->owner, new WorkerErrorEvent(error)); -} - -WorkerDataEvent::WorkerDataEvent(int workerId, const QByteArray &data) -: QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data) -{ -} - -WorkerDataEvent::~WorkerDataEvent() -{ -} - -int WorkerDataEvent::workerId() const -{ - return m_id; -} - -QByteArray WorkerDataEvent::data() const -{ - return m_data; -} - -WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url) -: QEvent((QEvent::Type)WorkerLoad), m_id(workerId), m_url(url) -{ -} - -int WorkerLoadEvent::workerId() const -{ - return m_id; -} - -QUrl WorkerLoadEvent::url() const -{ - return m_url; -} - -WorkerRemoveEvent::WorkerRemoveEvent(int workerId) -: QEvent((QEvent::Type)WorkerRemove), m_id(workerId) -{ -} - -int WorkerRemoveEvent::workerId() const -{ - return m_id; -} - -WorkerErrorEvent::WorkerErrorEvent(const QQmlError &error) -: QEvent((QEvent::Type)WorkerError), m_error(error) -{ -} - -QQmlError WorkerErrorEvent::error() const -{ - return m_error; -} - -QQuickWorkerScriptEngine::QQuickWorkerScriptEngine(QQmlEngine *parent) -: QThread(parent), d(new QQuickWorkerScriptEnginePrivate(parent)) -{ - d->m_lock.lock(); - connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection); - start(QThread::LowestPriority); - d->m_wait.wait(&d->m_lock); - d->moveToThread(this); - d->m_lock.unlock(); -} - -QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine() -{ - d->m_lock.lock(); - QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QQuickWorkerScriptEnginePrivate::WorkerDestroyEvent)); - d->m_lock.unlock(); - - //We have to force to cleanup the main thread's event queue here - //to make sure the main GUI release all pending locks/wait conditions which - //some worker script/agent are waiting for (QQmlListModelWorkerAgent::sync() for example). - while (!isFinished()) { - // We can't simply wait here, because the worker thread will not terminate - // until the main thread processes the last data event it generates - QCoreApplication::processEvents(); - yieldCurrentThread(); - } - - d->deleteLater(); -} - -QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript() -: id(-1), initialized(false), owner(0) -{ -} - -QQuickWorkerScriptEnginePrivate::WorkerScript::~WorkerScript() -{ - qPersistentDispose(object); -} - -int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner) -{ - typedef QQuickWorkerScriptEnginePrivate::WorkerScript WorkerScript; - WorkerScript *script = new WorkerScript; - - script->id = d->m_nextId++; - script->owner = owner; - - d->m_lock.lock(); - d->workers.insert(script->id, script); - d->m_lock.unlock(); - - return script->id; -} - -void QQuickWorkerScriptEngine::removeWorkerScript(int id) -{ - QQuickWorkerScriptEnginePrivate::WorkerScript* script = d->workers.value(id); - if (script) { - script->owner = 0; - QCoreApplication::postEvent(d, new WorkerRemoveEvent(id)); - } -} - -void QQuickWorkerScriptEngine::executeUrl(int id, const QUrl &url) -{ - QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url)); -} - -void QQuickWorkerScriptEngine::sendMessage(int id, const QByteArray &data) -{ - QCoreApplication::postEvent(d, new WorkerDataEvent(id, data)); -} - -void QQuickWorkerScriptEngine::run() -{ - d->m_lock.lock(); - - d->workerEngine = new QQuickWorkerScriptEnginePrivate::WorkerEngine(d); - d->workerEngine->init(); - - d->m_wait.wakeAll(); - - d->m_lock.unlock(); - - exec(); - - qDeleteAll(d->workers); - d->workers.clear(); - - delete d->workerEngine; d->workerEngine = 0; -} - - -/*! - \qmltype WorkerScript - \instantiates QQuickWorkerScript - \ingroup qtquick-threading - \inqmlmodule QtQuick 2 - \brief Enables the use of threads in a Qt Quick application - - Use WorkerScript to run operations in a new thread. - This is useful for running operations in the background so - that the main GUI thread is not blocked. - - Messages can be passed between the new thread and the parent thread - using \l sendMessage() and the \l {WorkerScript::onMessage}{onMessage()} handler. - - An example: - - \snippet qml/workerscript/workerscript.qml 0 - - The above worker script specifies a JavaScript file, "script.js", that handles - the operations to be performed in the new thread. Here is \c script.js: - - \quotefile qml/workerscript/script.js - - When the user clicks anywhere within the rectangle, \c sendMessage() is - called, triggering the \tt WorkerScript.onMessage() handler in - \tt script.js. This in turn sends a reply message that is then received - by the \tt onMessage() handler of \tt myWorker. - - - \section3 Restrictions - - Since the \c WorkerScript.onMessage() function is run in a separate thread, the - JavaScript file is evaluated in a context separate from the main QML engine. This means - that unlike an ordinary JavaScript file that is imported into QML, the \c script.js - in the above example cannot access the properties, methods or other attributes - of the QML item, nor can it access any context properties set on the QML object - through QQmlContext. - - Additionally, there are restrictions on the types of values that can be passed to and - from the worker script. See the sendMessage() documentation for details. - - Worker script can not use \l {qtqml-javascript-imports.html}{.import} syntax. - - \sa {declarative/threading/workerscript}{WorkerScript example}, - {declarative/threading/threadedlistmodel}{Threaded ListModel example} -*/ -QQuickWorkerScript::QQuickWorkerScript(QObject *parent) -: QObject(parent), m_engine(0), m_scriptId(-1), m_componentComplete(true) -{ -} - -QQuickWorkerScript::~QQuickWorkerScript() -{ - if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId); -} - -/*! - \qmlproperty url WorkerScript::source - - This holds the url of the JavaScript file that implements the - \tt WorkerScript.onMessage() handler for threaded operations. -*/ -QUrl QQuickWorkerScript::source() const -{ - return m_source; -} - -void QQuickWorkerScript::setSource(const QUrl &source) -{ - if (m_source == source) - return; - - m_source = source; - - if (engine()) - m_engine->executeUrl(m_scriptId, m_source); - - emit sourceChanged(); -} - -/*! - \qmlmethod WorkerScript::sendMessage(jsobject message) - - Sends the given \a message to a worker script handler in another - thread. The other worker script handler can receive this message - through the onMessage() handler. - - The \c message object may only contain values of the following - types: - - \list - \li boolean, number, string - \li JavaScript objects and arrays - \li ListModel objects (any other type of QObject* is not allowed) - \endlist - - All objects and arrays are copied to the \c message. With the exception - of ListModel objects, any modifications by the other thread to an object - passed in \c message will not be reflected in the original object. -*/ -void QQuickWorkerScript::sendMessage(QQmlV8Function *args) -{ - if (!engine()) { - qWarning("QQuickWorkerScript: Attempt to send message before WorkerScript establishment"); - return; - } - - v8::Handle argument = v8::Undefined(); - if (args->Length() != 0) - argument = (*args)[0]; - - m_engine->sendMessage(m_scriptId, QV8Worker::serialize(argument, args->engine())); -} - -void QQuickWorkerScript::classBegin() -{ - m_componentComplete = false; -} - -QQuickWorkerScriptEngine *QQuickWorkerScript::engine() -{ - if (m_engine) return m_engine; - if (m_componentComplete) { - QQmlEngine *engine = qmlEngine(this); - if (!engine) { - qWarning("QQuickWorkerScript: engine() called without qmlEngine() set"); - return 0; - } - - m_engine = QQmlEnginePrivate::get(engine)->getWorkerScriptEngine(); - m_scriptId = m_engine->registerWorkerScript(this); - - if (m_source.isValid()) - m_engine->executeUrl(m_scriptId, m_source); - - return m_engine; - } - return 0; -} - -void QQuickWorkerScript::componentComplete() -{ - m_componentComplete = true; - engine(); // Get it started now. -} - -/*! - \qmlsignal WorkerScript::onMessage(jsobject msg) - - This handler is called when a message \a msg is received from a worker - script in another thread through a call to sendMessage(). -*/ - -bool QQuickWorkerScript::event(QEvent *event) -{ - if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { - QQmlEngine *engine = qmlEngine(this); - if (engine) { - WorkerDataEvent *workerEvent = static_cast(event); - QV8Engine *v8engine = QQmlEnginePrivate::get(engine)->v8engine(); - v8::HandleScope handle_scope; - v8::Context::Scope scope(v8engine->context()); - v8::Handle value = QV8Worker::deserialize(workerEvent->data(), v8engine); - emit message(QQmlV8Handle::fromHandle(value)); - } - return true; - } else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) { - WorkerErrorEvent *workerEvent = static_cast(event); - QQmlEnginePrivate::warning(qmlEngine(this), workerEvent->error()); - return true; - } else { - return QObject::event(event); - } -} - -QT_END_NAMESPACE - -#include - diff --git a/src/qml/qml/qquickworkerscript_p.h b/src/qml/qml/qquickworkerscript_p.h deleted file mode 100644 index d568e58d0e..0000000000 --- a/src/qml/qml/qquickworkerscript_p.h +++ /dev/null @@ -1,126 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKWORKERSCRIPT_P_H -#define QQUICKWORKERSCRIPT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qqml.h" -#include "qqmlparserstatus.h" - -#include -#include -#include - -QT_BEGIN_NAMESPACE - - -class QQuickWorkerScript; -class QQuickWorkerScriptEnginePrivate; -class QQuickWorkerScriptEngine : public QThread -{ -Q_OBJECT -public: - QQuickWorkerScriptEngine(QQmlEngine *parent = 0); - virtual ~QQuickWorkerScriptEngine(); - - int registerWorkerScript(QQuickWorkerScript *); - void removeWorkerScript(int); - void executeUrl(int, const QUrl &); - void sendMessage(int, const QByteArray &); - -protected: - virtual void run(); - -private: - QQuickWorkerScriptEnginePrivate *d; -}; - -class QQmlV8Function; -class QQmlV8Handle; -class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus -{ - Q_OBJECT - Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) - - Q_INTERFACES(QQmlParserStatus) -public: - QQuickWorkerScript(QObject *parent = 0); - virtual ~QQuickWorkerScript(); - - QUrl source() const; - void setSource(const QUrl &); - -public slots: - void sendMessage(QQmlV8Function*); - -signals: - void sourceChanged(); - void message(const QQmlV8Handle &messageObject); - -protected: - virtual void classBegin(); - virtual void componentComplete(); - virtual bool event(QEvent *); - -private: - QQuickWorkerScriptEngine *engine(); - QQuickWorkerScriptEngine *m_engine; - int m_scriptId; - QUrl m_source; - bool m_componentComplete; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickWorkerScript) - -#endif // QQUICKWORKERSCRIPT_P_H diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp new file mode 100644 index 0000000000..fcb3079891 --- /dev/null +++ b/src/qml/types/qqmlbind.cpp @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlbind_p.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QQmlBindPrivate : public QObjectPrivate +{ +public: + QQmlBindPrivate() : componentComplete(true), obj(0), prevBind(0) {} + ~QQmlBindPrivate() { if (prevBind) prevBind->destroy(); } + + QQmlNullableValue when; + bool componentComplete; + QQmlGuard obj; + QString propName; + QQmlNullableValue value; + QQmlProperty prop; + QQmlAbstractBinding *prevBind; +}; + + +/*! + \qmltype Binding + \instantiates QQmlBind + \inqmlmodule QtQml 2 + \ingroup qtquick-interceptors + \brief Enables the arbitrary creation of property bindings + + \section1 Binding to an inaccessible property + + Sometimes it is necessary to bind to a property of an object that wasn't + directly instantiated by QML - generally a property of a class exported + to QML by C++. In these cases, regular property binding doesn't work. Binding + allows you to bind any value to any property. + + For example, imagine a C++ application that maps an "app.enteredText" + property into QML. You could use Binding to update the enteredText property + like this. + \code + TextEdit { id: myTextField; text: "Please type here..." } + Binding { target: app; property: "enteredText"; value: myTextField.text } + \endcode + Whenever the text in the TextEdit is updated, the C++ property will be + updated also. + + \section1 "Single-branch" conditional binding + + In some circumstances you may want to control the value of a property + only when a certain condition is true (and relinquish control in all + other circumstances). This often isn't possible to accomplish with a direct + binding, as you need to supply values for all possible branches. + + \code + // produces warning: "Unable to assign [undefined] to double value" + value: if (mouse.pressed) mouse.mouseX + \endcode + + The above example will produce a warning whenever we release the mouse, as the value + of the binding is undefined when the mouse isn't pressed. We can use the Binding + type to rewrite the above code and avoid the warning. + + \qml + Binding on value { + when: mouse.pressed + value: mouse.mouseX + } + \endqml + + The Binding type will also restore any previously set direct bindings on + the property. In that sense, it functions much like a simplified State. + + \qml + // this is equivalent to the above Binding + State { + name: "pressed" + when: mouse.pressed + PropertyChanges { + target: obj + value: mouse.mouseX + } + } + \endqml + + If the binding target or binding property is changed, the bound value is + immediately pushed onto the new target. + + \sa QtQml +*/ +QQmlBind::QQmlBind(QObject *parent) + : QObject(*(new QQmlBindPrivate), parent) +{ +} + +QQmlBind::~QQmlBind() +{ +} + +/*! + \qmlproperty bool QtQml2::Binding::when + + This property holds when the binding is active. + This should be set to an expression that evaluates to true when you want the binding to be active. + + \code + Binding { + target: contactName; property: 'text' + value: name; when: list.ListView.isCurrentItem + } + \endcode + + When the binding becomes inactive again, any direct bindings that were previously + set on the property will be restored. +*/ +bool QQmlBind::when() const +{ + Q_D(const QQmlBind); + return d->when; +} + +void QQmlBind::setWhen(bool v) +{ + Q_D(QQmlBind); + if (!d->when.isNull && d->when == v) + return; + + d->when = v; + eval(); +} + +/*! + \qmlproperty Object QtQml2::Binding::target + + The object to be updated. +*/ +QObject *QQmlBind::object() +{ + Q_D(const QQmlBind); + return d->obj; +} + +void QQmlBind::setObject(QObject *obj) +{ + Q_D(QQmlBind); + if (d->obj && d->when.isValid() && d->when) { + /* if we switch the object at runtime, we need to restore the + previous binding on the old object before continuing */ + d->when = false; + eval(); + d->when = true; + } + d->obj = obj; + if (d->componentComplete) + d->prop = QQmlProperty(d->obj, d->propName); + eval(); +} + +/*! + \qmlproperty string QtQml2::Binding::property + + The property to be updated. +*/ +QString QQmlBind::property() const +{ + Q_D(const QQmlBind); + return d->propName; +} + +void QQmlBind::setProperty(const QString &p) +{ + Q_D(QQmlBind); + if (!d->propName.isEmpty() && d->when.isValid() && d->when) { + /* if we switch the property name at runtime, we need to restore the + previous binding on the old object before continuing */ + d->when = false; + eval(); + d->when = true; + } + d->propName = p; + if (d->componentComplete) + d->prop = QQmlProperty(d->obj, d->propName); + eval(); +} + +/*! + \qmlproperty any QtQml2::Binding::value + + The value to be set on the target object and property. This can be a + constant (which isn't very useful), or a bound expression. +*/ +QVariant QQmlBind::value() const +{ + Q_D(const QQmlBind); + return d->value.value; +} + +void QQmlBind::setValue(const QVariant &v) +{ + Q_D(QQmlBind); + d->value = v; + eval(); +} + +void QQmlBind::setTarget(const QQmlProperty &p) +{ + Q_D(QQmlBind); + d->prop = p; +} + +void QQmlBind::classBegin() +{ + Q_D(QQmlBind); + d->componentComplete = false; +} + +void QQmlBind::componentComplete() +{ + Q_D(QQmlBind); + d->componentComplete = true; + if (!d->prop.isValid()) + d->prop = QQmlProperty(d->obj, d->propName); + eval(); +} + +void QQmlBind::eval() +{ + Q_D(QQmlBind); + if (!d->prop.isValid() || d->value.isNull || !d->componentComplete) + return; + + if (d->when.isValid()) { + if (!d->when) { + //restore any previous binding + if (d->prevBind) { + QQmlAbstractBinding *tmp = d->prevBind; + d->prevBind = 0; + tmp = QQmlPropertyPrivate::setBinding(d->prop, tmp); + if (tmp) //should this ever be true? + tmp->destroy(); + } + return; + } + + //save any set binding for restoration + QQmlAbstractBinding *tmp; + tmp = QQmlPropertyPrivate::setBinding(d->prop, 0); + if (tmp && d->prevBind) + tmp->destroy(); + else if (!d->prevBind) + d->prevBind = tmp; + } + + d->prop.write(d->value.value); +} + +QT_END_NAMESPACE diff --git a/src/qml/types/qqmlbind_p.h b/src/qml/types/qqmlbind_p.h new file mode 100644 index 0000000000..1e29c257f0 --- /dev/null +++ b/src/qml/types/qqmlbind_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLBIND_H +#define QQMLBIND_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QQmlBindPrivate; +class Q_AUTOTEST_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlBind) + Q_INTERFACES(QQmlParserStatus) + Q_INTERFACES(QQmlPropertyValueSource) + Q_PROPERTY(QObject *target READ object WRITE setObject) + Q_PROPERTY(QString property READ property WRITE setProperty) + Q_PROPERTY(QVariant value READ value WRITE setValue) + Q_PROPERTY(bool when READ when WRITE setWhen) + +public: + QQmlBind(QObject *parent=0); + ~QQmlBind(); + + bool when() const; + void setWhen(bool); + + QObject *object(); + void setObject(QObject *); + + QString property() const; + void setProperty(const QString &); + + QVariant value() const; + void setValue(const QVariant &); + +protected: + virtual void setTarget(const QQmlProperty &); + virtual void classBegin(); + virtual void componentComplete(); + +private: + void eval(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlBind) + +#endif diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp new file mode 100644 index 0000000000..286933e557 --- /dev/null +++ b/src/qml/types/qqmlconnections.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlconnections_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QQmlConnectionsPrivate : public QObjectPrivate +{ +public: + QQmlConnectionsPrivate() : target(0), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {} + + QList boundsignals; + QObject *target; + + bool targetSet; + bool ignoreUnknownSignals; + bool componentcomplete; + + QByteArray data; +}; + +/*! + \qmltype Connections + \instantiates QQmlConnections + \inqmlmodule QtQml 2 + \ingroup qtquick-interceptors + \brief Describes generalized connections to signals + + A Connections object creates a connection to a QML signal. + + When connecting to signals in QML, the usual way is to create an + "on" handler that reacts when a signal is received, like this: + + \qml + MouseArea { + onClicked: { foo(parameters) } + } + \endqml + + However, it is not possible to connect to a signal in this way in some + cases, such as when: + + \list + \li Multiple connections to the same signal are required + \li Creating connections outside the scope of the signal sender + \li Connecting to targets not defined in QML + \endlist + + When any of these are needed, the Connections type can be used instead. + + For example, the above code can be changed to use a Connections object, + like this: + + \qml + MouseArea { + Connections { + onClicked: foo(parameters) + } + } + \endqml + + More generally, the Connections object can be a child of some object other than + the sender of the signal: + + \qml + MouseArea { + id: area + } + // ... + \endqml + \qml + Connections { + target: area + onClicked: foo(parameters) + } + \endqml + + \sa QtQml +*/ +QQmlConnections::QQmlConnections(QObject *parent) : + QObject(*(new QQmlConnectionsPrivate), parent) +{ +} + +QQmlConnections::~QQmlConnections() +{ +} + +/*! + \qmlproperty Object QtQml2::Connections::target + This property holds the object that sends the signal. + + If this property is not set, the \c target defaults to the parent of the Connection. + + If set to null, no connection is made and any signal handlers are ignored + until the target is not null. +*/ +QObject *QQmlConnections::target() const +{ + Q_D(const QQmlConnections); + return d->targetSet ? d->target : parent(); +} + +class QQmlBoundSignalDeleter : public QObject +{ +public: + QQmlBoundSignalDeleter(QQmlBoundSignal *signal) : m_signal(signal) { m_signal->removeFromObject(); } + ~QQmlBoundSignalDeleter() { delete m_signal; } + +private: + QQmlBoundSignal *m_signal; +}; + +void QQmlConnections::setTarget(QObject *obj) +{ + Q_D(QQmlConnections); + d->targetSet = true; // even if setting to 0, it is *set* + if (d->target == obj) + return; + foreach (QQmlBoundSignal *s, d->boundsignals) { + // It is possible that target is being changed due to one of our signal + // handlers -> use deleteLater(). + if (s->isEvaluating()) + (new QQmlBoundSignalDeleter(s))->deleteLater(); + else + delete s; + } + d->boundsignals.clear(); + d->target = obj; + connectSignals(); + emit targetChanged(); +} + +/*! + \qmlproperty bool QtQml2::Connections::ignoreUnknownSignals + + Normally, a connection to a non-existent signal produces runtime errors. + + If this property is set to \c true, such errors are ignored. + This is useful if you intend to connect to different types of objects, handling + a different set of signals for each object. +*/ +bool QQmlConnections::ignoreUnknownSignals() const +{ + Q_D(const QQmlConnections); + return d->ignoreUnknownSignals; +} + +void QQmlConnections::setIgnoreUnknownSignals(bool ignore) +{ + Q_D(QQmlConnections); + d->ignoreUnknownSignals = ignore; +} + + + +QByteArray +QQmlConnectionsParser::compile(const QList &props) +{ + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + for(int ii = 0; ii < props.count(); ++ii) + { + QString propName = props.at(ii).name(); + int propLine = props.at(ii).location().line; + int propColumn = props.at(ii).location().column; + + if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) { + error(props.at(ii), QQmlConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); + return QByteArray(); + } + + QList values = props.at(ii).assignedValues(); + + for (int i = 0; i < values.count(); ++i) { + const QVariant &value = values.at(i); + + if (value.userType() == qMetaTypeId()) { + error(props.at(ii), QQmlConnections::tr("Connections: nested objects not allowed")); + return QByteArray(); + } else if (value.userType() == qMetaTypeId()) { + error(props.at(ii), QQmlConnections::tr("Connections: syntax error")); + return QByteArray(); + } else { + QQmlScript::Variant v = qvariant_cast(value); + if (v.isScript()) { + ds << propName; + ds << rewriteSignalHandler(v, propName).toUtf8(); + ds << propLine; + ds << propColumn; + } else { + error(props.at(ii), QQmlConnections::tr("Connections: script expected")); + return QByteArray(); + } + } + } + } + + return rv; +} + +void QQmlConnectionsParser::setCustomData(QObject *object, + const QByteArray &data) +{ + QQmlConnectionsPrivate *p = + static_cast(QObjectPrivate::get(object)); + p->data = data; +} + + +void QQmlConnections::connectSignals() +{ + Q_D(QQmlConnections); + if (!d->componentcomplete || (d->targetSet && !target())) + return; + + QDataStream ds(d->data); + while (!ds.atEnd()) { + QString propName; + ds >> propName; + QByteArray script; + ds >> script; + int line; + ds >> line; + int column; + ds >> column; + + QQmlProperty prop(target(), propName); + if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) { + int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex(); + QQmlBoundSignal *signal = + new QQmlBoundSignal(target(), signalIndex, this, qmlEngine(this)); + + QString location; + QQmlContextData *ctxtdata = 0; + QQmlData *ddata = QQmlData::get(this); + if (ddata) { + ctxtdata = ddata->outerContext; + if (ctxtdata && !ctxtdata->url.isEmpty()) + location = ddata->outerContext->urlString; + } + + QQmlBoundSignalExpression *expression = ctxtdata ? + new QQmlBoundSignalExpression(target(), signalIndex, + ctxtdata, this, script, + true, location, line, column) : 0; + signal->takeExpression(expression); + d->boundsignals += signal; + } else { + if (!d->ignoreUnknownSignals) + qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName); + } + } +} + +void QQmlConnections::classBegin() +{ + Q_D(QQmlConnections); + d->componentcomplete=false; +} + +void QQmlConnections::componentComplete() +{ + Q_D(QQmlConnections); + d->componentcomplete=true; + connectSignals(); +} + +QT_END_NAMESPACE diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h new file mode 100644 index 0000000000..9bc668e5f4 --- /dev/null +++ b/src/qml/types/qqmlconnections_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLCONNECTIONS_H +#define QQMLCONNECTIONS_H + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QQmlBoundSignal; +class QQmlContext; +class QQmlConnectionsPrivate; +class Q_AUTOTEST_EXPORT QQmlConnections : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlConnections) + + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals) + +public: + QQmlConnections(QObject *parent=0); + ~QQmlConnections(); + + QObject *target() const; + void setTarget(QObject *); + + bool ignoreUnknownSignals() const; + void setIgnoreUnknownSignals(bool ignore); + +Q_SIGNALS: + void targetChanged(); + +private: + void connectSignals(); + void classBegin(); + void componentComplete(); +}; + +class QQmlConnectionsParser : public QQmlCustomParser +{ +public: + virtual QByteArray compile(const QList &); + virtual void setCustomData(QObject *, const QByteArray &); +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlConnections) + +#endif diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp new file mode 100644 index 0000000000..efbd98bdbc --- /dev/null +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -0,0 +1,3189 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldelegatemodel_p_p.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQmlDelegateModelEngineData : public QV8Engine::Deletable +{ +public: + enum + { + Model, + Groups, + IsUnresolved, + ItemsIndex, + PersistedItemsIndex, + InItems, + InPersistedItems, + StringCount + }; + + QQmlDelegateModelEngineData(QV8Engine *engine); + ~QQmlDelegateModelEngineData(); + + v8::Local array( + QV8Engine *engine, const QVector &changes); + v8::Local array( + QV8Engine *engine, const QVector &changes); + v8::Local array( + QV8Engine *engine, const QVector &changes); + + + inline v8::Local model() { return strings->Get(Model)->ToString(); } + inline v8::Local groups() { return strings->Get(Groups)->ToString(); } + inline v8::Local isUnresolved() { return strings->Get(IsUnresolved)->ToString(); } + inline v8::Local itemsIndex() { return strings->Get(ItemsIndex)->ToString(); } + inline v8::Local persistedItemsIndex() { return strings->Get(PersistedItemsIndex)->ToString(); } + inline v8::Local inItems() { return strings->Get(InItems)->ToString(); } + inline v8::Local inPersistedItems() { return strings->Get(InPersistedItems)->ToString(); } + + v8::Persistent strings; + v8::Persistent constructorChange; + v8::Persistent constructorChangeArray; +}; + +V8_DEFINE_EXTENSION(QQmlDelegateModelEngineData, engineData) + + +void QQmlDelegateModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) +{ + prop.setWritable(false); +} + +QVariant QQmlDelegateModelPartsMetaObject::initialValue(int id) +{ + QQmlDelegateModelParts *parts = static_cast(object()); + QQmlPartsModel *m = new QQmlPartsModel( + parts->model, QString::fromUtf8(name(id)), parts); + parts->models.append(m); + return QVariant::fromValue(static_cast(m)); +} + +QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent) +: QObject(parent), model(parent) +{ + new QQmlDelegateModelPartsMetaObject(this); +} + +//--------------------------------------------------------------------------- + +/*! + \qmltype VisualDataModel + \instantiates QQmlDelegateModel + \inqmlmodule QtQuick 2 + \ingroup qtquick-models + \brief Encapsulates a model and delegate + + The VisualDataModel type encapsulates a model and the delegate that will + be instantiated for items in a model. + + This type is provided by QtQuick 2 for compatibility reasons. The same implementation + is now primarily available as DelegateModel in the QtQml.Models module. + + \sa {QtQml.Models2::DelegateModel} +*/ +/*! + \qmltype DelegateModel + \instantiates QQmlDelegateModel + \inqmlmodule QtQml.Models 2 + \brief Encapsulates a model and delegate + + The DelegateModel type encapsulates a model and the delegate that will + be instantiated for items in the model. + + This element is also available as DelegateModel in the QtQuick module. For full details, + see the \l DelegateModel documentation. + + The DelegateModel type encapsulates a model and the delegate that will + be instantiated for items in the model. + + It is usually not necessary to create a DelegateModel. + However, it can be useful for manipulating and accessing the \l modelIndex + when a QAbstractItemModel subclass is used as the + model. Also, DelegateModel is used together with \l Package to + provide delegates to multiple views, and with DelegateModelGroup to sort and filter + delegate items. + + The example below illustrates using a DelegateModel with a ListView. + + \snippet qml/visualdatamodel.qml 0 +*/ + +QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt) + : m_delegate(0) + , m_cacheMetaType(0) + , m_context(ctxt) + , m_parts(0) + , m_filterGroup(QStringLiteral("items")) + , m_count(0) + , m_groupCount(Compositor::MinimumGroupCount) + , m_compositorGroup(Compositor::Cache) + , m_complete(false) + , m_delegateValidated(false) + , m_reset(false) + , m_transaction(false) + , m_incubatorCleanupScheduled(false) + , m_cacheItems(0) + , m_items(0) + , m_persistedItems(0) +{ +} + +QQmlDelegateModelPrivate::~QQmlDelegateModelPrivate() +{ + qDeleteAll(m_finishedIncubating); + + if (m_cacheMetaType) + m_cacheMetaType->release(); +} + +void QQmlDelegateModelPrivate::init() +{ + Q_Q(QQmlDelegateModel); + m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag); + + m_items = new QQmlDelegateModelGroup(QStringLiteral("items"), q, Compositor::Default, q); + m_items->setDefaultInclude(true); + m_persistedItems = new QQmlDelegateModelGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q); + QQmlDelegateModelGroupPrivate::get(m_items)->emitters.insert(this); +} + +QQmlDelegateModel::QQmlDelegateModel() +: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(0))) +{ + Q_D(QQmlDelegateModel); + d->init(); +} + +QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent) +: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(ctxt)), parent) +{ + Q_D(QQmlDelegateModel); + d->init(); +} + +QQmlDelegateModel::~QQmlDelegateModel() +{ + Q_D(QQmlDelegateModel); + + foreach (QQmlDelegateModelItem *cacheItem, d->m_cache) { + if (cacheItem->object) { + delete cacheItem->object; + + cacheItem->object = 0; + cacheItem->contextData->destroy(); + cacheItem->contextData = 0; + cacheItem->scriptRef -= 1; + } + cacheItem->groups &= ~Compositor::UnresolvedFlag; + cacheItem->objectRef = 0; + if (!cacheItem->isReferenced()) + delete cacheItem; + } +} + + +void QQmlDelegateModel::classBegin() +{ + Q_D(QQmlDelegateModel); + if (!d->m_context) + d->m_context = qmlContext(this); +} + +void QQmlDelegateModel::componentComplete() +{ + Q_D(QQmlDelegateModel); + d->m_complete = true; + + int defaultGroups = 0; + QStringList groupNames; + groupNames.append(QStringLiteral("items")); + groupNames.append(QStringLiteral("persistedItems")); + if (QQmlDelegateModelGroupPrivate::get(d->m_items)->defaultInclude) + defaultGroups |= Compositor::DefaultFlag; + if (QQmlDelegateModelGroupPrivate::get(d->m_persistedItems)->defaultInclude) + defaultGroups |= Compositor::PersistedFlag; + for (int i = Compositor::MinimumGroupCount; i < d->m_groupCount; ++i) { + QString name = d->m_groups[i]->name(); + if (name.isEmpty()) { + d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; + --d->m_groupCount; + --i; + } else if (name.at(0).isUpper()) { + qmlInfo(d->m_groups[i]) << QQmlDelegateModelGroup::tr("Group names must start with a lower case letter"); + d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; + --d->m_groupCount; + --i; + } else { + groupNames.append(name); + + QQmlDelegateModelGroupPrivate *group = QQmlDelegateModelGroupPrivate::get(d->m_groups[i]); + group->setModel(this, Compositor::Group(i)); + if (group->defaultInclude) + defaultGroups |= (1 << i); + } + } + + d->m_cacheMetaType = new QQmlDelegateModelItemMetaType( + QQmlEnginePrivate::getV8Engine(d->m_context->engine()), this, groupNames); + + d->m_compositor.setGroupCount(d->m_groupCount); + d->m_compositor.setDefaultGroups(defaultGroups); + d->updateFilterGroup(); + + while (!d->m_pendingParts.isEmpty()) + static_cast(d->m_pendingParts.first())->updateFilterGroup(); + + QVector inserts; + d->m_count = d->m_adaptorModel.count(); + d->m_compositor.append( + &d->m_adaptorModel, + 0, + d->m_count, + defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag, + &inserts); + d->itemsInserted(inserts); + d->emitChanges(); + + if (d->m_adaptorModel.canFetchMore()) + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); +} + +/*! + \qmlproperty model QtQml.Models2::DelegateModel::model + This property holds the model providing data for the DelegateModel. + + The model provides a set of data that is used to create the items + for a view. For large or dynamic datasets the model is usually + provided by a C++ model object. The C++ model object must be a \l + {QAbstractItemModel} subclass or a simple list. + + Models can also be created directly in QML, using a \l{ListModel} or + \l{XmlListModel}. + + \sa {qml-data-models}{Data Models} +*/ +QVariant QQmlDelegateModel::model() const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.model(); +} + +void QQmlDelegateModel::setModel(const QVariant &model) +{ + Q_D(QQmlDelegateModel); + + if (d->m_complete) + _q_itemsRemoved(0, d->m_count); + + d->m_adaptorModel.setModel(model, this, d->m_context->engine()); + d->m_adaptorModel.replaceWatchedRoles(QList(), d->m_watchedRoles); + for (int i = 0; d->m_parts && i < d->m_parts->models.count(); ++i) { + d->m_adaptorModel.replaceWatchedRoles( + QList(), d->m_parts->models.at(i)->watchedRoles()); + } + + if (d->m_complete) { + _q_itemsInserted(0, d->m_adaptorModel.count()); + if (d->m_adaptorModel.canFetchMore()) + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); + } +} + +/*! + \qmlproperty Component QtQml.Models2::DelegateModel::delegate + + The delegate provides a template defining each item instantiated by a view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qml-data-models}{Data Model}. +*/ +QQmlComponent *QQmlDelegateModel::delegate() const +{ + Q_D(const QQmlDelegateModel); + return d->m_delegate; +} + +void QQmlDelegateModel::setDelegate(QQmlComponent *delegate) +{ + Q_D(QQmlDelegateModel); + if (d->m_transaction) { + qmlInfo(this) << tr("The delegate of a DelegateModel cannot be changed within onUpdated."); + return; + } + bool wasValid = d->m_delegate != 0; + d->m_delegate = delegate; + d->m_delegateValidated = false; + if (wasValid && d->m_complete) { + for (int i = 1; i < d->m_groupCount; ++i) { + QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.remove( + 0, d->m_compositor.count(Compositor::Group(i))); + } + } + if (d->m_complete && d->m_delegate) { + for (int i = 1; i < d->m_groupCount; ++i) { + QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.insert( + 0, d->m_compositor.count(Compositor::Group(i))); + } + } + d->emitChanges(); +} + +/*! + \qmlproperty QModelIndex QtQml.Models2::DelegateModel::rootIndex + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. \c rootIndex allows the children of + any node in a QAbstractItemModel to be provided by this model. + + This property only affects models of type QAbstractItemModel that + are hierarchical (e.g, a tree model). + + For example, here is a simple interactive file system browser. + When a directory name is clicked, the view's \c rootIndex is set to the + QModelIndex node of the clicked directory, thus updating the view to show + the new directory's contents. + + \c main.cpp: + \snippet qml/visualdatamodel_rootindex/main.cpp 0 + + \c view.qml: + \snippet qml/visualdatamodel_rootindex/view.qml 0 + + If the \l model is a QAbstractItemModel subclass, the delegate can also + reference a \c hasModelChildren property (optionally qualified by a + \e model. prefix) that indicates whether the delegate's model item has + any child nodes. + + + \sa modelIndex(), parentModelIndex() +*/ +QVariant QQmlDelegateModel::rootIndex() const +{ + Q_D(const QQmlDelegateModel); + return QVariant::fromValue(QModelIndex(d->m_adaptorModel.rootIndex)); +} + +void QQmlDelegateModel::setRootIndex(const QVariant &root) +{ + Q_D(QQmlDelegateModel); + + QModelIndex modelIndex = qvariant_cast(root); + const bool changed = d->m_adaptorModel.rootIndex != modelIndex; + if (changed || !d->m_adaptorModel.isValid()) { + const int oldCount = d->m_count; + d->m_adaptorModel.rootIndex = modelIndex; + if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) // The previous root index was invalidated, so we need to reconnect the model. + d->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine()); + if (d->m_adaptorModel.canFetchMore()) + d->m_adaptorModel.fetchMore(); + if (d->m_complete) { + const int newCount = d->m_adaptorModel.count(); + if (oldCount) + _q_itemsRemoved(0, oldCount); + if (newCount) + _q_itemsInserted(0, newCount); + } + if (changed) + emit rootIndexChanged(); + } +} + +/*! + \qmlmethod QModelIndex QtQml.Models2::DelegateModel::modelIndex(int index) + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. This function assists in using + tree models in QML. + + Returns a QModelIndex for the specified index. + This value can be assigned to rootIndex. + + \sa rootIndex +*/ +QVariant QQmlDelegateModel::modelIndex(int idx) const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.modelIndex(idx); +} + +/*! + \qmlmethod QModelIndex QtQml.Models2::DelegateModel::parentModelIndex() + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. This function assists in using + tree models in QML. + + Returns a QModelIndex for the parent of the current rootIndex. + This value can be assigned to rootIndex. + + \sa rootIndex +*/ +QVariant QQmlDelegateModel::parentModelIndex() const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.parentModelIndex(); +} + +/*! + \qmlproperty int QtQml.Models2::DelegateModel::count +*/ + +int QQmlDelegateModel::count() const +{ + Q_D(const QQmlDelegateModel); + if (!d->m_delegate) + return 0; + return d->m_compositor.count(d->m_compositorGroup); +} + +QQmlDelegateModel::ReleaseFlags QQmlDelegateModelPrivate::release(QObject *object) +{ + QQmlDelegateModel::ReleaseFlags stat = 0; + if (!object) + return stat; + + if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object)) { + if (cacheItem->releaseObject()) { + cacheItem->destroyObject(); + emitDestroyingItem(object); + if (cacheItem->incubationTask) { + releaseIncubator(cacheItem->incubationTask); + cacheItem->incubationTask = 0; + } + cacheItem->Dispose(); + stat |= QQmlInstanceModel::Destroyed; + } else { + stat |= QQmlDelegateModel::Referenced; + } + } + return stat; +} + +/* + Returns ReleaseStatus flags. +*/ + +QQmlDelegateModel::ReleaseFlags QQmlDelegateModel::release(QObject *item) +{ + Q_D(QQmlDelegateModel); + QQmlInstanceModel::ReleaseFlags stat = d->release(item); + return stat; +} + +// Cancel a requested async item +void QQmlDelegateModel::cancel(int index) +{ + Q_D(QQmlDelegateModel); + if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { + qWarning() << "DelegateModel::cancel: index out range" << index << d->m_compositor.count(d->m_compositorGroup); + return; + } + + Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index); + QQmlDelegateModelItem *cacheItem = it->inCache() ? d->m_cache.at(it.cacheIndex) : 0; + if (cacheItem) { + if (cacheItem->incubationTask && !cacheItem->isObjectReferenced()) { + d->releaseIncubator(cacheItem->incubationTask); + cacheItem->incubationTask = 0; + + if (cacheItem->object) { + QObject *object = cacheItem->object; + cacheItem->destroyObject(); + if (QQuickPackage *package = qmlobject_cast(object)) + d->emitDestroyingPackage(package); + else + d->emitDestroyingItem(object); + } + + cacheItem->scriptRef -= 1; + } + if (!cacheItem->isReferenced()) { + d->m_compositor.clearFlags(Compositor::Cache, it.cacheIndex, 1, Compositor::CacheFlag); + d->m_cache.removeAt(it.cacheIndex); + delete cacheItem; + Q_ASSERT(d->m_cache.count() == d->m_compositor.count(Compositor::Cache)); + } + } +} + +void QQmlDelegateModelPrivate::group_append( + QQmlListProperty *property, QQmlDelegateModelGroup *group) +{ + QQmlDelegateModelPrivate *d = static_cast(property->data); + if (d->m_complete) + return; + if (d->m_groupCount == Compositor::MaximumGroupCount) { + qmlInfo(d->q_func()) << QQmlDelegateModel::tr("The maximum number of supported DelegateModelGroups is 8"); + return; + } + d->m_groups[d->m_groupCount] = group; + d->m_groupCount += 1; +} + +int QQmlDelegateModelPrivate::group_count( + QQmlListProperty *property) +{ + QQmlDelegateModelPrivate *d = static_cast(property->data); + return d->m_groupCount - 1; +} + +QQmlDelegateModelGroup *QQmlDelegateModelPrivate::group_at( + QQmlListProperty *property, int index) +{ + QQmlDelegateModelPrivate *d = static_cast(property->data); + return index >= 0 && index < d->m_groupCount - 1 + ? d->m_groups[index + 1] + : 0; +} + +/*! + \qmlproperty list QtQml.Models2::DelegateModel::groups + + This property holds a visual data model's group definitions. + + Groups define a sub-set of the items in a visual data model and can be used to filter + a model. + + For every group defined in a DelegateModel two attached properties are added to each + delegate item. The first of the form DelegateModel.in\e{GroupName} holds whether the + item belongs to the group and the second DelegateModel.\e{groupName}Index holds the + index of the item in that group. + + The following example illustrates using groups to select items in a model. + + \snippet qml/visualdatagroup.qml 0 +*/ + +QQmlListProperty QQmlDelegateModel::groups() +{ + Q_D(QQmlDelegateModel); + return QQmlListProperty( + this, + d, + QQmlDelegateModelPrivate::group_append, + QQmlDelegateModelPrivate::group_count, + QQmlDelegateModelPrivate::group_at, + 0); +} + +/*! + \qmlproperty DelegateModelGroup QtQml.Models2::DelegateModel::items + + This property holds visual data model's default group to which all new items are added. +*/ + +QQmlDelegateModelGroup *QQmlDelegateModel::items() +{ + Q_D(QQmlDelegateModel); + return d->m_items; +} + +/*! + \qmlproperty DelegateModelGroup QtQml.Models2::DelegateModel::persistedItems + + This property holds visual data model's persisted items group. + + Items in this group are not destroyed when released by a view, instead they are persisted + until removed from the group. + + An item can be removed from the persistedItems group by setting the + DelegateModel.inPersistedItems property to false. If the item is not referenced by a view + at that time it will be destroyed. Adding an item to this group will not create a new + instance. + + Items returned by the \l QtQml.Models2::DelegateModelGroup::create() function are automatically added + to this group. +*/ + +QQmlDelegateModelGroup *QQmlDelegateModel::persistedItems() +{ + Q_D(QQmlDelegateModel); + return d->m_persistedItems; +} + +/*! + \qmlproperty string QtQml.Models2::DelegateModel::filterOnGroup + + This property holds the name of the group used to filter the visual data model. + + Only items which belong to this group are visible to a view. + + By default this is the \l items group. +*/ + +QString QQmlDelegateModel::filterGroup() const +{ + Q_D(const QQmlDelegateModel); + return d->m_filterGroup; +} + +void QQmlDelegateModel::setFilterGroup(const QString &group) +{ + Q_D(QQmlDelegateModel); + + if (d->m_transaction) { + qmlInfo(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); + return; + } + + if (d->m_filterGroup != group) { + d->m_filterGroup = group; + d->updateFilterGroup(); + emit filterGroupChanged(); + } +} + +void QQmlDelegateModel::resetFilterGroup() +{ + setFilterGroup(QStringLiteral("items")); +} + +void QQmlDelegateModelPrivate::updateFilterGroup() +{ + Q_Q(QQmlDelegateModel); + if (!m_cacheMetaType) + return; + + QQmlListCompositor::Group previousGroup = m_compositorGroup; + m_compositorGroup = Compositor::Default; + for (int i = 1; i < m_groupCount; ++i) { + if (m_filterGroup == m_cacheMetaType->groupNames.at(i - 1)) { + m_compositorGroup = Compositor::Group(i); + break; + } + } + + QQmlDelegateModelGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this); + if (m_compositorGroup != previousGroup) { + QVector removes; + QVector inserts; + m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); + + QQmlChangeSet changeSet; + changeSet.move(removes, inserts); + emit q->modelUpdated(changeSet, false); + + if (changeSet.difference() != 0) + emit q->countChanged(); + + if (m_parts) { + foreach (QQmlPartsModel *model, m_parts->models) + model->updateFilterGroup(m_compositorGroup, changeSet); + } + } +} + +/*! + \qmlproperty object QtQml.Models2::DelegateModel::parts + + The \a parts property selects a DelegateModel which creates + delegates from the part named. This is used in conjunction with + the \l Package type. + + For example, the code below selects a model which creates + delegates named \e list from a \l Package: + + \code + DelegateModel { + id: visualModel + delegate: Package { + Item { Package.name: "list" } + } + model: myModel + } + + ListView { + width: 200; height:200 + model: visualModel.parts.list + } + \endcode + + \sa Package +*/ + +QObject *QQmlDelegateModel::parts() +{ + Q_D(QQmlDelegateModel); + if (!d->m_parts) + d->m_parts = new QQmlDelegateModelParts(this); + return d->m_parts; +} + +void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) +{ + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->createdPackage(incubationTask->index[i], package); +} + +void QQmlDelegateModelPrivate::emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) +{ + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->initPackage(incubationTask->index[i], package); +} + +void QQmlDelegateModelPrivate::emitDestroyingPackage(QQuickPackage *package) +{ + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->destroyingPackage(package); +} + +void QQDMIncubationTask::statusChanged(Status status) +{ + vdm->incubatorStatusChanged(this, status); +} + +void QQmlDelegateModelPrivate::releaseIncubator(QQDMIncubationTask *incubationTask) +{ + Q_Q(QQmlDelegateModel); + if (!incubationTask->isError()) + incubationTask->clear(); + m_finishedIncubating.append(incubationTask); + if (!m_incubatorCleanupScheduled) { + m_incubatorCleanupScheduled = true; + QCoreApplication::postEvent(q, new QEvent(QEvent::User)); + } +} + +void QQmlDelegateModelPrivate::removeCacheItem(QQmlDelegateModelItem *cacheItem) +{ + int cidx = m_cache.indexOf(cacheItem); + if (cidx >= 0) { + m_compositor.clearFlags(Compositor::Cache, cidx, 1, Compositor::CacheFlag); + m_cache.removeAt(cidx); + } + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); +} + +void QQmlDelegateModelPrivate::incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status) +{ + Q_Q(QQmlDelegateModel); + if (status != QQmlIncubator::Ready && status != QQmlIncubator::Error) + return; + + QQmlDelegateModelItem *cacheItem = incubationTask->incubating; + cacheItem->incubationTask = 0; + incubationTask->incubating = 0; + releaseIncubator(incubationTask); + + if (status == QQmlIncubator::Ready) { + if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) + emitCreatedPackage(incubationTask, package); + else + emitCreatedItem(incubationTask, cacheItem->object); + } else if (status == QQmlIncubator::Error) { + qmlInfo(q, m_delegate->errors()) << "Error creating delegate"; + } + + if (!cacheItem->isObjectReferenced()) { + if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) + emitDestroyingPackage(package); + else + emitDestroyingItem(cacheItem->object); + delete cacheItem->object; + cacheItem->object = 0; + cacheItem->scriptRef -= 1; + cacheItem->contextData->destroy(); + cacheItem->contextData = 0; + if (!cacheItem->isReferenced()) { + removeCacheItem(cacheItem); + delete cacheItem; + } + } +} + +void QQDMIncubationTask::setInitialState(QObject *o) +{ + vdm->setInitialState(this, o); +} + +void QQmlDelegateModelPrivate::setInitialState(QQDMIncubationTask *incubationTask, QObject *o) +{ + QQmlDelegateModelItem *cacheItem = incubationTask->incubating; + cacheItem->object = o; + + if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) + emitInitPackage(incubationTask, package); + else + emitInitItem(incubationTask, cacheItem->object); +} + +QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, bool asynchronous) +{ + Q_Q(QQmlDelegateModel); + if (!m_delegate || index < 0 || index >= m_compositor.count(group)) { + qWarning() << "DelegateModel::item: index out range" << index << m_compositor.count(group); + return 0; + } else if (!m_context->isValid()) { + return 0; + } + + Compositor::iterator it = m_compositor.find(group, index); + + QQmlDelegateModelItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0; + + if (!cacheItem) { + cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), it.modelIndex()); + if (!cacheItem) + return 0; + + cacheItem->groups = it->flags; + + m_cache.insert(it.cacheIndex, cacheItem); + m_compositor.setFlags(it, 1, Compositor::CacheFlag); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + } + + // Bump the reference counts temporarily so neither the content data or the delegate object + // are deleted if incubatorStatusChanged() is called synchronously. + cacheItem->scriptRef += 1; + cacheItem->referenceObject(); + + if (cacheItem->incubationTask) { + if (!asynchronous && cacheItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) { + // previously requested async - now needed immediately + cacheItem->incubationTask->forceCompletion(); + } + } else if (!cacheItem->object) { + QQmlContext *creationContext = m_delegate->creationContext(); + + cacheItem->scriptRef += 1; + + cacheItem->incubationTask = new QQDMIncubationTask(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested); + cacheItem->incubationTask->incubating = cacheItem; + cacheItem->incubationTask->clear(); + + for (int i = 1; i < m_groupCount; ++i) + cacheItem->incubationTask->index[i] = it.index[i]; + + QQmlContextData *ctxt = new QQmlContextData; + ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context)); + ctxt->contextObject = cacheItem; + cacheItem->contextData = ctxt; + + if (m_adaptorModel.hasProxyObject()) { + if (QQmlAdaptorModelProxyInterface *proxy + = qobject_cast(cacheItem)) { + ctxt = new QQmlContextData; + ctxt->setParent(cacheItem->contextData, true); + ctxt->contextObject = proxy->proxiedObject(); + } + } + + cacheItem->incubateObject( + m_delegate, + m_context->engine(), + ctxt, + QQmlContextData::get(m_context)); + } + + if (index == m_compositor.count(group) - 1 && m_adaptorModel.canFetchMore()) + QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); + + // Remove the temporary reference count. + cacheItem->scriptRef -= 1; + if (cacheItem->object) + return cacheItem->object; + + cacheItem->releaseObject(); + if (!cacheItem->isReferenced()) { + removeCacheItem(cacheItem); + delete cacheItem; + } + + return 0; +} + +/* + If asynchronous is true or the component is being loaded asynchronously due + to an ancestor being loaded asynchronously, item() may return 0. In this + case itemCreated() will be emitted when the item is available. The item + at this stage does not have any references, so item() must be called again + to ensure a reference is held. Any call to item() which returns a valid item + must be matched by a call to release() in order to destroy the item. +*/ +QObject *QQmlDelegateModel::object(int index, bool asynchronous) +{ + Q_D(QQmlDelegateModel); + if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { + qWarning() << "DelegateModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup); + return 0; + } + + QObject *object = d->object(d->m_compositorGroup, index, asynchronous); + if (!object) + return 0; + + return object; +} + +QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) +{ + Compositor::iterator it = m_compositor.find(group, index); + if (QQmlAdaptorModel *model = it.list()) { + QString role = name; + int dot = name.indexOf(QLatin1Char('.')); + if (dot > 0) + role = name.left(dot); + QVariant value = model->value(it.modelIndex(), role); + while (dot > 0) { + QObject *obj = qvariant_cast(value); + if (!obj) + return QString(); + int from = dot+1; + dot = name.indexOf(QLatin1Char('.'), from); + value = obj->property(name.mid(from, dot-from).toUtf8()); + } + return value.toString(); + } + return QString(); +} + +QString QQmlDelegateModel::stringValue(int index, const QString &name) +{ + Q_D(QQmlDelegateModel); + return d->stringValue(d->m_compositorGroup, index, name); +} + +int QQmlDelegateModel::indexOf(QObject *item, QObject *) const +{ + Q_D(const QQmlDelegateModel); + if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(item)) + return cacheItem->groupIndex(d->m_compositorGroup); + return -1; +} + +void QQmlDelegateModel::setWatchedRoles(QList roles) +{ + Q_D(QQmlDelegateModel); + d->m_adaptorModel.replaceWatchedRoles(d->m_watchedRoles, roles); + d->m_watchedRoles = roles; +} + +void QQmlDelegateModelPrivate::addGroups( + Compositor::iterator from, int count, Compositor::Group group, int groupFlags) +{ + QVector inserts; + m_compositor.setFlags(from, count, group, groupFlags, &inserts); + itemsInserted(inserts); + emitChanges(); +} + +void QQmlDelegateModelPrivate::removeGroups( + Compositor::iterator from, int count, Compositor::Group group, int groupFlags) +{ + QVector removes; + m_compositor.clearFlags(from, count, group, groupFlags, &removes); + itemsRemoved(removes); + emitChanges(); +} + +void QQmlDelegateModelPrivate::setGroups( + Compositor::iterator from, int count, Compositor::Group group, int groupFlags) +{ + QVector removes; + QVector inserts; + + m_compositor.setFlags(from, count, group, groupFlags, &inserts); + itemsInserted(inserts); + const int removeFlags = ~groupFlags & Compositor::GroupMask; + + from = m_compositor.find(from.group, from.index[from.group]); + m_compositor.clearFlags(from, count, group, removeFlags, &removes); + itemsRemoved(removes); + emitChanges(); +} + +bool QQmlDelegateModel::event(QEvent *e) +{ + Q_D(QQmlDelegateModel); + if (e->type() == QEvent::UpdateRequest) { + d->m_adaptorModel.fetchMore(); + } else if (e->type() == QEvent::User) { + d->m_incubatorCleanupScheduled = false; + qDeleteAll(d->m_finishedIncubating); + d->m_finishedIncubating.clear(); + } + return QQmlInstanceModel::event(e); +} + +void QQmlDelegateModelPrivate::itemsChanged(const QVector &changes) +{ + if (!m_delegate) + return; + + QVarLengthArray, Compositor::MaximumGroupCount> translatedChanges(m_groupCount); + + foreach (const Compositor::Change &change, changes) { + for (int i = 1; i < m_groupCount; ++i) { + if (change.inGroup(i)) { + translatedChanges[i].append(QQmlChangeSet::Change(change.index[i], change.count)); + } + } + } + + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.change(translatedChanges.at(i)); +} + +void QQmlDelegateModel::_q_itemsChanged(int index, int count, const QVector &roles) +{ + Q_D(QQmlDelegateModel); + if (count <= 0 || !d->m_complete) + return; + + if (d->m_adaptorModel.notify(d->m_cache, index, count, roles)) { + QVector changes; + d->m_compositor.listItemsChanged(&d->m_adaptorModel, index, count, &changes); + d->itemsChanged(changes); + d->emitChanges(); + } +} + +static void incrementIndexes(QQmlDelegateModelItem *cacheItem, int count, const int *deltas) +{ + if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { + for (int i = 1; i < count; ++i) + incubationTask->index[i] += deltas[i]; + } + if (QQmlDelegateModelAttached *attached = cacheItem->attached) { + for (int i = 1; i < count; ++i) + attached->m_currentIndex[i] += deltas[i]; + } +} + +void QQmlDelegateModelPrivate::itemsInserted( + const QVector &inserts, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, + QHash > *movedItems) +{ + int cacheIndex = 0; + + int inserted[Compositor::MaximumGroupCount]; + for (int i = 1; i < m_groupCount; ++i) + inserted[i] = 0; + + foreach (const Compositor::Insert &insert, inserts) { + for (; cacheIndex < insert.cacheIndex; ++cacheIndex) + incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); + + for (int i = 1; i < m_groupCount; ++i) { + if (insert.inGroup(i)) { + (*translatedInserts)[i].append( + QQmlChangeSet::Insert(insert.index[i], insert.count, insert.moveId)); + inserted[i] += insert.count; + } + } + + if (!insert.inCache()) + continue; + + if (movedItems && insert.isMove()) { + QList items = movedItems->take(insert.moveId); + Q_ASSERT(items.count() == insert.count); + m_cache = m_cache.mid(0, insert.cacheIndex) + items + m_cache.mid(insert.cacheIndex); + } + if (insert.inGroup()) { + for (int offset = 0; cacheIndex < insert.cacheIndex + insert.count; ++cacheIndex, ++offset) { + QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); + cacheItem->groups |= insert.flags & Compositor::GroupMask; + + if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { + for (int i = 1; i < m_groupCount; ++i) + incubationTask->index[i] = cacheItem->groups & (1 << i) + ? insert.index[i] + offset + : insert.index[i]; + } + if (QQmlDelegateModelAttached *attached = cacheItem->attached) { + for (int i = 1; i < m_groupCount; ++i) + attached->m_currentIndex[i] = cacheItem->groups & (1 << i) + ? insert.index[i] + offset + : insert.index[i]; + } + } + } else { + cacheIndex = insert.cacheIndex + insert.count; + } + } + for (; cacheIndex < m_cache.count(); ++cacheIndex) + incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); +} + +void QQmlDelegateModelPrivate::itemsInserted(const QVector &inserts) +{ + QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); + itemsInserted(inserts, &translatedInserts); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + if (!m_delegate) + return; + + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert(translatedInserts.at(i)); +} + +void QQmlDelegateModel::_q_itemsInserted(int index, int count) +{ + + Q_D(QQmlDelegateModel); + if (count <= 0 || !d->m_complete) + return; + + d->m_count += count; + + for (int i = 0, c = d->m_cache.count(); i < c; ++i) { + QQmlDelegateModelItem *item = d->m_cache.at(i); + if (item->modelIndex() >= index) + item->setModelIndex(item->modelIndex() + count); + } + + QVector inserts; + d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts); + d->itemsInserted(inserts); + d->emitChanges(); +} + +void QQmlDelegateModelPrivate::itemsRemoved( + const QVector &removes, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, + QHash > *movedItems) +{ + int cacheIndex = 0; + int removedCache = 0; + + int removed[Compositor::MaximumGroupCount]; + for (int i = 1; i < m_groupCount; ++i) + removed[i] = 0; + + foreach (const Compositor::Remove &remove, removes) { + for (; cacheIndex < remove.cacheIndex; ++cacheIndex) + incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); + + for (int i = 1; i < m_groupCount; ++i) { + if (remove.inGroup(i)) { + (*translatedRemoves)[i].append( + QQmlChangeSet::Remove(remove.index[i], remove.count, remove.moveId)); + removed[i] -= remove.count; + } + } + + if (!remove.inCache()) + continue; + + if (movedItems && remove.isMove()) { + movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex, remove.count)); + QList::iterator begin = m_cache.begin() + remove.cacheIndex; + QList::iterator end = begin + remove.count; + m_cache.erase(begin, end); + } else { + for (; cacheIndex < remove.cacheIndex + remove.count - removedCache; ++cacheIndex) { + QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); + if (remove.inGroup(Compositor::Persisted) && cacheItem->objectRef == 0 && cacheItem->object) { + QObject *object = cacheItem->object; + cacheItem->destroyObject(); + if (QQuickPackage *package = qmlobject_cast(object)) + emitDestroyingPackage(package); + else + emitDestroyingItem(object); + cacheItem->scriptRef -= 1; + } + if (!cacheItem->isReferenced()) { + m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); + m_cache.removeAt(cacheIndex); + delete cacheItem; + --cacheIndex; + ++removedCache; + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + } else if (remove.groups() == cacheItem->groups) { + cacheItem->groups = 0; + if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { + for (int i = 1; i < m_groupCount; ++i) + incubationTask->index[i] = -1; + } + if (QQmlDelegateModelAttached *attached = cacheItem->attached) { + for (int i = 1; i < m_groupCount; ++i) + attached->m_currentIndex[i] = -1; + } + } else { + if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { + for (int i = 1; i < m_groupCount; ++i) { + if (remove.inGroup(i)) + incubationTask->index[i] = remove.index[i]; + } + } + if (QQmlDelegateModelAttached *attached = cacheItem->attached) { + for (int i = 1; i < m_groupCount; ++i) { + if (remove.inGroup(i)) + attached->m_currentIndex[i] = remove.index[i]; + } + } + cacheItem->groups &= ~remove.flags; + } + } + } + } + + for (; cacheIndex < m_cache.count(); ++cacheIndex) + incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); +} + +void QQmlDelegateModelPrivate::itemsRemoved(const QVector &removes) +{ + QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); + itemsRemoved(removes, &translatedRemoves); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + if (!m_delegate) + return; + + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove(translatedRemoves.at(i)); +} + +void QQmlDelegateModel::_q_itemsRemoved(int index, int count) +{ + Q_D(QQmlDelegateModel); + if (count <= 0|| !d->m_complete) + return; + + d->m_count -= count; + + for (int i = 0, c = d->m_cache.count(); i < c; ++i) { + QQmlDelegateModelItem *item = d->m_cache.at(i); + if (item->modelIndex() >= index + count) + item->setModelIndex(item->modelIndex() - count); + else if (item->modelIndex() >= index) + item->setModelIndex(-1); + } + + QVector removes; + d->m_compositor.listItemsRemoved(&d->m_adaptorModel, index, count, &removes); + d->itemsRemoved(removes); + + d->emitChanges(); +} + +void QQmlDelegateModelPrivate::itemsMoved( + const QVector &removes, const QVector &inserts) +{ + QHash > movedItems; + + QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); + itemsRemoved(removes, &translatedRemoves, &movedItems); + + QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); + itemsInserted(inserts, &translatedInserts, &movedItems); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + Q_ASSERT(movedItems.isEmpty()); + if (!m_delegate) + return; + + for (int i = 1; i < m_groupCount; ++i) { + QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.move( + translatedRemoves.at(i), + translatedInserts.at(i)); + } +} + +void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count) +{ + Q_D(QQmlDelegateModel); + if (count <= 0 || !d->m_complete) + return; + + const int minimum = qMin(from, to); + const int maximum = qMax(from, to) + count; + const int difference = from > to ? count : -count; + + for (int i = 0, c = d->m_cache.count(); i < c; ++i) { + QQmlDelegateModelItem *item = d->m_cache.at(i); + if (item->modelIndex() >= from && item->modelIndex() < from + count) + item->setModelIndex(item->modelIndex() - from + to); + else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) + item->setModelIndex(item->modelIndex() + difference); + } + + QVector removes; + QVector inserts; + d->m_compositor.listItemsMoved(&d->m_adaptorModel, from, to, count, &removes, &inserts); + d->itemsMoved(removes, inserts); + d->emitChanges(); +} + +template v8::Local +QQmlDelegateModelPrivate::buildChangeList(const QVector &changes) +{ + v8::Local indexes = v8::Array::New(changes.count()); + v8::Local indexKey = v8::String::New("index"); + v8::Local countKey = v8::String::New("count"); + v8::Local moveIdKey = v8::String::New("moveId"); + + for (int i = 0; i < changes.count(); ++i) { + v8::Local object = v8::Object::New(); + object->Set(indexKey, v8::Integer::New(changes.at(i).index)); + object->Set(countKey, v8::Integer::New(changes.at(i).count)); + object->Set(moveIdKey, changes.at(i).moveId != -1 ? v8::Integer::New(changes.at(i).count) : v8::Undefined()); + indexes->Set(i, object); + } + return indexes; +} + +void QQmlDelegateModelPrivate::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) +{ + Q_Q(QQmlDelegateModel); + emit q->modelUpdated(changeSet, reset); + if (changeSet.difference() != 0) + emit q->countChanged(); +} + +void QQmlDelegateModelPrivate::emitChanges() +{ + if (m_transaction || !m_complete || !m_context->isValid()) + return; + + m_transaction = true; + QV8Engine *engine = QQmlEnginePrivate::getV8Engine(m_context->engine()); + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitChanges(engine); + m_transaction = false; + + const bool reset = m_reset; + m_reset = false; + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); + + foreach (QQmlDelegateModelItem *cacheItem, m_cache) { + if (cacheItem->attached) + cacheItem->attached->emitChanges(); + } +} + +void QQmlDelegateModel::_q_modelReset() +{ + Q_D(QQmlDelegateModel); + if (!d->m_delegate) + return; + + int oldCount = d->m_count; + d->m_adaptorModel.rootIndex = QModelIndex(); + + if (d->m_complete) { + d->m_count = d->m_adaptorModel.count(); + + for (int i = 0, c = d->m_cache.count(); i < c; ++i) { + QQmlDelegateModelItem *item = d->m_cache.at(i); + if (item->modelIndex() != -1) + item->setModelIndex(-1); + } + + QVector removes; + QVector inserts; + if (oldCount) + d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); + if (d->m_count) + d->m_compositor.listItemsInserted(&d->m_adaptorModel, 0, d->m_count, &inserts); + d->itemsMoved(removes, inserts); + d->m_reset = true; + + if (d->m_adaptorModel.canFetchMore()) + d->m_adaptorModel.fetchMore(); + + d->emitChanges(); + } + emit rootIndexChanged(); +} + +void QQmlDelegateModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end) +{ + Q_D(QQmlDelegateModel); + if (parent == d->m_adaptorModel.rootIndex) + _q_itemsInserted(begin, end - begin + 1); +} + +void QQmlDelegateModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end) +{ + Q_D(QQmlDelegateModel); + if (!d->m_adaptorModel.rootIndex.isValid()) + return; + const QModelIndex index = d->m_adaptorModel.rootIndex; + if (index.parent() == parent && index.row() >= begin && index.row() <= end) { + const int oldCount = d->m_count; + d->m_count = 0; + d->m_adaptorModel.invalidateModel(this); + + if (d->m_complete && oldCount > 0) { + QVector removes; + d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); + d->itemsRemoved(removes); + d->emitChanges(); + } + } +} + +void QQmlDelegateModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end) +{ + Q_D(QQmlDelegateModel); + if (parent == d->m_adaptorModel.rootIndex) + _q_itemsRemoved(begin, end - begin + 1); +} + +void QQmlDelegateModel::_q_rowsMoved( + const QModelIndex &sourceParent, int sourceStart, int sourceEnd, + const QModelIndex &destinationParent, int destinationRow) +{ + Q_D(QQmlDelegateModel); + const int count = sourceEnd - sourceStart + 1; + if (destinationParent == d->m_adaptorModel.rootIndex && sourceParent == d->m_adaptorModel.rootIndex) { + _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow - count, count); + } else if (sourceParent == d->m_adaptorModel.rootIndex) { + _q_itemsRemoved(sourceStart, count); + } else if (destinationParent == d->m_adaptorModel.rootIndex) { + _q_itemsInserted(destinationRow, count); + } +} + +void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector &roles) +{ + Q_D(QQmlDelegateModel); + if (begin.parent() == d->m_adaptorModel.rootIndex) + _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, roles); +} + +void QQmlDelegateModel::_q_layoutChanged() +{ + Q_D(QQmlDelegateModel); + _q_itemsChanged(0, d->m_count, QVector()); +} + +QQmlDelegateModelAttached *QQmlDelegateModel::qmlAttachedProperties(QObject *obj) +{ + if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(obj)) { + if (cacheItem->object == obj) { // Don't create attached item for child objects. + cacheItem->attached = new QQmlDelegateModelAttached(cacheItem, obj); + return cacheItem->attached; + } + } + return new QQmlDelegateModelAttached(obj); +} + +bool QQmlDelegateModelPrivate::insert( + Compositor::insert_iterator &before, const v8::Local &object, int groups) +{ + if (!m_context->isValid()) + return false; + + QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), -1); + if (!cacheItem) + return false; + + v8::Local propertyNames = object->GetPropertyNames(); + for (uint i = 0; i < propertyNames->Length(); ++i) { + v8::Local propertyName = propertyNames->Get(i)->ToString(); + cacheItem->setValue( + m_cacheMetaType->v8Engine->toString(propertyName), + m_cacheMetaType->v8Engine->toVariant(object->Get(propertyName), QVariant::Invalid)); + } + + cacheItem->groups = groups | Compositor::UnresolvedFlag | Compositor::CacheFlag; + + // Must be before the new object is inserted into the cache or its indexes will be adjusted too. + itemsInserted(QVector() << Compositor::Insert(before, 1, cacheItem->groups & ~Compositor::CacheFlag)); + + before = m_compositor.insert(before, 0, 0, 1, cacheItem->groups); + m_cache.insert(before.cacheIndex, cacheItem); + + return true; +} + +//============================================================================ + +QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType( + QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames) + : model(model) + , groupCount(groupNames.count() + 1) + , v8Engine(engine) + , metaObject(0) + , groupNames(groupNames) +{ +} + +QQmlDelegateModelItemMetaType::~QQmlDelegateModelItemMetaType() +{ + if (metaObject) + metaObject->release(); + qPersistentDispose(constructor); +} + +void QQmlDelegateModelItemMetaType::initializeMetaObject() +{ + QMetaObjectBuilder builder; + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + builder.setClassName(QQmlDelegateModelAttached::staticMetaObject.className()); + builder.setSuperClass(&QQmlDelegateModelAttached::staticMetaObject); + + int notifierId = 0; + for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { + QString propertyName = QStringLiteral("in") + groupNames.at(i); + propertyName.replace(2, 1, propertyName.at(2).toUpper()); + builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); + QMetaPropertyBuilder propertyBuilder = builder.addProperty( + propertyName.toUtf8(), "bool", notifierId); + propertyBuilder.setWritable(true); + } + for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { + const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); + builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); + QMetaPropertyBuilder propertyBuilder = builder.addProperty( + propertyName.toUtf8(), "int", notifierId); + propertyBuilder.setWritable(true); + } + + metaObject = new QQmlDelegateModelAttachedMetaObject(this, builder.toMetaObject()); +} + +void QQmlDelegateModelItemMetaType::initializeConstructor() +{ + v8::HandleScope handleScope; + v8::Context::Scope contextScope(v8Engine->context()); + + QQmlDelegateModelEngineData *data = engineData(v8Engine); + + constructor = qPersistentNew(v8::ObjectTemplate::New()); + + constructor->SetHasExternalResource(true); + constructor->SetAccessor(data->model(), get_model); + constructor->SetAccessor(data->groups(), get_groups, set_groups); + constructor->SetAccessor(data->isUnresolved(), get_member, 0, v8::Int32::New(30)); + constructor->SetAccessor(data->inItems(), get_member, set_member, v8::Int32::New(1)); + constructor->SetAccessor(data->inPersistedItems(), get_member, set_member, v8::Int32::New(2)); + constructor->SetAccessor(data->itemsIndex(), get_index, 0, v8::Int32::New(1)); + constructor->SetAccessor(data->persistedItemsIndex(), get_index, 0, v8::Int32::New(2)); + + for (int i = 2; i < groupNames.count(); ++i) { + QString propertyName = QStringLiteral("in") + groupNames.at(i); + propertyName.replace(2, 1, propertyName.at(2).toUpper()); + constructor->SetAccessor( + v8Engine->toString(propertyName), get_member, set_member, v8::Int32::New(i + 1)); + } + for (int i = 2; i < groupNames.count(); ++i) { + const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); + constructor->SetAccessor( + v8Engine->toString(propertyName), get_index, 0, v8::Int32::New(i + 1)); + } +} + +int QQmlDelegateModelItemMetaType::parseGroups(const QStringList &groups) const +{ + int groupFlags = 0; + foreach (const QString &groupName, groups) { + int index = groupNames.indexOf(groupName); + if (index != -1) + groupFlags |= 2 << index; + } + return groupFlags; +} + +int QQmlDelegateModelItemMetaType::parseGroups(const v8::Local &groups) const +{ + int groupFlags = 0; + if (groups->IsString()) { + const QString groupName = v8Engine->toString(groups); + int index = groupNames.indexOf(groupName); + if (index != -1) + groupFlags |= 2 << index; + } else if (groups->IsArray()) { + v8::Local array = v8::Local::Cast(groups); + for (uint i = 0; i < array->Length(); ++i) { + const QString groupName = v8Engine->toString(array->Get(i)); + int index = groupNames.indexOf(groupName); + if (index != -1) + groupFlags |= 2 << index; + } + } + return groupFlags; +} + +v8::Handle QQmlDelegateModelItemMetaType::get_model( + v8::Local, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); + if (!cacheItem->metaType->model) + return v8::Undefined(); + + return cacheItem->get(); +} + +v8::Handle QQmlDelegateModelItemMetaType::get_groups( + v8::Local, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); + + QStringList groups; + for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { + if (cacheItem->groups & (1 << i)) + groups.append(cacheItem->metaType->groupNames.at(i - 1)); + } + + return cacheItem->engine->fromVariant(groups); +} + +void QQmlDelegateModelItemMetaType::set_groups( + v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object"); + + if (!cacheItem->metaType->model) + return; + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model); + + const int groupFlags = model->m_cacheMetaType->parseGroups(value); + const int cacheIndex = model->m_cache.indexOf(cacheItem); + Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); + model->setGroups(it, 1, Compositor::Cache, groupFlags); +} + +v8::Handle QQmlDelegateModelItemMetaType::get_member( + v8::Local, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); + + return v8::Boolean::New(cacheItem->groups & (1 << info.Data()->Int32Value())); +} + +void QQmlDelegateModelItemMetaType::set_member( + v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object"); + + if (!cacheItem->metaType->model) + return; + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model); + + Compositor::Group group = Compositor::Group(info.Data()->Int32Value()); + const bool member = value->BooleanValue(); + const int groupFlag = (1 << group); + if (member == ((cacheItem->groups & groupFlag) != 0)) + return; + + const int cacheIndex = model->m_cache.indexOf(cacheItem); + Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); + if (member) + model->addGroups(it, 1, Compositor::Cache, groupFlag); + else + model->removeGroups(it, 1, Compositor::Cache, groupFlag); +} + +v8::Handle QQmlDelegateModelItemMetaType::get_index( + v8::Local, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); + + return v8::Integer::New(cacheItem->groupIndex(Compositor::Group(info.Data()->Int32Value()))); +} + + +//--------------------------------------------------------------------------- + +QQmlDelegateModelItem::QQmlDelegateModelItem( + QQmlDelegateModelItemMetaType *metaType, int modelIndex) + : QV8ObjectResource(metaType->v8Engine) + , metaType(metaType) + , contextData(0) + , object(0) + , attached(0) + , incubationTask(0) + , objectRef(0) + , scriptRef(0) + , groups(0) + , index(modelIndex) +{ + metaType->addref(); +} + +QQmlDelegateModelItem::~QQmlDelegateModelItem() +{ + Q_ASSERT(scriptRef == 0); + Q_ASSERT(objectRef == 0); + Q_ASSERT(!object); + + if (incubationTask && metaType->model) + QQmlDelegateModelPrivate::get(metaType->model)->releaseIncubator(incubationTask); + + metaType->release(); + +} + +void QQmlDelegateModelItem::Dispose() +{ + --scriptRef; + if (isReferenced()) + return; + + if (metaType->model) { + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); + model->removeCacheItem(this); + } + delete this; +} + +/* + This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData + arguments instead of QQmlContext which means we don't have to construct the rather weighty + wrapper class for every delegate item. +*/ +void QQmlDelegateModelItem::incubateObject( + QQmlComponent *component, + QQmlEngine *engine, + QQmlContextData *context, + QQmlContextData *forContext) +{ + QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubationTask); + QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); + QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component); + + incubatorPriv->compiledData = componentPriv->cc; + incubatorPriv->compiledData->addref(); + incubatorPriv->vme.init( + context, + componentPriv->cc, + componentPriv->start, + componentPriv->creationContext); + + enginePriv->incubate(*incubationTask, forContext); +} + +void QQmlDelegateModelItem::destroyObject() +{ + Q_ASSERT(object); + Q_ASSERT(contextData); + + QObjectPrivate *p = QObjectPrivate::get(object); + Q_ASSERT(p->declarativeData); + QQmlData *data = static_cast(p->declarativeData); + if (data->ownContext && data->context) + data->context->clearContext(); + object->deleteLater(); + + if (attached) { + attached->m_cacheItem = 0; + attached = 0; + } + + contextData->destroy(); + contextData = 0; + object = 0; +} + +QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object) +{ + QObjectPrivate *p = QObjectPrivate::get(object); + QQmlContextData *context = p->declarativeData + ? static_cast(p->declarativeData)->context + : 0; + for (context = context ? context->parent : 0; context; context = context->parent) { + if (QQmlDelegateModelItem *cacheItem = qobject_cast( + context->contextObject)) { + return cacheItem; + } + } + return 0; +} + +int QQmlDelegateModelItem::groupIndex(Compositor::Group group) +{ + if (QQmlDelegateModelPrivate * const model = metaType->model + ? QQmlDelegateModelPrivate::get(metaType->model) + : 0) { + return model->m_compositor.find(Compositor::Cache, model->m_cache.indexOf(this)).index[group]; + } + return -1; +} + +//--------------------------------------------------------------------------- + +QQmlDelegateModelAttachedMetaObject::QQmlDelegateModelAttachedMetaObject( + QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject) + : metaType(metaType) + , metaObject(metaObject) + , memberPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount()) + , indexPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount() + metaType->groupNames.count()) +{ + // Don't reference count the meta-type here as that would create a circular reference. + // Instead we rely the fact that the meta-type's reference count can't reach 0 without first + // destroying all delegates with attached objects. + *static_cast(this) = *metaObject; +} + +QQmlDelegateModelAttachedMetaObject::~QQmlDelegateModelAttachedMetaObject() +{ + ::free(metaObject); +} + +void QQmlDelegateModelAttachedMetaObject::objectDestroyed(QObject *) +{ + release(); +} + +int QQmlDelegateModelAttachedMetaObject::metaCall(QObject *object, QMetaObject::Call call, int _id, void **arguments) +{ + QQmlDelegateModelAttached *attached = static_cast(object); + if (call == QMetaObject::ReadProperty) { + if (_id >= indexPropertyOffset) { + Compositor::Group group = Compositor::Group(_id - indexPropertyOffset + 1); + *static_cast(arguments[0]) = attached->m_currentIndex[group]; + return -1; + } else if (_id >= memberPropertyOffset) { + Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); + *static_cast(arguments[0]) = attached->m_cacheItem->groups & (1 << group); + return -1; + } + } else if (call == QMetaObject::WriteProperty) { + if (_id >= memberPropertyOffset) { + if (!metaType->model) + return -1; + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); + Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); + const int groupFlag = 1 << group; + const bool member = attached->m_cacheItem->groups & groupFlag; + if (member && !*static_cast(arguments[0])) { + Compositor::iterator it = model->m_compositor.find( + group, attached->m_currentIndex[group]); + model->removeGroups(it, 1, group, groupFlag); + } else if (!member && *static_cast(arguments[0])) { + for (int i = 1; i < metaType->groupCount; ++i) { + if (attached->m_cacheItem->groups & (1 << i)) { + Compositor::iterator it = model->m_compositor.find( + Compositor::Group(i), attached->m_currentIndex[i]); + model->addGroups(it, 1, Compositor::Group(i), groupFlag); + break; + } + } + } + return -1; + } + } + return attached->qt_metacall(call, _id, arguments); +} + +QQmlDelegateModelAttached::QQmlDelegateModelAttached(QObject *parent) + : m_cacheItem(0) + , m_previousGroups(0) +{ + QQml_setParent_noEvent(this, parent); +} + +QQmlDelegateModelAttached::QQmlDelegateModelAttached( + QQmlDelegateModelItem *cacheItem, QObject *parent) + : m_cacheItem(cacheItem) + , m_previousGroups(cacheItem->groups) +{ + QQml_setParent_noEvent(this, parent); + if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) { + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) + m_currentIndex[i] = m_previousIndex[i] = incubationTask->index[i]; + } else { + QQmlDelegateModelPrivate * const model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); + Compositor::iterator it = model->m_compositor.find( + Compositor::Cache, model->m_cache.indexOf(m_cacheItem)); + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) + m_currentIndex[i] = m_previousIndex[i] = it.index[i]; + } + + if (!cacheItem->metaType->metaObject) + cacheItem->metaType->initializeMetaObject(); + + QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject; + cacheItem->metaType->metaObject->addref(); +} + +/*! + \qmlattachedproperty int QtQml.Models2::DelegateModel::model + + This attached property holds the visual data model this delegate instance belongs to. + + It is attached to each instance of the delegate. +*/ + +QQmlDelegateModel *QQmlDelegateModelAttached::model() const +{ + return m_cacheItem ? m_cacheItem->metaType->model : 0; +} + +/*! + \qmlattachedproperty stringlist QtQml.Models2::DelegateModel::groups + + This attached property holds the name of DelegateModelGroups the item belongs to. + + It is attached to each instance of the delegate. +*/ + +QStringList QQmlDelegateModelAttached::groups() const +{ + QStringList groups; + + if (!m_cacheItem) + return groups; + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { + if (m_cacheItem->groups & (1 << i)) + groups.append(m_cacheItem->metaType->groupNames.at(i - 1)); + } + return groups; +} + +void QQmlDelegateModelAttached::setGroups(const QStringList &groups) +{ + if (!m_cacheItem) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); + + const int groupFlags = model->m_cacheMetaType->parseGroups(groups); + const int cacheIndex = model->m_cache.indexOf(m_cacheItem); + Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); + model->setGroups(it, 1, Compositor::Cache, groupFlags); +} + +/*! + \qmlattachedproperty bool QtQml.Models2::DelegateModel::isUnresolved + + This attached property holds whether the visual item is bound to a data model index. + Returns true if the item is not bound to the model, and false if it is. + + An unresolved item can be bound to the data model using the DelegateModelGroup::resolve() + function. + + It is attached to each instance of the delegate. +*/ + +bool QQmlDelegateModelAttached::isUnresolved() const +{ + if (!m_cacheItem) + return false; + + return m_cacheItem->groups & Compositor::UnresolvedFlag; +} + +/*! + \qmlattachedproperty int QtQml.Models2::DelegateModel::inItems + + This attached property holds whether the item belongs to the default \l items DelegateModelGroup. + + Changing this property will add or remove the item from the items group. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty int QtQml.Models2::DelegateModel::itemsIndex + + This attached property holds the index of the item in the default \l items DelegateModelGroup. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty int QtQml.Models2::DelegateModel::inPersistedItems + + This attached property holds whether the item belongs to the \l persistedItems DelegateModelGroup. + + Changing this property will add or remove the item from the items group. Change with caution + as removing an item from the persistedItems group will destroy the current instance if it is + not referenced by a model. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty int QtQml.Models2::DelegateModel::persistedItemsIndex + + This attached property holds the index of the item in the \l persistedItems DelegateModelGroup. + + It is attached to each instance of the delegate. +*/ + +void QQmlDelegateModelAttached::emitChanges() +{ + const int groupChanges = m_previousGroups ^ m_cacheItem->groups; + m_previousGroups = m_cacheItem->groups; + + int indexChanges = 0; + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { + if (m_previousIndex[i] != m_currentIndex[i]) { + m_previousIndex[i] = m_currentIndex[i]; + indexChanges |= (1 << i); + } + } + + int notifierId = 0; + const QMetaObject *meta = metaObject(); + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { + if (groupChanges & (1 << i)) + QMetaObject::activate(this, meta, notifierId, 0); + } + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { + if (indexChanges & (1 << i)) + QMetaObject::activate(this, meta, notifierId, 0); + } + + if (groupChanges) + emit groupsChanged(); +} + +//============================================================================ + +void QQmlDelegateModelGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::Group g) +{ + Q_ASSERT(!model); + model = m; + group = g; +} + +bool QQmlDelegateModelGroupPrivate::isChangedConnected() +{ + Q_Q(QQmlDelegateModelGroup); + IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QQmlV8Handle &,const QQmlV8Handle &)); +} + +void QQmlDelegateModelGroupPrivate::emitChanges(QV8Engine *engine) +{ + Q_Q(QQmlDelegateModelGroup); + if (isChangedConnected() && !changeSet.isEmpty()) { + v8::HandleScope handleScope; + v8::Context::Scope contextScope(engine->context()); + v8::Local removed = engineData(engine)->array(engine, changeSet.removes()); + v8::Local inserted = engineData(engine)->array(engine, changeSet.inserts()); + emit q->changed(QQmlV8Handle::fromHandle(removed), QQmlV8Handle::fromHandle(inserted)); + } + if (changeSet.difference() != 0) + emit q->countChanged(); +} + +void QQmlDelegateModelGroupPrivate::emitModelUpdated(bool reset) +{ + for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->emitModelUpdated(changeSet, reset); + changeSet.clear(); +} + +void QQmlDelegateModelGroupPrivate::createdPackage(int index, QQuickPackage *package) +{ + for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->createdPackage(index, package); +} + +void QQmlDelegateModelGroupPrivate::initPackage(int index, QQuickPackage *package) +{ + for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->initPackage(index, package); +} + +void QQmlDelegateModelGroupPrivate::destroyingPackage(QQuickPackage *package) +{ + for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->destroyingPackage(package); +} + +/*! + \qmltype DelegateModelGroup + \instantiates QQmlDelegateModelGroup + \inqmlmodule QtQuick 2 + \ingroup qtquick-models + \brief Encapsulates a filtered set of visual data items + + The DelegateModelGroup type provides a means to address the model data of a DelegateModel's + delegate items, as well as sort and filter these delegate items. + + The initial set of instantiable delegate items in a DelegateModel is represented + by its \l {QtQml.Models2::DelegateModel::items}{items} group, which normally directly reflects + the contents of the model assigned to DelegateModel::model. This set can be changed to + the contents of any other member of DelegateModel::groups by assigning the \l name of that + DelegateModelGroup to the DelegateModel::filterOnGroup property. + + The data of an item in a DelegateModelGroup can be accessed using the get() function, which returns + information about group membership and indexes as well as model data. In combination + with the move() function this can be used to implement view sorting, with remove() to filter + items out of a view, or with setGroups() and \l Package delegates to categorize items into + different views. + + Data from models can be supplemented by inserting data directly into a DelegateModelGroup + with the insert() function. This can be used to introduce mock items into a view, or + placeholder items that are later \l {resolve()}{resolved} to real model data when it becomes + available. + + Delegate items can also be be instantiated directly from a DelegateModelGroup using the + create() function, making it possible to use DelegateModel without an accompanying view + type or to cherry-pick specific items that should be instantiated irregardless of whether + they're currently within a view's visible area. + + \sa {QML Dynamic View Ordering Tutorial} +*/ +/*! + \qmltype DelegateModelGroup + \instantiates QQmlDelegateModelGroup + \inqmlmodule QtQml.Models 2 + \brief Encapsulates a filtered set of visual data items + + The DelegateModelGroup type provides a means to address the model data of a DelegateModel's + delegate items, as well as sort and filter these delegate items. + + This element is also available as DelegateModelGroup in the QtQuick module. For full details, + see the \l DelegateModelGroup documentation. + + \sa {QtQuick::DelegateModelGroup} +*/ + + +QQmlDelegateModelGroup::QQmlDelegateModelGroup(QObject *parent) + : QObject(*new QQmlDelegateModelGroupPrivate, parent) +{ +} + +QQmlDelegateModelGroup::QQmlDelegateModelGroup( + const QString &name, QQmlDelegateModel *model, int index, QObject *parent) + : QObject(*new QQmlDelegateModelGroupPrivate, parent) +{ + Q_D(QQmlDelegateModelGroup); + d->name = name; + d->setModel(model, Compositor::Group(index)); +} + +QQmlDelegateModelGroup::~QQmlDelegateModelGroup() +{ +} + +/*! + \qmlproperty string QtQml.Models2::DelegateModelGroup::name + + This property holds the name of the group. + + Each group in a model must have a unique name starting with a lower case letter. +*/ + +QString QQmlDelegateModelGroup::name() const +{ + Q_D(const QQmlDelegateModelGroup); + return d->name; +} + +void QQmlDelegateModelGroup::setName(const QString &name) +{ + Q_D(QQmlDelegateModelGroup); + if (d->model) + return; + if (d->name != name) { + d->name = name; + emit nameChanged(); + } +} + +/*! + \qmlproperty int QtQml.Models2::DelegateModelGroup::count + + This property holds the number of items in the group. +*/ + +int QQmlDelegateModelGroup::count() const +{ + Q_D(const QQmlDelegateModelGroup); + if (!d->model) + return 0; + return QQmlDelegateModelPrivate::get(d->model)->m_compositor.count(d->group); +} + +/*! + \qmlproperty bool QtQml.Models2::DelegateModelGroup::includeByDefault + + This property holds whether new items are assigned to this group by default. +*/ + +bool QQmlDelegateModelGroup::defaultInclude() const +{ + Q_D(const QQmlDelegateModelGroup); + return d->defaultInclude; +} + +void QQmlDelegateModelGroup::setDefaultInclude(bool include) +{ + Q_D(QQmlDelegateModelGroup); + if (d->defaultInclude != include) { + d->defaultInclude = include; + + if (d->model) { + if (include) + QQmlDelegateModelPrivate::get(d->model)->m_compositor.setDefaultGroup(d->group); + else + QQmlDelegateModelPrivate::get(d->model)->m_compositor.clearDefaultGroup(d->group); + } + emit defaultIncludeChanged(); + } +} + +/*! + \qmlmethod object QtQml.Models2::DelegateModelGroup::get(int index) + + Returns a javascript object describing the item at \a index in the group. + + The returned object contains the same information that is available to a delegate from the + DelegateModel attached as well as the model for that item. It has the properties: + + \list + \li \b model The model data of the item. This is the same as the model context property in + a delegate + \li \b groups A list the of names of groups the item is a member of. This property can be + written to change the item's membership. + \li \b inItems Whether the item belongs to the \l {QtQml.Models2::DelegateModel::items}{items} group. + Writing to this property will add or remove the item from the group. + \li \b itemsIndex The index of the item within the \l {QtQml.Models2::DelegateModel::items}{items} group. + \li \b {in} Whether the item belongs to the dynamic group \e groupName. Writing to + this property will add or remove the item from the group. + \li \b {Index} The index of the item within the dynamic group \e groupName. + \li \b isUnresolved Whether the item is bound to an index in the model assigned to + DelegateModel::model. Returns true if the item is not bound to the model, and false if it is. + \endlist +*/ + +QQmlV8Handle QQmlDelegateModelGroup::get(int index) +{ + Q_D(QQmlDelegateModelGroup); + if (!d->model) + return QQmlV8Handle::fromHandle(v8::Undefined());; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (!model->m_context->isValid()) { + return QQmlV8Handle::fromHandle(v8::Undefined()); + } else if (index < 0 || index >= model->m_compositor.count(d->group)) { + qmlInfo(this) << tr("get: index out of range"); + return QQmlV8Handle::fromHandle(v8::Undefined()); + } + + Compositor::iterator it = model->m_compositor.find(d->group, index); + QQmlDelegateModelItem *cacheItem = it->inCache() + ? model->m_cache.at(it.cacheIndex) + : 0; + + if (!cacheItem) { + cacheItem = model->m_adaptorModel.createItem( + model->m_cacheMetaType, model->m_context->engine(), it.modelIndex()); + if (!cacheItem) + return QQmlV8Handle::fromHandle(v8::Undefined()); + cacheItem->groups = it->flags; + + model->m_cache.insert(it.cacheIndex, cacheItem); + model->m_compositor.setFlags(it, 1, Compositor::CacheFlag); + } + + if (model->m_cacheMetaType->constructor.IsEmpty()) + model->m_cacheMetaType->initializeConstructor(); + v8::Local handle = model->m_cacheMetaType->constructor->NewInstance(); + handle->SetExternalResource(cacheItem); + ++cacheItem->scriptRef; + + return QQmlV8Handle::fromHandle(handle); +} + +bool QQmlDelegateModelGroupPrivate::parseIndex( + const v8::Local &value, int *index, Compositor::Group *group) const +{ + if (value->IsInt32()) { + *index = value->Int32Value(); + return true; + } else if (value->IsObject()) { + v8::Local object = value->ToObject(); + QQmlDelegateModelItem * const cacheItem = v8_resource_cast(object); + if (QQmlDelegateModelPrivate *model = cacheItem && cacheItem->metaType->model + ? QQmlDelegateModelPrivate::get(cacheItem->metaType->model) + : 0) { + *index = model->m_cache.indexOf(cacheItem); + *group = Compositor::Cache; + return true; + } + } + return false; +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::insert(int index, jsdict data, array groups = undefined) + \qmlmethod QtQml.Models2::DelegateModelGroup::insert(jsdict data, var groups = undefined) + + Creates a new entry at \a index in a DelegateModel with the values from \a data that + correspond to roles in the model assigned to DelegateModel::model. + + If no index is supplied the data is appended to the model. + + The optional \a groups parameter identifies the groups the new entry should belong to, + if unspecified this is equal to the group insert was called on. + + Data inserted into a DelegateModel can later be merged with an existing entry in + DelegateModel::model using the \l resolve() function. This can be used to create placeholder + items that are later replaced by actual data. +*/ + +void QQmlDelegateModelGroup::insert(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + + int index = model->m_compositor.count(d->group); + Compositor::Group group = d->group; + + if (args->Length() == 0) + return; + + int i = 0; + v8::Local v = (*args)[i]; + if (d->parseIndex(v, &index, &group)) { + if (index < 0 || index > model->m_compositor.count(group)) { + qmlInfo(this) << tr("insert: index out of range"); + return; + } + if (++i == args->Length()) + return; + v = (*args)[i]; + } + + Compositor::insert_iterator before = index < model->m_compositor.count(group) + ? model->m_compositor.findInsertPosition(group, index) + : model->m_compositor.end(); + + int groups = 1 << d->group; + if (++i < args->Length()) + groups |= model->m_cacheMetaType->parseGroups((*args)[i]); + + if (v->IsArray()) { + return; + } else if (v->IsObject()) { + model->insert(before, v->ToObject(), groups); + model->emitChanges(); + } +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::create(int index) + \qmlmethod QtQml.Models2::DelegateModelGroup::create(int index, jsdict data, array groups = undefined) + \qmlmethod QtQml.Models2::DelegateModelGroup::create(jsdict data, array groups = undefined) + + Returns a reference to the instantiated item at \a index in the group. + + If a \a data object is provided it will be \l {insert}{inserted} at \a index and an item + referencing this new entry will be returned. The optional \a groups parameter identifies + the groups the new entry should belong to, if unspecified this is equal to the group create() + was called on. + + All items returned by create are added to the + \l {QtQml.Models2::DelegateModel::persistedItems}{persistedItems} group. Items in this + group remain instantiated when not referenced by any view. +*/ + +void QQmlDelegateModelGroup::create(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + if (!d->model) + return; + + if (args->Length() == 0) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + + int index = model->m_compositor.count(d->group); + Compositor::Group group = d->group; + + int i = 0; + v8::Local v = (*args)[i]; + if (d->parseIndex(v, &index, &group)) + ++i; + + if (i < args->Length() && index >= 0 && index <= model->m_compositor.count(group)) { + v = (*args)[i]; + if (v->IsObject()) { + int groups = 1 << d->group; + if (++i < args->Length()) + groups |= model->m_cacheMetaType->parseGroups((*args)[i]); + + Compositor::insert_iterator before = index < model->m_compositor.count(group) + ? model->m_compositor.findInsertPosition(group, index) + : model->m_compositor.end(); + + index = before.index[d->group]; + group = d->group; + + if (!model->insert(before, v->ToObject(), groups)) { + return; + } + } + } + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("create: index out of range"); + return; + } + + QObject *object = model->object(group, index, false); + if (object) { + QVector inserts; + Compositor::iterator it = model->m_compositor.find(group, index); + model->m_compositor.setFlags(it, 1, d->group, Compositor::PersistedFlag, &inserts); + model->itemsInserted(inserts); + model->m_cache.at(it.cacheIndex)->releaseObject(); + } + + args->returnValue(args->engine()->newQObject(object)); + model->emitChanges(); +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::resolve(int from, int to) + + Binds an unresolved item at \a from to an item in DelegateModel::model at index \a to. + + Unresolved items are entries whose data has been \l {insert()}{inserted} into a DelegateModelGroup + instead of being derived from a DelegateModel::model index. Resolving an item will replace + the item at the target index with the unresolved item. A resolved an item will reflect the data + of the source model at its bound index and will move when that index moves like any other item. + + If a new item is replaced in the DelegateModelGroup onChanged() handler its insertion and + replacement will be communicated to views as an atomic operation, creating the appearance + that the model contents have not changed, or if the unresolved and model item are not adjacent + that the previously unresolved item has simply moved. + +*/ +void QQmlDelegateModelGroup::resolve(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + if (!d->model) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + + if (args->Length() < 2) + return; + + int from = -1; + int to = -1; + Compositor::Group fromGroup = d->group; + Compositor::Group toGroup = d->group; + + v8::Local v = (*args)[0]; + if (d->parseIndex(v, &from, &fromGroup)) { + if (from < 0 || from >= model->m_compositor.count(fromGroup)) { + qmlInfo(this) << tr("resolve: from index out of range"); + return; + } + } else { + qmlInfo(this) << tr("resolve: from index invalid"); + return; + } + + v = (*args)[1]; + if (d->parseIndex(v, &to, &toGroup)) { + if (to < 0 || to >= model->m_compositor.count(toGroup)) { + qmlInfo(this) << tr("resolve: to index out of range"); + return; + } + } else { + qmlInfo(this) << tr("resolve: to index invalid"); + return; + } + + Compositor::iterator fromIt = model->m_compositor.find(fromGroup, from); + Compositor::iterator toIt = model->m_compositor.find(toGroup, to); + + if (!fromIt->isUnresolved()) { + qmlInfo(this) << tr("resolve: from is not an unresolved item"); + return; + } + if (!toIt->list) { + qmlInfo(this) << tr("resolve: to is not a model item"); + return; + } + + const int unresolvedFlags = fromIt->flags; + const int resolvedFlags = toIt->flags; + const int resolvedIndex = toIt.modelIndex(); + void * const resolvedList = toIt->list; + + QQmlDelegateModelItem *cacheItem = model->m_cache.at(fromIt.cacheIndex); + cacheItem->groups &= ~Compositor::UnresolvedFlag; + + if (toIt.cacheIndex > fromIt.cacheIndex) + toIt.decrementIndexes(1, unresolvedFlags); + if (!toIt->inGroup(fromGroup) || toIt.index[fromGroup] > from) + from += 1; + + model->itemsMoved( + QVector() << Compositor::Remove(fromIt, 1, unresolvedFlags, 0), + QVector() << Compositor::Insert(toIt, 1, unresolvedFlags, 0)); + model->itemsInserted( + QVector() << Compositor::Insert(toIt, 1, (resolvedFlags & ~unresolvedFlags) | Compositor::CacheFlag)); + toIt.incrementIndexes(1, resolvedFlags | unresolvedFlags); + model->itemsRemoved(QVector() << Compositor::Remove(toIt, 1, resolvedFlags)); + + model->m_compositor.setFlags(toGroup, to, 1, unresolvedFlags & ~Compositor::UnresolvedFlag); + model->m_compositor.clearFlags(fromGroup, from, 1, unresolvedFlags); + + if (resolvedFlags & Compositor::CacheFlag) + model->m_compositor.insert(Compositor::Cache, toIt.cacheIndex, resolvedList, resolvedIndex, 1, Compositor::CacheFlag); + + Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); + + if (!cacheItem->isReferenced()) { + Q_ASSERT(toIt.cacheIndex == model->m_cache.indexOf(cacheItem)); + model->m_cache.removeAt(toIt.cacheIndex); + model->m_compositor.clearFlags(Compositor::Cache, toIt.cacheIndex, 1, Compositor::CacheFlag); + delete cacheItem; + Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); + } else { + cacheItem->resolveIndex(model->m_adaptorModel, resolvedIndex); + if (cacheItem->attached) + cacheItem->attached->emitUnresolvedChanged(); + } + + model->emitChanges(); +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::remove(int index, int count) + + Removes \a count items starting at \a index from the group. +*/ + +void QQmlDelegateModelGroup::remove(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + if (!d->model) + return; + Compositor::Group group = d->group; + int index = -1; + int count = 1; + + if (args->Length() == 0) + return; + + int i = 0; + v8::Local v = (*args)[i]; + if (!d->parseIndex(v, &index, &group)) { + qmlInfo(this) << tr("remove: invalid index"); + return; + } + + if (++i < args->Length()) { + v = (*args)[i]; + if (v->IsInt32()) + count = v->Int32Value(); + } + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("remove: index out of range"); + } else if (count != 0) { + Compositor::iterator it = model->m_compositor.find(group, index); + if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { + qmlInfo(this) << tr("remove: invalid count"); + } else { + model->removeGroups(it, count, d->group, 1 << d->group); + } + } +} + +bool QQmlDelegateModelGroupPrivate::parseGroupArgs( + QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const +{ + if (!model || !QQmlDelegateModelPrivate::get(model)->m_cacheMetaType) + return false; + + if (args->Length() < 2) + return false; + + int i = 0; + v8::Local v = (*args)[i]; + if (!parseIndex(v, index, group)) + return false; + + v = (*args)[++i]; + if (v->IsInt32()) { + *count = v->Int32Value(); + + if (++i == args->Length()) + return false; + v = (*args)[i]; + } + + *groups = QQmlDelegateModelPrivate::get(model)->m_cacheMetaType->parseGroups(v); + + return true; +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::addGroups(int index, int count, stringlist groups) + + Adds \a count items starting at \a index to \a groups. +*/ + +void QQmlDelegateModelGroup::addGroups(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + Compositor::Group group = d->group; + int index = -1; + int count = 1; + int groups = 0; + + if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("addGroups: index out of range"); + } else if (count != 0) { + Compositor::iterator it = model->m_compositor.find(group, index); + if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { + qmlInfo(this) << tr("addGroups: invalid count"); + } else { + model->addGroups(it, count, d->group, groups); + } + } +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::removeGroups(int index, int count, stringlist groups) + + Removes \a count items starting at \a index from \a groups. +*/ + +void QQmlDelegateModelGroup::removeGroups(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + Compositor::Group group = d->group; + int index = -1; + int count = 1; + int groups = 0; + + if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("removeGroups: index out of range"); + } else if (count != 0) { + Compositor::iterator it = model->m_compositor.find(group, index); + if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { + qmlInfo(this) << tr("removeGroups: invalid count"); + } else { + model->removeGroups(it, count, d->group, groups); + } + } +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::setGroups(int index, int count, stringlist groups) + + Sets the \a groups \a count items starting at \a index belong to. +*/ + +void QQmlDelegateModelGroup::setGroups(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + Compositor::Group group = d->group; + int index = -1; + int count = 1; + int groups = 0; + + if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("setGroups: index out of range"); + } else if (count != 0) { + Compositor::iterator it = model->m_compositor.find(group, index); + if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { + qmlInfo(this) << tr("setGroups: invalid count"); + } else { + model->setGroups(it, count, d->group, groups); + } + } +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::setGroups(int index, int count, stringlist groups) + + Sets the \a groups \a count items starting at \a index belong to. +*/ + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::move(var from, var to, int count) + + Moves \a count at \a from in a group \a to a new position. +*/ + +void QQmlDelegateModelGroup::move(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + + if (args->Length() < 2) + return; + + Compositor::Group fromGroup = d->group; + Compositor::Group toGroup = d->group; + int from = -1; + int to = -1; + int count = 1; + + if (!d->parseIndex((*args)[0], &from, &fromGroup)) { + qmlInfo(this) << tr("move: invalid from index"); + return; + } + + if (!d->parseIndex((*args)[1], &to, &toGroup)) { + qmlInfo(this) << tr("move: invalid to index"); + return; + } + + if (args->Length() > 2) { + v8::Local v = (*args)[2]; + if (v->IsInt32()) + count = v->Int32Value(); + } + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + + if (count < 0) { + qmlInfo(this) << tr("move: invalid count"); + } else if (from < 0 || from + count > model->m_compositor.count(fromGroup)) { + qmlInfo(this) << tr("move: from index out of range"); + } else if (!model->m_compositor.verifyMoveTo(fromGroup, from, toGroup, to, count, d->group)) { + qmlInfo(this) << tr("move: to index out of range"); + } else if (count > 0) { + QVector removes; + QVector inserts; + + model->m_compositor.move(fromGroup, from, toGroup, to, count, d->group, &removes, &inserts); + model->itemsMoved(removes, inserts); + model->emitChanges(); + } + +} + +/*! + \qmlsignal QtQml.Models2::DelegateModelGroup::onChanged(array removed, array inserted) + + This handler is called when items have been removed from or inserted into the group. + + Each object in the \a removed and \a inserted arrays has two values; the \e index of the first + item inserted or removed and a \e count of the number of consecutive items inserted or removed. + + Each index is adjusted for previous changes with all removed items preceding any inserted + items. +*/ + +//============================================================================ + +QQmlPartsModel::QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent) + : QQmlInstanceModel(*new QObjectPrivate, parent) + , m_model(model) + , m_part(part) + , m_compositorGroup(Compositor::Cache) + , m_inheritGroup(true) +{ + QQmlDelegateModelPrivate *d = QQmlDelegateModelPrivate::get(m_model); + if (d->m_cacheMetaType) { + QQmlDelegateModelGroupPrivate::get(d->m_groups[1])->emitters.insert(this); + m_compositorGroup = Compositor::Default; + } else { + d->m_pendingParts.insert(this); + } +} + +QQmlPartsModel::~QQmlPartsModel() +{ +} + +QString QQmlPartsModel::filterGroup() const +{ + if (m_inheritGroup) + return m_model->filterGroup(); + return m_filterGroup; +} + +void QQmlPartsModel::setFilterGroup(const QString &group) +{ + if (QQmlDelegateModelPrivate::get(m_model)->m_transaction) { + qmlInfo(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); + return; + } + + if (m_filterGroup != group || m_inheritGroup) { + m_filterGroup = group; + m_inheritGroup = false; + updateFilterGroup(); + + emit filterGroupChanged(); + } +} + +void QQmlPartsModel::resetFilterGroup() +{ + if (!m_inheritGroup) { + m_inheritGroup = true; + updateFilterGroup(); + emit filterGroupChanged(); + } +} + +void QQmlPartsModel::updateFilterGroup() +{ + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + if (!model->m_cacheMetaType) + return; + + if (m_inheritGroup) { + if (m_filterGroup == model->m_filterGroup) + return; + m_filterGroup = model->m_filterGroup; + } + + QQmlListCompositor::Group previousGroup = m_compositorGroup; + m_compositorGroup = Compositor::Default; + QQmlDelegateModelGroupPrivate::get(model->m_groups[Compositor::Default])->emitters.insert(this); + for (int i = 1; i < model->m_groupCount; ++i) { + if (m_filterGroup == model->m_cacheMetaType->groupNames.at(i - 1)) { + m_compositorGroup = Compositor::Group(i); + break; + } + } + + QQmlDelegateModelGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this); + if (m_compositorGroup != previousGroup) { + QVector removes; + QVector inserts; + model->m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); + + QQmlChangeSet changeSet; + changeSet.move(removes, inserts); + if (!changeSet.isEmpty()) + emit modelUpdated(changeSet, false); + + if (changeSet.difference() != 0) + emit countChanged(); + } +} + +void QQmlPartsModel::updateFilterGroup( + Compositor::Group group, const QQmlChangeSet &changeSet) +{ + if (!m_inheritGroup) + return; + + m_compositorGroup = group; + QQmlDelegateModelGroupPrivate::get(QQmlDelegateModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this); + + if (!changeSet.isEmpty()) + emit modelUpdated(changeSet, false); + + if (changeSet.difference() != 0) + emit countChanged(); + + emit filterGroupChanged(); +} + +int QQmlPartsModel::count() const +{ + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + return model->m_delegate + ? model->m_compositor.count(m_compositorGroup) + : 0; +} + +bool QQmlPartsModel::isValid() const +{ + return m_model->isValid(); +} + +QObject *QQmlPartsModel::object(int index, bool asynchronous) +{ + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + + if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) { + qWarning() << "DelegateModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup); + return 0; + } + + QObject *object = model->object(m_compositorGroup, index, asynchronous); + + if (QQuickPackage *package = qmlobject_cast(object)) { + QObject *part = package->part(m_part); + if (!part) + return 0; + m_packaged.insertMulti(part, package); + return part; + } + + model->release(object); + if (!model->m_delegateValidated) { + if (object) + qmlInfo(model->m_delegate) << tr("Delegate component must be Package type."); + model->m_delegateValidated = true; + } + + return 0; +} + +QQmlInstanceModel::ReleaseFlags QQmlPartsModel::release(QObject *item) +{ + QQmlInstanceModel::ReleaseFlags flags = 0; + + QHash::iterator it = m_packaged.find(item); + if (it != m_packaged.end()) { + QQuickPackage *package = *it; + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + flags = model->release(package); + m_packaged.erase(it); + if (!m_packaged.contains(item)) + flags &= ~Referenced; + if (flags & Destroyed) + QQmlDelegateModelPrivate::get(m_model)->emitDestroyingPackage(package); + } + return flags; +} + +QString QQmlPartsModel::stringValue(int index, const QString &role) +{ + return QQmlDelegateModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role); +} + +void QQmlPartsModel::setWatchedRoles(QList roles) +{ + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + model->m_adaptorModel.replaceWatchedRoles(m_watchedRoles, roles); + m_watchedRoles = roles; +} + +int QQmlPartsModel::indexOf(QObject *item, QObject *) const +{ + QHash::const_iterator it = m_packaged.find(item); + if (it != m_packaged.end()) { + if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(*it)) + return cacheItem->groupIndex(m_compositorGroup); + } + return -1; +} + +void QQmlPartsModel::createdPackage(int index, QQuickPackage *package) +{ + emit createdItem(index, package->part(m_part)); +} + +void QQmlPartsModel::initPackage(int index, QQuickPackage *package) +{ + emit initItem(index, package->part(m_part)); +} + +void QQmlPartsModel::destroyingPackage(QQuickPackage *package) +{ + QObject *item = package->part(m_part); + Q_ASSERT(!m_packaged.contains(item)); + emit destroyingItem(item); +} + +void QQmlPartsModel::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) +{ + emit modelUpdated(changeSet, reset); + if (changeSet.difference() != 0) + emit countChanged(); +} + +//============================================================================ + +v8::Handle get_change_index(v8::Local, const v8::AccessorInfo &info) +{ + return info.This()->GetInternalField(0); +} + +v8::Handle get_change_count(v8::Local, const v8::AccessorInfo &info) +{ + return info.This()->GetInternalField(1); +} + +v8::Handle get_change_moveId(v8::Local, const v8::AccessorInfo &info) +{ + return info.This()->GetInternalField(2); +} + +class QQmlDelegateModelGroupChangeArray : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(ChangeSetArrayType) +public: + QQmlDelegateModelGroupChangeArray(QV8Engine *engine) + : QV8ObjectResource(engine) + { + } + + virtual quint32 count() const = 0; + virtual const QQmlChangeSet::Change &at(int index) const = 0; + + static v8::Handle get_change(quint32 index, const v8::AccessorInfo &info) + { + QQmlDelegateModelGroupChangeArray *array = v8_resource_cast(info.This()); + V8ASSERT_TYPE(array, "Not a valid change array"); + + if (index >= array->count()) + return v8::Undefined(); + + const QQmlChangeSet::Change &change = array->at(index); + + v8::Local object = engineData(array->engine)->constructorChange->NewInstance(); + object->SetInternalField(0, v8::Int32::New(change.index)); + object->SetInternalField(1, v8::Int32::New(change.count)); + if (change.isMove()) + object->SetInternalField(2, v8::Int32::New(change.moveId)); + + return object; + } + + static v8::Handle get_length(v8::Local, const v8::AccessorInfo &info) + { + QQmlDelegateModelGroupChangeArray *array = v8_resource_cast(info.This()); + V8ASSERT_TYPE(array, "Not a valid change array"); + + return v8::Integer::New(array->count()); + } + + static v8::Local constructor() + { + v8::Local changeArray = v8::FunctionTemplate::New(); + changeArray->InstanceTemplate()->SetHasExternalResource(true); + changeArray->InstanceTemplate()->SetIndexedPropertyHandler(get_change); + changeArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), get_length); + return changeArray->GetFunction(); + } +}; + +class QQmlDelegateModelGroupRemoveArray : public QQmlDelegateModelGroupChangeArray +{ +public: + QQmlDelegateModelGroupRemoveArray(QV8Engine *engine, const QVector &changes) + : QQmlDelegateModelGroupChangeArray(engine) + , changes(changes) + { + } + + quint32 count() const { return changes.count(); } + const QQmlChangeSet::Change &at(int index) const { return changes.at(index); } + +private: + QVector changes; +}; + +class QQmlDelegateModelGroupInsertArray : public QQmlDelegateModelGroupChangeArray +{ +public: + QQmlDelegateModelGroupInsertArray(QV8Engine *engine, const QVector &changes) + : QQmlDelegateModelGroupChangeArray(engine) + , changes(changes) + { + } + + quint32 count() const { return changes.count(); } + const QQmlChangeSet::Change &at(int index) const { return changes.at(index); } + +private: + QVector changes; +}; + +QQmlDelegateModelEngineData::QQmlDelegateModelEngineData(QV8Engine *) +{ + strings = qPersistentNew(v8::Array::New(StringCount)); + strings->Set(Model, v8::String::New("model")); + strings->Set(Groups, v8::String::New("groups")); + strings->Set(IsUnresolved, v8::String::New("isUnresolved")); + strings->Set(ItemsIndex, v8::String::New("itemsIndex")); + strings->Set(PersistedItemsIndex, v8::String::New("persistedItemsIndex")); + strings->Set(InItems, v8::String::New("inItems")); + strings->Set(InPersistedItems, v8::String::New("inPersistedItems")); + + v8::Local change = v8::FunctionTemplate::New(); + change->InstanceTemplate()->SetAccessor(v8::String::New("index"), get_change_index); + change->InstanceTemplate()->SetAccessor(v8::String::New("count"), get_change_count); + change->InstanceTemplate()->SetAccessor(v8::String::New("moveId"), get_change_moveId); + change->InstanceTemplate()->SetInternalFieldCount(3); + constructorChange = qPersistentNew(change->GetFunction()); + constructorChangeArray = qPersistentNew(QQmlDelegateModelGroupChangeArray::constructor()); +} + +QQmlDelegateModelEngineData::~QQmlDelegateModelEngineData() +{ + qPersistentDispose(strings); + qPersistentDispose(constructorChange); + qPersistentDispose(constructorChangeArray); +} + +v8::Local QQmlDelegateModelEngineData::array( + QV8Engine *engine, const QVector &changes) +{ + v8::Local array = constructorChangeArray->NewInstance(); + array->SetExternalResource(new QQmlDelegateModelGroupRemoveArray(engine, changes)); + return array; +} + +v8::Local QQmlDelegateModelEngineData::array( + QV8Engine *engine, const QVector &changes) +{ + v8::Local array = constructorChangeArray->NewInstance(); + array->SetExternalResource(new QQmlDelegateModelGroupInsertArray(engine, changes)); + return array; +} + +QT_END_NAMESPACE + diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h new file mode 100644 index 0000000000..5702c59787 --- /dev/null +++ b/src/qml/types/qqmldelegatemodel_p.h @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDATAMODEL_P_H +#define QQMLDATAMODEL_P_H + +#include +#include +#include + +#include +#include + +#include +#include + +Q_DECLARE_METATYPE(QModelIndex) + +QT_BEGIN_NAMESPACE + +class QQmlChangeSet; +class QQmlComponent; +class QQuickPackage; +class QQmlV8Function; +class QQmlDelegateModelGroup; +class QQmlDelegateModelAttached; +class QQmlDelegateModelPrivate; + + +class Q_QML_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public QQmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlDelegateModel) + + Q_PROPERTY(QVariant model READ model WRITE setModel) + Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate) + Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) + Q_PROPERTY(QQmlDelegateModelGroup *items READ items CONSTANT) //TODO : worth renaming? + Q_PROPERTY(QQmlDelegateModelGroup *persistedItems READ persistedItems CONSTANT) + Q_PROPERTY(QQmlListProperty groups READ groups CONSTANT) + Q_PROPERTY(QObject *parts READ parts CONSTANT) + Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) + Q_CLASSINFO("DefaultProperty", "delegate") + Q_INTERFACES(QQmlParserStatus) +public: + QQmlDelegateModel(); + QQmlDelegateModel(QQmlContext *, QObject *parent=0); + virtual ~QQmlDelegateModel(); + + void classBegin(); + void componentComplete(); + + QVariant model() const; + void setModel(const QVariant &); + + QQmlComponent *delegate() const; + void setDelegate(QQmlComponent *); + + QVariant rootIndex() const; + void setRootIndex(const QVariant &root); + + Q_INVOKABLE QVariant modelIndex(int idx) const; + Q_INVOKABLE QVariant parentModelIndex() const; + + int count() const; + bool isValid() const { return delegate() != 0; } + QObject *object(int index, bool asynchronous=false); + ReleaseFlags release(QObject *object); + void cancel(int index); + virtual QString stringValue(int index, const QString &role); + virtual void setWatchedRoles(QList roles); + + int indexOf(QObject *object, QObject *objectContext) const; + + QString filterGroup() const; + void setFilterGroup(const QString &group); + void resetFilterGroup(); + + QQmlDelegateModelGroup *items(); + QQmlDelegateModelGroup *persistedItems(); + QQmlListProperty groups(); + QObject *parts(); + + bool event(QEvent *); + + static QQmlDelegateModelAttached *qmlAttachedProperties(QObject *obj); + +Q_SIGNALS: + void filterGroupChanged(); + void defaultGroupsChanged(); + void rootIndexChanged(); + +private Q_SLOTS: + void _q_itemsChanged(int index, int count, const QVector &roles); + void _q_itemsInserted(int index, int count); + void _q_itemsRemoved(int index, int count); + void _q_itemsMoved(int from, int to, int count); + void _q_modelReset(); + void _q_rowsInserted(const QModelIndex &,int,int); + void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end); + void _q_rowsRemoved(const QModelIndex &,int,int); + void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int); + void _q_dataChanged(const QModelIndex&,const QModelIndex&,const QVector &); + void _q_layoutChanged(); + +private: + Q_DISABLE_COPY(QQmlDelegateModel) +}; + +class QQmlDelegateModelGroupPrivate; +class Q_QML_PRIVATE_EXPORT QQmlDelegateModelGroup : public QObject +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(bool includeByDefault READ defaultInclude WRITE setDefaultInclude NOTIFY defaultIncludeChanged) +public: + QQmlDelegateModelGroup(QObject *parent = 0); + QQmlDelegateModelGroup(const QString &name, QQmlDelegateModel *model, int compositorType, QObject *parent = 0); + ~QQmlDelegateModelGroup(); + + QString name() const; + void setName(const QString &name); + + int count() const; + + bool defaultInclude() const; + void setDefaultInclude(bool include); + + Q_INVOKABLE QQmlV8Handle get(int index); + +public Q_SLOTS: + void insert(QQmlV8Function *); + void create(QQmlV8Function *); + void resolve(QQmlV8Function *); + void remove(QQmlV8Function *); + void addGroups(QQmlV8Function *); + void removeGroups(QQmlV8Function *); + void setGroups(QQmlV8Function *); + void move(QQmlV8Function *); + +Q_SIGNALS: + void countChanged(); + void nameChanged(); + void defaultIncludeChanged(); + void changed(const QQmlV8Handle &removed, const QQmlV8Handle &inserted); +private: + Q_DECLARE_PRIVATE(QQmlDelegateModelGroup) +}; + +class QQmlDelegateModelItem; +class QQmlDelegateModelAttachedMetaObject; +class QQmlDelegateModelAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlDelegateModel *model READ model CONSTANT) + Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged) + Q_PROPERTY(bool isUnresolved READ isUnresolved NOTIFY unresolvedChanged) +public: + QQmlDelegateModelAttached(QObject *parent); + QQmlDelegateModelAttached(QQmlDelegateModelItem *cacheItem, QObject *parent); + ~QQmlDelegateModelAttached() {} + + void setCacheItem(QQmlDelegateModelItem *item); + + QQmlDelegateModel *model() const; + + QStringList groups() const; + void setGroups(const QStringList &groups); + + bool isUnresolved() const; + + void emitChanges(); + + void emitUnresolvedChanged() { emit unresolvedChanged(); } + +Q_SIGNALS: + void groupsChanged(); + void unresolvedChanged(); + +public: + QQmlDelegateModelItem *m_cacheItem; + int m_previousGroups; + int m_currentIndex[QQmlListCompositor::MaximumGroupCount]; + int m_previousIndex[QQmlListCompositor::MaximumGroupCount]; + + friend class QQmlDelegateModelAttachedMetaObject; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlDelegateModel) +QML_DECLARE_TYPEINFO(QQmlDelegateModel, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QQmlDelegateModelGroup) + +#endif // QQMLDATAMODEL_P_H diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h new file mode 100644 index 0000000000..68242f433d --- /dev/null +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -0,0 +1,411 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDATAMODEL_P_P_H +#define QQMLDATAMODEL_P_P_H + +#include "qqmldelegatemodel_p.h" + + +#include +#include + +#include +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +typedef QQmlListCompositor Compositor; + +class QQmlDelegateModelAttachedMetaObject; + +class QQmlDelegateModelItemMetaType : public QQmlRefCount +{ +public: + QQmlDelegateModelItemMetaType(QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames); + ~QQmlDelegateModelItemMetaType(); + + void initializeMetaObject(); + void initializeConstructor(); + + int parseGroups(const QStringList &groupNames) const; + int parseGroups(const v8::Local &groupNames) const; + + static void release_index(v8::Persistent object, void *parameter); + static void release_model(v8::Persistent object, void *parameter); + + static v8::Handle get_model(v8::Local, const v8::AccessorInfo &info); + static v8::Handle get_groups(v8::Local, const v8::AccessorInfo &info); + static void set_groups( + v8::Local, v8::Local value, const v8::AccessorInfo &info); + static v8::Handle get_member(v8::Local, const v8::AccessorInfo &info); + static void set_member( + v8::Local, v8::Local value, const v8::AccessorInfo &info); + static v8::Handle get_index(v8::Local, const v8::AccessorInfo &info); + + QQmlGuard model; + const int groupCount; + QV8Engine * const v8Engine; + QQmlDelegateModelAttachedMetaObject *metaObject; + const QStringList groupNames; + v8::Persistent constructor; +}; + +class QQmlAdaptorModel; +class QQDMIncubationTask; + +class QQmlDelegateModelItem : public QObject, public QV8ObjectResource +{ + Q_OBJECT + Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged) + Q_PROPERTY(QObject *model READ modelObject CONSTANT) + V8_RESOURCE_TYPE(VisualDataItemType) +public: + QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex); + ~QQmlDelegateModelItem(); + + void referenceObject() { ++objectRef; } + bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } + bool isObjectReferenced() const { return objectRef != 0 || (groups & Compositor::PersistedFlag); } + + bool isReferenced() const { + return scriptRef + || incubationTask + || ((groups & Compositor::UnresolvedFlag) && (groups & Compositor::GroupMask)); + } + + void Dispose(); + + QObject *modelObject() { return this; } + + void incubateObject( + QQmlComponent *component, + QQmlEngine *engine, + QQmlContextData *context, + QQmlContextData *forContext); + void destroyObject(); + + static QQmlDelegateModelItem *dataForObject(QObject *object); + + int groupIndex(Compositor::Group group); + + int modelIndex() const { return index; } + void setModelIndex(int idx) { index = idx; emit modelIndexChanged(); } + + virtual v8::Handle get() { return engine->newQObject(this); } + + virtual void setValue(const QString &role, const QVariant &value) { Q_UNUSED(role); Q_UNUSED(value); } + virtual bool resolveIndex(const QQmlAdaptorModel &, int) { return false; } + + QQmlDelegateModelItemMetaType * const metaType; + QQmlContextData *contextData; + QObject *object; + QQmlDelegateModelAttached *attached; + QQDMIncubationTask *incubationTask; + int objectRef; + int scriptRef; + int groups; + int index; + + +Q_SIGNALS: + void modelIndexChanged(); + +protected: + void objectDestroyed(QObject *); +}; + + +class QQmlDelegateModelPrivate; +class QQDMIncubationTask : public QQmlIncubator +{ +public: + QQDMIncubationTask(QQmlDelegateModelPrivate *l, IncubationMode mode) + : QQmlIncubator(mode) + , incubating(0) + , vdm(l) {} + + virtual void statusChanged(Status); + virtual void setInitialState(QObject *); + + QQmlDelegateModelItem *incubating; + QQmlDelegateModelPrivate *vdm; + int index[QQmlListCompositor::MaximumGroupCount]; +}; + + +class QQmlDelegateModelGroupEmitter +{ +public: + virtual ~QQmlDelegateModelGroupEmitter() {} + virtual void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) = 0; + virtual void createdPackage(int, QQuickPackage *) {} + virtual void initPackage(int, QQuickPackage *) {} + virtual void destroyingPackage(QQuickPackage *) {} + + QIntrusiveListNode emitterNode; +}; + +typedef QIntrusiveList QQmlDelegateModelGroupEmitterList; + +class QQmlDelegateModelGroupPrivate : public QObjectPrivate +{ +public: + Q_DECLARE_PUBLIC(QQmlDelegateModelGroup) + + QQmlDelegateModelGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {} + + static QQmlDelegateModelGroupPrivate *get(QQmlDelegateModelGroup *group) { + return static_cast(QObjectPrivate::get(group)); } + + void setModel(QQmlDelegateModel *model, Compositor::Group group); + bool isChangedConnected(); + void emitChanges(QV8Engine *engine); + void emitModelUpdated(bool reset); + + void createdPackage(int index, QQuickPackage *package); + void initPackage(int index, QQuickPackage *package); + void destroyingPackage(QQuickPackage *package); + + bool parseIndex(const v8::Local &value, int *index, Compositor::Group *group) const; + bool parseGroupArgs( + QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const; + + Compositor::Group group; + QQmlGuard model; + QQmlDelegateModelGroupEmitterList emitters; + QQmlChangeSet changeSet; + QString name; + bool defaultInclude; +}; + +class QQmlDelegateModelParts; + +class QQmlDelegateModelPrivate : public QObjectPrivate, public QQmlDelegateModelGroupEmitter +{ + Q_DECLARE_PUBLIC(QQmlDelegateModel) +public: + QQmlDelegateModelPrivate(QQmlContext *); + ~QQmlDelegateModelPrivate(); + + static QQmlDelegateModelPrivate *get(QQmlDelegateModel *m) { + return static_cast(QObjectPrivate::get(m)); + } + + void init(); + void connectModel(QQmlAdaptorModel *model); + + QObject *object(Compositor::Group group, int index, bool asynchronous); + QQmlDelegateModel::ReleaseFlags release(QObject *object); + QString stringValue(Compositor::Group group, int index, const QString &name); + void emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); + void emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); + void emitCreatedItem(QQDMIncubationTask *incubationTask, QObject *item) { + emit q_func()->createdItem(incubationTask->index[m_compositorGroup], item); } + void emitInitItem(QQDMIncubationTask *incubationTask, QObject *item) { + emit q_func()->initItem(incubationTask->index[m_compositorGroup], item); } + void emitDestroyingPackage(QQuickPackage *package); + void emitDestroyingItem(QObject *item) { emit q_func()->destroyingItem(item); } + void removeCacheItem(QQmlDelegateModelItem *cacheItem); + + void updateFilterGroup(); + + void addGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); + void removeGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); + void setGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); + + void itemsInserted( + const QVector &inserts, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, + QHash > *movedItems = 0); + void itemsInserted(const QVector &inserts); + void itemsRemoved( + const QVector &removes, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, + QHash > *movedItems = 0); + void itemsRemoved(const QVector &removes); + void itemsMoved( + const QVector &removes, const QVector &inserts); + void itemsChanged(const QVector &changes); + template static v8::Local buildChangeList(const QVector &changes); + void emitChanges(); + void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset); + + bool insert(Compositor::insert_iterator &before, const v8::Local &object, int groups); + + static void group_append(QQmlListProperty *property, QQmlDelegateModelGroup *group); + static int group_count(QQmlListProperty *property); + static QQmlDelegateModelGroup *group_at(QQmlListProperty *property, int index); + + void releaseIncubator(QQDMIncubationTask *incubationTask); + void incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status); + void setInitialState(QQDMIncubationTask *incubationTask, QObject *o); + + QQmlAdaptorModel m_adaptorModel; + QQmlListCompositor m_compositor; + QQmlComponent *m_delegate; + QQmlDelegateModelItemMetaType *m_cacheMetaType; + QQmlContext *m_context; + QQmlDelegateModelParts *m_parts; + QQmlDelegateModelGroupEmitterList m_pendingParts; + + QList m_cache; + QList m_finishedIncubating; + QList m_watchedRoles; + + QString m_filterGroup; + + int m_count; + int m_groupCount; + + QQmlListCompositor::Group m_compositorGroup; + bool m_complete : 1; + bool m_delegateValidated : 1; + bool m_reset : 1; + bool m_transaction : 1; + bool m_incubatorCleanupScheduled : 1; + + union { + struct { + QQmlDelegateModelGroup *m_cacheItems; + QQmlDelegateModelGroup *m_items; + QQmlDelegateModelGroup *m_persistedItems; + }; + QQmlDelegateModelGroup *m_groups[Compositor::MaximumGroupCount]; + }; +}; + +class QQmlPartsModel : public QQmlInstanceModel, public QQmlDelegateModelGroupEmitter +{ + Q_OBJECT + Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) +public: + QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent = 0); + ~QQmlPartsModel(); + + QString filterGroup() const; + void setFilterGroup(const QString &group); + void resetFilterGroup(); + void updateFilterGroup(); + void updateFilterGroup(Compositor::Group group, const QQmlChangeSet &changeSet); + + int count() const; + bool isValid() const; + QObject *object(int index, bool asynchronous=false); + ReleaseFlags release(QObject *item); + QString stringValue(int index, const QString &role); + QList watchedRoles() const { return m_watchedRoles; } + void setWatchedRoles(QList roles); + + int indexOf(QObject *item, QObject *objectContext) const; + + void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset); + + void createdPackage(int index, QQuickPackage *package); + void initPackage(int index, QQuickPackage *package); + void destroyingPackage(QQuickPackage *package); + +Q_SIGNALS: + void filterGroupChanged(); + +private: + QQmlDelegateModel *m_model; + QHash m_packaged; + QString m_part; + QString m_filterGroup; + QList m_watchedRoles; + Compositor::Group m_compositorGroup; + bool m_inheritGroup; +}; + +class QMetaPropertyBuilder; + +class QQmlDelegateModelPartsMetaObject : public QQmlOpenMetaObject +{ +public: + QQmlDelegateModelPartsMetaObject(QObject *parent) + : QQmlOpenMetaObject(parent) {} + + virtual void propertyCreated(int, QMetaPropertyBuilder &); + virtual QVariant initialValue(int); +}; + +class QQmlDelegateModelParts : public QObject +{ +Q_OBJECT +public: + QQmlDelegateModelParts(QQmlDelegateModel *parent); + + QQmlDelegateModel *model; + QList models; +}; + +class QQmlDelegateModelAttachedMetaObject : public QAbstractDynamicMetaObject, public QQmlRefCount +{ +public: + QQmlDelegateModelAttachedMetaObject( + QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject); + ~QQmlDelegateModelAttachedMetaObject(); + + void objectDestroyed(QObject *); + int metaCall(QObject *, QMetaObject::Call, int _id, void **); + +private: + QQmlDelegateModelItemMetaType * const metaType; + QMetaObject * const metaObject; + const int memberPropertyOffset; + const int indexPropertyOffset; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp new file mode 100644 index 0000000000..9609e91b1c --- /dev/null +++ b/src/qml/types/qqmllistmodel.cpp @@ -0,0 +1,2588 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmllistmodel_p_p.h" +#include "qqmllistmodelworkeragent_p.h" +#include +#include +#include + + +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models. +enum { MIN_LISTMODEL_UID = 1024 }; + +static QAtomicInt uidCounter(MIN_LISTMODEL_UID); + +template +static bool isMemoryUsed(const char *mem) +{ + for (size_t i=0 ; i < sizeof(T) ; ++i) { + if (mem[i] != 0) + return true; + } + + return false; +} + +static QString roleTypeName(ListLayout::Role::DataType t) +{ + QString result; + const char *roleTypeNames[] = { "String", "Number", "Bool", "List", "QObject", "VariantMap", "DateTime" }; + + if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType) + result = QString::fromLatin1(roleTypeNames[t]); + + return result; +} + +const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type) +{ + QStringHash::Node *node = roleHash.findNode(key); + if (node) { + const Role &r = *node->value; + if (type != r.type) + qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); + return r; + } + + return createRole(key, type); +} + +const ListLayout::Role &ListLayout::getRoleOrCreate(v8::Handle key, Role::DataType type) +{ + QHashedV8String hashedKey(key); + QStringHash::Node *node = roleHash.findNode(hashedKey); + if (node) { + const Role &r = *node->value; + if (type != r.type) + qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); + return r; + } + + QString qkey; + qkey.resize(key->Length()); + key->Write(reinterpret_cast(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(ListModel *), sizeof(QQmlGuard), sizeof(QVariantMap), sizeof(QDateTime) }; + const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime) }; + + 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) +{ + 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; +} + +ListLayout::~ListLayout() +{ + for (int i=0 ; i < roles.count() ; ++i) { + delete roles[i]; + } +} + +void ListLayout::sync(ListLayout *src, ListLayout *target) +{ + 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); + } + + target->currentBlockOffset = src->currentBlockOffset; + target->currentBlock = src->currentBlock; +} + +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; +} + +ListLayout::Role::~Role() +{ + delete subLayout; +} + +const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QVariant &data) +{ + 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; + case QVariant::Map: type = Role::VariantMap; break; + default: type = Role::Invalid; break; + } + + if (type == Role::Invalid) { + qmlInfo(0) << "Can't create role for unsupported data type"; + return 0; + } + + return &getRoleOrCreate(key, type); +} + +const ListLayout::Role *ListLayout::getExistingRole(const QString &key) +{ + Role *r = 0; + QStringHash::Node *node = roleHash.findNode(key); + if (node) + r = node->value; + return r; +} + +const ListLayout::Role *ListLayout::getExistingRole(v8::Handle key) +{ + Role *r = 0; + QHashedV8String hashedKey(key); + QStringHash::Node *node = roleHash.findNode(hashedKey); + if (node) + r = node->value; + return r; +} + +ModelObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex) +{ + ListElement *e = elements[elementIndex]; + if (e->m_objectCache == 0) { + e->m_objectCache = new ModelObject(model, elementIndex); + } + return e->m_objectCache; +} + +void ListModel::sync(ListModel *src, ListModel *target, QHash *targetModelHash) +{ + // Sanity check + target->m_uid = src->m_uid; + if (targetModelHash) + targetModelHash->insert(target->m_uid, target); + + // Build hash of elements <-> uid for each of the lists + QHash 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::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::iterator it = elementHash.begin(); + QHash::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; + } + + // 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); + } + + target->updateCacheIndices(); + + // 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(); + } +} + +ListModel::ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid) : m_layout(layout), m_modelCache(modelCache) +{ + if (uid == -1) + uid = uidCounter.fetchAndAddOrdered(1); + 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; +} + +void ListModel::insertElement(int index) +{ + newElement(index); + updateCacheIndices(); +} + +void ListModel::move(int from, int to, int 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; + } + + QPODVector 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]; + + updateCacheIndices(); +} + +void ListModel::newElement(int index) +{ + ListElement *e = new ListElement; + elements.insert(index, e); +} + +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 QQmlListModel *owner, QV8Engine *eng) +{ + ListElement *e = elements[elementIndex]; + const ListLayout::Role &r = m_layout->getExistingRole(roleIndex); + return e->getProperty(r, owner, eng); +} + +ListModel *ListModel::getListProperty(int elementIndex, const ListLayout::Role &role) +{ + ListElement *e = elements[elementIndex]; + return e->getListProperty(role); +} + +void ListModel::set(int elementIndex, v8::Handle object, QVector *roles, QV8Engine *eng) +{ + ListElement *e = elements[elementIndex]; + + v8::Local propertyNames = object->GetPropertyNames(); + int propertyCount = propertyNames->Length(); + + for (int i=0 ; i < propertyCount ; ++i) { + v8::Local propertyName = propertyNames->Get(i)->ToString(); + v8::Local 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 jsString = propertyValue->ToString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast(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 subArray = v8::Handle::Cast(propertyValue); + int arrayLength = subArray->Length(); + for (int j=0 ; j < arrayLength ; ++j) { + v8::Handle subObject = subArray->Get(j)->ToObject(); + subModel->append(subObject, eng); + } + + roleIndex = e->setListProperty(r, subModel); + } else if (propertyValue->IsBoolean()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); + roleIndex = e->setBoolProperty(r, propertyValue->BooleanValue()); + } else if (propertyValue->IsDate()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); + QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(propertyValue)->NumberValue()); + roleIndex = e->setDateTimeProperty(r, dt); + } 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) + roleIndex = e->setQObjectProperty(role, o); + } else { + const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); + if (role.type == ListLayout::Role::VariantMap) + roleIndex = e->setVariantMapProperty(role, propertyValue->ToObject(), eng); + } + } 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 object, QV8Engine *eng) +{ + ListElement *e = elements[elementIndex]; + + v8::Local propertyNames = object->GetPropertyNames(); + int propertyCount = propertyNames->Length(); + + for (int i=0 ; i < propertyCount ; ++i) { + v8::Local propertyName = propertyNames->Get(i)->ToString(); + v8::Local 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 jsString = propertyValue->ToString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast(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 subArray = v8::Handle::Cast(propertyValue); + int arrayLength = subArray->Length(); + for (int j=0 ; j < arrayLength ; ++j) { + v8::Handle subObject = subArray->Get(j)->ToObject(); + subModel->append(subObject, eng); + } + + e->setListPropertyFast(r, subModel); + } + } 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->IsDate()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); + if (r.type == ListLayout::Role::DateTime) { + QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(propertyValue)->NumberValue()); + e->setDateTimePropertyFast(r, dt); + } + } 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 { + const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); + if (role.type == ListLayout::Role::VariantMap) + e->setVariantMapFast(role, propertyValue->ToObject(), eng); + } + } 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, int count) +{ + for (int i=0 ; i < count ; ++i) { + elements[index+i]->destroy(m_layout); + delete elements[index+i]; + } + elements.remove(index, count); + updateCacheIndices(); +} + +void ListModel::insert(int elementIndex, v8::Handle object, QV8Engine *eng) +{ + insertElement(elementIndex); + set(elementIndex, object, eng); +} + +int ListModel::append(v8::Handle object, QV8Engine *eng) +{ + int elementIndex = appendElement(); + set(elementIndex, object, eng); + 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) { + QVector roles; + roles << roleIndex; + e->m_objectCache->updateValues(roles); + } + } + } + + return roleIndex; +} + +int ListModel::setExistingProperty(int elementIndex, const QString &key, v8::Handle data, QV8Engine *eng) +{ + 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, eng); + } + + 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(mem); + return s->data_ptr() ? s : 0; +} + +QObject *ListElement::getQObjectProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + QQmlGuard *o = reinterpret_cast *>(mem); + return o->data(); +} + +QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role) +{ + QVariantMap *map = 0; + + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) + map = reinterpret_cast(mem); + + return map; +} + +QDateTime *ListElement::getDateTimeProperty(const ListLayout::Role &role) +{ + QDateTime *dt = 0; + + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) + dt = reinterpret_cast(mem); + + return dt; +} + +QQmlGuard *ListElement::getGuardProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + + bool existingGuard = false; + for (size_t i=0 ; i < sizeof(QQmlGuard) ; ++i) { + if (mem[i] != 0) { + existingGuard = true; + break; + } + } + + QQmlGuard *o = 0; + + if (existingGuard) + o = reinterpret_cast *>(mem); + + return o; +} + +ListModel *ListElement::getListProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + ListModel **value = reinterpret_cast(mem); + return *value; +} + +QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV8Engine *eng) +{ + char *mem = getPropertyMemory(role); + + QVariant data; + + switch (role.type) { + case ListLayout::Role::Number: + { + double *value = reinterpret_cast(mem); + data = *value; + } + break; + case ListLayout::Role::String: + { + QString *value = reinterpret_cast(mem); + if (value->data_ptr() != 0) + data = *value; + } + break; + case ListLayout::Role::Bool: + { + bool *value = reinterpret_cast(mem); + data = *value; + } + break; + case ListLayout::Role::List: + { + ListModel **value = reinterpret_cast(mem); + ListModel *model = *value; + + if (model) { + if (model->m_modelCache == 0) { + model->m_modelCache = new QQmlListModel(owner, model, eng); + QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner)); + } + + QObject *object = model->m_modelCache; + data = QVariant::fromValue(object); + } + } + break; + case ListLayout::Role::QObject: + { + QQmlGuard *guard = reinterpret_cast *>(mem); + QObject *object = guard->data(); + if (object) + data = QVariant::fromValue(object); + } + break; + case ListLayout::Role::VariantMap: + { + if (isMemoryUsed(mem)) { + QVariantMap *map = reinterpret_cast(mem); + data = *map; + } + } + break; + case ListLayout::Role::DateTime: + { + if (isMemoryUsed(mem)) { + QDateTime *dt = reinterpret_cast(mem); + data = *dt; + } + } + 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(mem); + bool changed; + if (c->data_ptr() == 0) { + new (mem) QString(s); + changed = true; + } else { + changed = c->compare(s) != 0; + *c = s; + } + if (changed) + roleIndex = role.index; + } + + 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; + } + + 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; + } + + 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; + } + + return roleIndex; +} + +int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::QObject) { + char *mem = getPropertyMemory(role); + QQmlGuard *g = reinterpret_cast *>(mem); + bool existingGuard = false; + for (size_t i=0 ; i < sizeof(QQmlGuard) ; ++i) { + if (mem[i] != 0) { + existingGuard = true; + break; + } + } + bool changed; + if (existingGuard) { + changed = g->data() != o; + g->~QQmlGuard(); + } else { + changed = true; + } + new (mem) QQmlGuard(o); + if (changed) + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setVariantMapProperty(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::VariantMap) { + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) { + QVariantMap *map = reinterpret_cast(mem); + map->~QMap(); + } + new (mem) QVariantMap(eng->variantMapFromJS(o)); + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::VariantMap) { + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) { + QVariantMap *map = reinterpret_cast(mem); + map->~QMap(); + } + if (m) + new (mem) QVariantMap(*m); + else + new (mem) QVariantMap; + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::DateTime) { + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) { + QDateTime *dt = reinterpret_cast(mem); + dt->~QDateTime(); + } + new (mem) QDateTime(dt); + roleIndex = role.index; + } + + 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) QQmlGuard(o); +} + +void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m) +{ + char *mem = getPropertyMemory(role); + ListModel **value = new (mem) ListModel *; + *value = m; +} + +void ListElement::setVariantMapFast(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng) +{ + char *mem = getPropertyMemory(role); + QVariantMap *map = new (mem) QVariantMap; + *map = eng->variantMapFromJS(o); +} + +void ListElement::setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt) +{ + char *mem = getPropertyMemory(role); + new (mem) QDateTime(dt); +} + +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; + case ListLayout::Role::DateTime: + setDateTimeProperty(role, QDateTime()); + break; + case ListLayout::Role::VariantMap: + setVariantMapProperty(role, 0); + break; + default: + break; + } +} + +ListElement::ListElement() +{ + m_objectCache = 0; + uid = uidCounter.fetchAndAddOrdered(1); + next = 0; + memset(data, 0, sizeof(data)); +} + +ListElement::ListElement(int existingUid) +{ + m_objectCache = 0; + uid = existingUid; + next = 0; + memset(data, 0, sizeof(data)); +} + +ListElement::~ListElement() +{ + delete next; +} + +void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash *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: + case ListLayout::Role::DateTime: + { + QVariant v = src->getProperty(srcRole, 0, 0); + target->setVariantProperty(targetRole, v); + } + case ListLayout::Role::VariantMap: + { + QVariantMap *map = src->getVariantMapProperty(srcRole); + target->setVariantMapProperty(targetRole, map); + } + break; + 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: + { + QQmlGuard *guard = getGuardProperty(r); + if (guard) + guard->~QQmlGuard(); + } + break; + case ListLayout::Role::VariantMap: + { + QVariantMap *map = getVariantMapProperty(r); + if (map) + map->~QMap(); + } + break; + case ListLayout::Role::DateTime: + { + QDateTime *dt = getDateTimeProperty(r); + if (dt) + dt->~QDateTime(); + } + 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()); + break; + case ListLayout::Role::VariantMap: { + QVariantMap map = d.toMap(); + roleIndex = setVariantMapProperty(role, &map); + } + break; + case ListLayout::Role::DateTime: + roleIndex = setDateTimeProperty(role, d.toDateTime()); + break; + default: + break; + } + + return roleIndex; +} + +int ListElement::setJsProperty(const ListLayout::Role &role, v8::Handle d, QV8Engine *eng) +{ + // Check if this key exists yet + int roleIndex = -1; + + // Add the value now + if (d->IsString()) { + v8::Handle jsString = d->ToString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast(qstr.data())); + roleIndex = setStringProperty(role, qstr); + } else if (d->IsNumber()) { + roleIndex = setDoubleProperty(role, d->NumberValue()); + } else if (d->IsArray()) { + if (role.type == ListLayout::Role::List) { + ListModel *subModel = new ListModel(role.subLayout, 0, -1); + v8::Handle subArray = v8::Handle::Cast(d); + int arrayLength = subArray->Length(); + for (int j=0 ; j < arrayLength ; ++j) { + v8::Handle subObject = subArray->Get(j)->ToObject(); + subModel->append(subObject, eng); + } + roleIndex = setListProperty(role, subModel); + } else { + qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List)); + } + } else if (d->IsBoolean()) { + roleIndex = setBoolProperty(role, d->BooleanValue()); + } else if (d->IsDate()) { + QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(d)->NumberValue()); + roleIndex = setDateTimeProperty(role, dt); + } 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 (role.type == ListLayout::Role::VariantMap) { + roleIndex = setVariantMapProperty(role, d->ToObject(), eng); + } + } else if (d.IsEmpty() || d->IsUndefined() || d->IsNull()) { + clearProperty(role); + } + + return roleIndex; +} + +ModelObject::ModelObject(QQmlListModel *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 QVector &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) +: QQmlOpenMetaObject(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 v = eng->fromVariant(value); + + int roleIndex = m_obj->m_model->m_listModel->setExistingProperty(m_obj->m_elementIndex, propName, v, eng); + if (roleIndex != -1) { + QVector roles; + roles << roleIndex; + m_obj->m_model->emitItemsChanged(m_obj->m_elementIndex, 1, roles); + } +} + +DynamicRoleModelNode::DynamicRoleModelNode(QQmlListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this)) +{ + setNodeUpdatesEnabled(true); +} + +DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QQmlListModel *owner) +{ + DynamicRoleModelNode *object = new DynamicRoleModelNode(owner, uidCounter.fetchAndAddOrdered(1)); + QVector roles; + object->updateValues(obj, roles); + return object; +} + +void DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash *targetModelHash) +{ + for (int i=0 ; i < src->m_meta->count() ; ++i) { + const QByteArray &name = src->m_meta->name(i); + QVariant value = src->m_meta->value(i); + + QQmlListModel *srcModel = qobject_cast(value.value()); + QQmlListModel *targetModel = qobject_cast(target->m_meta->value(i).value()); + + if (srcModel) { + if (targetModel == 0) + targetModel = QQmlListModel::createWithOwner(target->m_owner); + + QQmlListModel::sync(srcModel, targetModel, targetModelHash); + + QObject *targetModelObject = targetModel; + value = QVariant::fromValue(targetModelObject); + } else if (targetModel) { + delete targetModel; + } + + target->setValue(name, value); + } +} + +void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector &roles) +{ + const QList &keys = object.keys(); + + QList::const_iterator it = keys.begin(); + QList::const_iterator end = keys.end(); + + while (it != end) { + const QString &key = *it; + + int roleIndex = m_owner->m_roles.indexOf(key); + if (roleIndex == -1) { + roleIndex = m_owner->m_roles.count(); + m_owner->m_roles.append(key); + } + + QVariant value = object[key]; + + if (value.type() == QVariant::List) { + QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner); + + QVariantList subArray = value.toList(); + QVariantList::const_iterator subIt = subArray.begin(); + QVariantList::const_iterator subEnd = subArray.end(); + while (subIt != subEnd) { + const QVariantMap &subObject = subIt->toMap(); + subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); + ++subIt; + } + + QObject *subModelObject = subModel; + value = QVariant::fromValue(subModelObject); + } + + const QByteArray &keyUtf8 = key.toUtf8(); + + QQmlListModel *existingModel = qobject_cast(m_meta->value(keyUtf8).value()); + if (existingModel) + delete existingModel; + + if (m_meta->setValue(keyUtf8, value)) + roles << roleIndex; + + ++it; + } +} + +DynamicRoleModelNodeMetaObject::DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object) + : QQmlOpenMetaObject(object), m_enabled(false), m_owner(object) +{ +} + +DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject() +{ + for (int i=0 ; i < count() ; ++i) { + QQmlListModel *subModel = qobject_cast(value(i).value()); + if (subModel) + delete subModel; + } +} + +void DynamicRoleModelNodeMetaObject::propertyWrite(int index) +{ + if (!m_enabled) + return; + + QVariant v = value(index); + QQmlListModel *model = qobject_cast(v.value()); + if (model) + delete model; +} + +void DynamicRoleModelNodeMetaObject::propertyWritten(int index) +{ + if (!m_enabled) + return; + + QQmlListModel *parentModel = m_owner->m_owner; + + QVariant v = value(index); + if (v.type() == QVariant::List) { + QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel); + + QVariantList subArray = v.toList(); + QVariantList::const_iterator subIt = subArray.begin(); + QVariantList::const_iterator subEnd = subArray.end(); + while (subIt != subEnd) { + const QVariantMap &subObject = subIt->toMap(); + subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); + ++subIt; + } + + QObject *subModelObject = subModel; + v = QVariant::fromValue(subModelObject); + + setValue(index, v); + } + + int elementIndex = parentModel->m_modelObjects.indexOf(m_owner); + int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData())); + + if (elementIndex != -1 && roleIndex != -1) { + + QVector roles; + roles << roleIndex; + + parentModel->emitItemsChanged(elementIndex, 1, roles); + } +} + +QQmlListModelParser::ListInstruction *QQmlListModelParser::ListModelData::instructions() const +{ + return (QQmlListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData)); +} + +/*! + \qmltype ListModel + \instantiates QQmlListModel + \inqmlmodule QtQml.Models 2 + \brief Defines a free-form list data source + + The ListModel is a simple container of ListElement definitions, each containing data roles. + The contents can be defined dynamically, or explicitly in QML. + + This type is also available in the QtQuick 2 import. For full documentation, see \l QtQuick2::ListModel +*/ +/*! + \qmltype ListModel + \instantiates QQmlListModel + \inqmlmodule QtQuick 2 + \brief Defines a free-form list data source + \ingroup qtquick-models + + The ListModel is a simple container of ListElement definitions, each containing data roles. + The contents can be defined dynamically, or explicitly in QML. + + The number of elements in the model can be obtained from its \l count property. + A number of familiar methods are also provided to manipulate the contents of the + model, including append(), insert(), move(), remove() and set(). These methods + accept dictionaries as their arguments; these are translated to ListElement objects + by the model. + + Elements can be manipulated via the model using the setProperty() method, which + allows the roles of the specified element to be set and changed. + + \section1 Example Usage + + The following example shows a ListModel containing three elements, with the roles + "name" and "cost". + + \div {class="float-right"} + \inlineimage listmodel.png + \enddiv + + \snippet qml/listmodel/listmodel.qml 0 + + Roles (properties) in each element must begin with a lower-case letter and + should be common to all elements in a model. The ListElement documentation + provides more guidelines for how elements should be defined. + + Since the example model contains an \c id property, it can be referenced + by views, such as the ListView in this example: + + \snippet qml/listmodel/listmodel-simple.qml 0 + \dots 8 + \snippet qml/listmodel/listmodel-simple.qml 1 + + It is possible for roles to contain list data. In the following example we + create a list of fruit attributes: + + \snippet qml/listmodel/listmodel-nested.qml model + + The delegate displays all the fruit attributes: + + \div {class="float-right"} + \inlineimage listmodel-nested.png + \enddiv + + \snippet qml/listmodel/listmodel-nested.qml delegate + + \section1 Modifying List Models + + The content of a ListModel may be created and modified using the clear(), + append(), set(), insert() and setProperty() methods. For example: + + \snippet qml/listmodel/listmodel-modify.qml delegate + + Note that when creating content dynamically the set of available properties + cannot be changed once set. Whatever properties are first added to the model + are the only permitted properties in the model. + + \section1 Using Threaded List Models with WorkerScript + + ListModel can be used together with WorkerScript access a list model + from multiple threads. This is useful if list modifications are + synchronous and take some time: the list operations can be moved to a + different thread to avoid blocking of the main GUI thread. + + Here is an example that uses WorkerScript to periodically append the + current time to a list model: + + \snippet quick/threading/threadedlistmodel/timedisplay.qml 0 + + The included file, \tt dataloader.js, looks like this: + + \snippet quick/threading/threadedlistmodel/dataloader.js 0 + + The timer in the main example sends messages to the worker script by calling + \l WorkerScript::sendMessage(). When this message is received, + \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js, + which appends the current time to the list model. + + Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()} + 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. + + \sa {qml-data-models}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtQml +*/ + +QQmlListModel::QQmlListModel(QObject *parent) +: QAbstractListModel(parent) +{ + m_mainThread = true; + m_primary = true; + m_agent = 0; + m_uid = uidCounter.fetchAndAddOrdered(1); + m_dynamicRoles = false; + + m_layout = new ListLayout; + m_listModel = new ListModel(m_layout, this, -1); + + m_engine = 0; +} + +QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent) +: QAbstractListModel(parent) +{ + m_mainThread = owner->m_mainThread; + m_primary = false; + m_agent = owner->m_agent; + + Q_ASSERT(owner->m_dynamicRoles == false); + m_dynamicRoles = false; + m_layout = 0; + m_listModel = data; + + m_engine = eng; +} + +QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent) +: QAbstractListModel(agent) +{ + m_mainThread = false; + m_primary = true; + m_agent = agent; + m_dynamicRoles = orig->m_dynamicRoles; + + m_layout = new ListLayout(orig->m_layout); + m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid()); + + if (m_dynamicRoles) + sync(orig, this, 0); + else + ListModel::sync(orig->m_listModel, m_listModel, 0); + + m_engine = 0; +} + +QQmlListModel::~QQmlListModel() +{ + for (int i=0 ; i < m_modelObjects.count() ; ++i) + delete m_modelObjects[i]; + + if (m_primary) { + m_listModel->destroy(); + delete m_listModel; + + if (m_mainThread && m_agent) { + m_agent->modelDestroyed(); + m_agent->release(); + } + } + + m_listModel = 0; + + delete m_layout; + m_layout = 0; +} + +QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner) +{ + QQmlListModel *model = new QQmlListModel; + + model->m_mainThread = newOwner->m_mainThread; + model->m_engine = newOwner->m_engine; + model->m_agent = newOwner->m_agent; + model->m_dynamicRoles = newOwner->m_dynamicRoles; + + if (model->m_mainThread && model->m_agent) + model->m_agent->addref(); + + QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner)); + + return model; +} + +QV8Engine *QQmlListModel::engine() const +{ + if (m_engine == 0) { + m_engine = QQmlEnginePrivate::getV8Engine(qmlEngine(this)); + } + + return m_engine; +} + +void QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target, QHash *targetModelHash) +{ + Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles); + + target->m_uid = src->m_uid; + if (targetModelHash) + targetModelHash->insert(target->m_uid, target); + target->m_roles = src->m_roles; + + // Build hash of elements <-> uid for each of the lists + QHash elementHash; + for (int i=0 ; i < target->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *e = target->m_modelObjects.at(i); + int uid = e->getUid(); + ElementSync sync; + sync.target = e; + elementHash.insert(uid, sync); + } + for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *e = src->m_modelObjects.at(i); + int uid = e->getUid(); + + QHash::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::iterator it = elementHash.begin(); + QHash::iterator end = elementHash.end(); + while (it != end) { + const ElementSync &s = it.value(); + if (s.src == 0) { + int targetIndex = target->m_modelObjects.indexOf(s.target); + target->m_modelObjects.remove(targetIndex, 1); + delete s.target; + } + ++it; + } + + // Clear the target list, and append in correct order from the source + target->m_modelObjects.clear(); + for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *srcElement = src->m_modelObjects.at(i); + it = elementHash.find(srcElement->getUid()); + const ElementSync &s = it.value(); + DynamicRoleModelNode *targetElement = s.target; + if (targetElement == 0) { + targetElement = new DynamicRoleModelNode(target, srcElement->getUid()); + } + DynamicRoleModelNode::sync(srcElement, targetElement, targetModelHash); + target->m_modelObjects.append(targetElement); + } +} + +void QQmlListModel::emitItemsChanged(int index, int count, const QVector &roles) +{ + if (count <= 0) + return; + + if (m_mainThread) { + emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);; + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + m_agent->data.changedChange(uid, index, count, roles); + } +} + +void QQmlListModel::emitItemsRemoved(int index, int count) +{ + if (count <= 0) + return; + + if (m_mainThread) { + beginRemoveRows(QModelIndex(), index, index + count - 1); + endRemoveRows(); + emit countChanged(); + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + if (index == 0 && count == this->count()) + m_agent->data.clearChange(uid); + m_agent->data.removeChange(uid, index, count); + } +} + +void QQmlListModel::emitItemsInserted(int index, int count) +{ + if (count <= 0) + return; + + if (m_mainThread) { + beginInsertRows(QModelIndex(), index, index + count - 1); + endInsertRows(); + emit countChanged(); + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + m_agent->data.insertChange(uid, index, count); + } +} + +void QQmlListModel::emitItemsMoved(int from, int to, int n) +{ + if (n <= 0) + return; + + if (m_mainThread) { + beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to); + endMoveRows(); + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + m_agent->data.moveChange(uid, from, n, to); + } +} + +QQmlListModelWorkerAgent *QQmlListModel::agent() +{ + if (m_agent) + return m_agent; + + m_agent = new QQmlListModelWorkerAgent(this); + return m_agent; +} + +QModelIndex QQmlListModel::index(int row, int column, const QModelIndex &parent) const +{ + return row >= 0 && row < count() && column == 0 && !parent.isValid() + ? createIndex(row, column) + : QModelIndex(); +} + +int QQmlListModel::rowCount(const QModelIndex &parent) const +{ + return !parent.isValid() ? count() : 0; +} + +QVariant QQmlListModel::data(const QModelIndex &index, int role) const +{ + return data(index.row(), role); +} + +QVariant QQmlListModel::data(int index, int role) const +{ + QVariant v; + + if (index >= count() || index < 0) + return v; + + if (m_dynamicRoles) + v = m_modelObjects[index]->getValue(m_roles[role]); + else + v = m_listModel->getProperty(index, role, this, engine()); + + return v; +} + +QHash QQmlListModel::roleNames() const +{ + QHash roleNames; + + if (m_dynamicRoles) { + for (int i = 0 ; i < m_roles.count() ; ++i) + roleNames.insert(i, m_roles.at(i).toUtf8()); + } else { + for (int i = 0 ; i < m_listModel->roleCount() ; ++i) { + const ListLayout::Role &r = m_listModel->getExistingRole(i); + roleNames.insert(i, r.name.toUtf8()); + } + } + + return roleNames; +} + +/*! + \qmlproperty bool QtQml2::ListModel::dynamicRoles + + By default, the type of a role is fixed the first time + the role is used. For example, if you create a role called + "data" and assign a number to it, you can no longer assign + a string to the "data" role. However, when the dynamicRoles + property is enabled, the type of a given role is not fixed + and can be different between elements. + + The dynamicRoles property must be set before any data is + added to the ListModel, and must be set from the main + thread. + + A ListModel that has data statically defined (via the + ListElement QML syntax) cannot have the dynamicRoles + property enabled. + + There is a significant performance cost to using a + ListModel with dynamic roles enabled. The cost varies + from platform to platform but is typically somewhere + between 4-6x slower than using static role types. + + Due to the performance cost of using dynamic roles, + they are disabled by default. +*/ +void QQmlListModel::setDynamicRoles(bool enableDynamicRoles) +{ + if (m_mainThread && m_agent == 0) { + if (enableDynamicRoles) { + if (m_layout->roleCount()) + qmlInfo(this) << tr("unable to enable dynamic roles as this model is not empty!"); + else + m_dynamicRoles = true; + } else { + if (m_roles.count()) { + qmlInfo(this) << tr("unable to enable static roles as this model is not empty!"); + } else { + m_dynamicRoles = false; + } + } + } else { + qmlInfo(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created"); + } +} + +/*! + \qmlproperty int QtQml2::ListModel::count + The number of data entries in the model. +*/ +int QQmlListModel::count() const +{ + int count; + + if (m_dynamicRoles) + count = m_modelObjects.count(); + else { + count = m_listModel->elementCount(); + } + + return count; +} + +/*! + \qmlmethod QtQml2::ListModel::clear() + + Deletes all content from the model. + + \sa append(), remove() +*/ +void QQmlListModel::clear() +{ + int cleared = count(); + + if (m_dynamicRoles) { + for (int i=0 ; i < m_modelObjects.count() ; ++i) + delete m_modelObjects[i]; + m_modelObjects.clear(); + } else { + m_listModel->clear(); + } + + emitItemsRemoved(0, cleared); +} + +/*! + \qmlmethod QtQml2::ListModel::remove(int index, int count = 1) + + Deletes the content at \a index from the model. + + \sa clear() +*/ +void QQmlListModel::remove(QQmlV8Function *args) +{ + int argLength = args->Length(); + + if (argLength == 1 || argLength == 2) { + int index = (*args)[0]->Int32Value(); + int removeCount = (argLength == 2 ? ((*args)[1]->Int32Value()) : 1); + + if (index < 0 || index+removeCount > count() || removeCount <= 0) { + qmlInfo(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count()); + return; + } + + if (m_dynamicRoles) { + for (int i=0 ; i < removeCount ; ++i) + delete m_modelObjects[index+i]; + m_modelObjects.remove(index, removeCount); + } else { + m_listModel->remove(index, removeCount); + } + + emitItemsRemoved(index, removeCount); + } else { + qmlInfo(this) << tr("remove: incorrect number of arguments"); + } +} + +/*! + \qmlmethod QtQml2::ListModel::insert(int index, jsobject 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 + + The \a index must be to an existing item in the list, or one past + the end of the list (equivalent to append). + + \sa set(), append() +*/ + +void QQmlListModel::insert(QQmlV8Function *args) +{ + if (args->Length() == 2) { + + v8::Handle arg0 = (*args)[0]; + int index = arg0->Int32Value(); + + if (index < 0 || index > count()) { + qmlInfo(this) << tr("insert: index %1 out of range").arg(index); + return; + } + + v8::Handle arg1 = (*args)[1]; + + if (arg1->IsArray()) { + v8::Handle objectArray = v8::Handle::Cast(arg1); + int objectArrayLength = objectArray->Length(); + for (int i=0 ; i < objectArrayLength ; ++i) { + v8::Handle argObject = objectArray->Get(i)->ToObject(); + + if (m_dynamicRoles) { + m_modelObjects.insert(index+i, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + m_listModel->insert(index+i, argObject, args->engine()); + } + } + emitItemsInserted(index, objectArrayLength); + } else if (arg1->IsObject()) { + v8::Handle argObject = arg1->ToObject(); + + if (m_dynamicRoles) { + m_modelObjects.insert(index, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + m_listModel->insert(index, argObject, args->engine()); + } + + emitItemsInserted(index, 1); + } else { + qmlInfo(this) << tr("insert: value is not an object"); + } + } else { + qmlInfo(this) << tr("insert: value is not an object"); + } +} + +/*! + \qmlmethod QtQml2::ListModel::move(int from, int to, int 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 QQmlListModel::move(int from, int to, int n) +{ + if (n==0 || from==to) + return; + if (!canMove(from, to, n)) { + qmlInfo(this) << tr("move: out of range"); + return; + } + + if (m_dynamicRoles) { + + int realFrom = from; + int realTo = to; + int realN = n; + + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + realFrom = tto; + realTo = tto+n; + realN = tfrom-tto; + } + + QPODVector store; + for (int i=0 ; i < (realTo-realFrom) ; ++i) + store.append(m_modelObjects[realFrom+realN+i]); + for (int i=0 ; i < realN ; ++i) + store.append(m_modelObjects[realFrom+i]); + for (int i=0 ; i < store.count() ; ++i) + m_modelObjects[realFrom+i] = store[i]; + + } else { + m_listModel->move(from, to, n); + } + + emitItemsMoved(from, to, n); +} + +/*! + \qmlmethod QtQml2::ListModel::append(jsobject 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 QQmlListModel::append(QQmlV8Function *args) +{ + if (args->Length() == 1) { + v8::Handle arg = (*args)[0]; + + if (arg->IsArray()) { + v8::Handle objectArray = v8::Handle::Cast(arg); + int objectArrayLength = objectArray->Length(); + + int index = count(); + for (int i=0 ; i < objectArrayLength ; ++i) { + v8::Handle argObject = objectArray->Get(i)->ToObject(); + + if (m_dynamicRoles) { + m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + m_listModel->append(argObject, args->engine()); + } + } + + emitItemsInserted(index, objectArrayLength); + } else if (arg->IsObject()) { + v8::Handle argObject = arg->ToObject(); + + int index; + + if (m_dynamicRoles) { + index = m_modelObjects.count(); + m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + index = m_listModel->append(argObject, args->engine()); + } + + emitItemsInserted(index, 1); + } else { + qmlInfo(this) << tr("append: value is not an object"); + } + } else { + qmlInfo(this) << tr("append: value is not an object"); + } +} + +/*! + \qmlmethod object QtQml2::ListModel::get(int index) + + Returns the item at \a index in the list model. This allows the item + data to be accessed or modified from JavaScript: + + \code + Component.onCompleted: { + fruitModel.append({"cost": 5.95, "name":"Jackfruit"}); + console.log(fruitModel.get(0).cost); + fruitModel.get(0).cost = 10.95; + } + \endcode + + The \a index must be an element in the list. + + Note that properties of the returned object that are themselves objects + will also be models, and this get() method is used to access elements: + + \code + fruitModel.append(..., "attributes": + [{"name":"spikes","value":"7mm"}, + {"name":"color","value":"green"}]); + fruitModel.get(0).attributes.get(1).value; // == "green" + \endcode + + \warning The returned object is not guaranteed to remain valid. It + should not be used in \l{Property Binding}{property bindings}. + + \sa append() +*/ +QQmlV8Handle QQmlListModel::get(int index) const +{ + v8::Handle result = v8::Undefined(); + + if (index >= 0 && index < count()) { + QV8Engine *v8engine = engine(); + + if (m_dynamicRoles) { + DynamicRoleModelNode *object = m_modelObjects[index]; + result = v8engine->newQObject(object); + } else { + ModelObject *object = m_listModel->getOrCreateModelObject(const_cast(this), index); + result = v8engine->newQObject(object); + } + } + + return QQmlV8Handle::fromHandle(result); +} + +/*! + \qmlmethod QtQml2::ListModel::set(int index, jsobject dict) + + Changes the item at \a index in the list model with the + values in \a dict. Properties not appearing in \a dict + are left unchanged. + + \code + fruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) + \endcode + + If \a index is equal to count() then a new item is appended to the + list. Otherwise, \a index must be an element in the list. + + \sa append() +*/ +void QQmlListModel::set(int index, const QQmlV8Handle &handle) +{ + v8::Handle valuemap = handle.toHandle(); + + if (!valuemap->IsObject() || valuemap->IsArray()) { + qmlInfo(this) << tr("set: value is not an object"); + return; + } + if (index > count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + + v8::Handle object = valuemap->ToObject(); + + if (index == count()) { + + if (m_dynamicRoles) { + m_modelObjects.append(DynamicRoleModelNode::create(engine()->variantMapFromJS(object), this)); + } else { + m_listModel->insert(index, object, engine()); + } + + emitItemsInserted(index, 1); + } else { + + QVector roles; + + if (m_dynamicRoles) { + m_modelObjects[index]->updateValues(engine()->variantMapFromJS(object), roles); + } else { + m_listModel->set(index, object, &roles, engine()); + } + + if (roles.count()) + emitItemsChanged(index, 1, roles); + } +} + +/*! + \qmlmethod QtQml2::ListModel::setProperty(int index, string property, variant value) + + Changes the \a property of the item at \a index in the list model to \a value. + + \code + fruitModel.setProperty(3, "cost", 5.95) + \endcode + + The \a index must be an element in the list. + + \sa append() +*/ +void QQmlListModel::setProperty(int index, const QString& property, const QVariant& value) +{ + if (count() == 0 || index >= count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + + if (m_dynamicRoles) { + int roleIndex = m_roles.indexOf(property); + if (roleIndex == -1) { + roleIndex = m_roles.count(); + m_roles.append(property); + } + if (m_modelObjects[index]->setValue(property.toUtf8(), value)) { + QVector roles; + roles << roleIndex; + emitItemsChanged(index, 1, roles); + } + } else { + int roleIndex = m_listModel->setOrCreateProperty(index, property, value); + if (roleIndex != -1) { + + QVector roles; + roles << roleIndex; + + emitItemsChanged(index, 1, roles); + } + } +} + +/*! + \qmlmethod QtQml2::ListModel::sync() + + Writes any unsaved changes to the list model after it has been modified + from a worker script. +*/ +void QQmlListModel::sync() +{ + // This is just a dummy method to make it look like sync() exists in + // ListModel (and not just QQmlListModelWorkerAgent) and to let + // us document sync(). + qmlInfo(this) << "List sync() can only be called from a WorkerScript"; +} + +bool QQmlListModelParser::compileProperty(const QQmlCustomParserProperty &prop, QList &instr, QByteArray &data) +{ + QList values = prop.assignedValues(); + for(int ii = 0; ii < values.count(); ++ii) { + const QVariant &value = values.at(ii); + + if(value.userType() == qMetaTypeId()) { + QQmlCustomParserNode node = + qvariant_cast(value); + + if (node.name() != listElementTypeName) { + const QMetaObject *mo = resolveType(node.name()); + if (mo != &QQmlListElement::staticMetaObject) { + error(node, QQmlListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + listElementTypeName = node.name(); // cache right name for next time + } + + { + ListInstruction li; + li.type = ListInstruction::Push; + li.dataIdx = -1; + instr << li; + } + + QList props = node.properties(); + for(int jj = 0; jj < props.count(); ++jj) { + const QQmlCustomParserProperty &nodeProp = props.at(jj); + if (nodeProp.name().isEmpty()) { + error(nodeProp, QQmlListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + if (nodeProp.name() == QStringLiteral("id")) { + error(nodeProp, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property")); + return false; + } + + ListInstruction li; + int ref = data.count(); + data.append(nodeProp.name().toUtf8()); + data.append('\0'); + li.type = ListInstruction::Set; + li.dataIdx = ref; + instr << li; + + if(!compileProperty(nodeProp, instr, data)) + return false; + + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + { + ListInstruction li; + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + } else { + + QQmlScript::Variant variant = + qvariant_cast(value); + + int ref = data.count(); + + QByteArray d; + d += char(variant.type()); // type tag + if (variant.isString()) { + d += variant.asString().toUtf8(); + } else if (variant.isNumber()) { + d += QByteArray::number(variant.asNumber(),'g',20); + } else if (variant.isBoolean()) { + d += char(variant.asBoolean()); + } else if (variant.isScript()) { + if (definesEmptyList(variant.asScript())) { + d[0] = char(QQmlScript::Variant::Invalid); // marks empty list + } else { + QByteArray script = variant.asScript().toUtf8(); + bool ok; + int v = evaluateEnum(script, &ok); + if (!ok) { + using namespace QQmlJS; + AST::Node *node = variant.asAST(); + AST::StringLiteral *literal = 0; + if (AST::CallExpression *callExpr = AST::cast(node)) { + if (AST::IdentifierExpression *idExpr = AST::cast(callExpr->base)) { + if (idExpr->name == QLatin1String("QT_TR_NOOP") || idExpr->name == QLatin1String("QT_TRID_NOOP")) { + if (callExpr->arguments && !callExpr->arguments->next) + literal = AST::cast(callExpr->arguments->expression); + if (!literal) { + error(prop, QQmlListModel::tr("ListElement: improperly specified %1").arg(idExpr->name.toString())); + return false; + } + } else if (idExpr->name == QLatin1String("QT_TRANSLATE_NOOP")) { + if (callExpr->arguments && callExpr->arguments->next && !callExpr->arguments->next->next) + literal = AST::cast(callExpr->arguments->next->expression); + if (!literal) { + error(prop, QQmlListModel::tr("ListElement: improperly specified QT_TRANSLATE_NOOP")); + return false; + } + } + } + } + + if (literal) { + d[0] = char(QQmlScript::Variant::String); + d += literal->value.toUtf8(); + } else { + error(prop, QQmlListModel::tr("ListElement: cannot use script for property value")); + return false; + } + } else { + d[0] = char(QQmlScript::Variant::Number); + d += QByteArray::number(v); + } + } + } + d.append('\0'); + data.append(d); + + ListInstruction li; + li.type = ListInstruction::Value; + li.dataIdx = ref; + instr << li; + } + } + + return true; +} + +QByteArray QQmlListModelParser::compile(const QList &customProps) +{ + QList instr; + QByteArray data; + listElementTypeName = QString(); // unknown + + for(int ii = 0; ii < customProps.count(); ++ii) { + const QQmlCustomParserProperty &prop = customProps.at(ii); + if(!prop.name().isEmpty()) { // isn't default property + error(prop, QQmlListModel::tr("ListModel: undefined property '%1'").arg(prop.name())); + return QByteArray(); + } + + if(!compileProperty(prop, instr, data)) { + return QByteArray(); + } + } + + int size = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction) + + data.count(); + + QByteArray rv; + rv.resize(size); + + ListModelData *lmd = (ListModelData *)rv.data(); + lmd->dataOffset = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction); + lmd->instrCount = instr.count(); + for (int ii = 0; ii < instr.count(); ++ii) + lmd->instructions()[ii] = instr.at(ii); + ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count()); + + return rv; +} + +void QQmlListModelParser::setCustomData(QObject *obj, const QByteArray &d) +{ + QQmlListModel *rv = static_cast(obj); + + QV8Engine *engine = QQmlEnginePrivate::getV8Engine(qmlEngine(rv)); + rv->m_engine = engine; + + const ListModelData *lmd = (const ListModelData *)d.constData(); + const char *data = ((const char *)lmd) + lmd->dataOffset; + + bool setRoles = false; + + QStack stack; + + for (int ii = 0; ii < lmd->instrCount; ++ii) { + const ListInstruction &instr = lmd->instructions()[ii]; + + switch(instr.type) { + case ListInstruction::Push: + { + Q_ASSERT(!rv->m_dynamicRoles); + + 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: + stack.pop(); + break; + + case ListInstruction::Value: + { + const DataStackElement &e0 = stack.at(stack.size() - 1); + DataStackElement &e1 = stack[stack.size() - 2]; + + QString name = e0.name; + QVariant value; + + switch (QQmlScript::Variant::Type(data[instr.dataIdx])) { + case QQmlScript::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 QQmlScript::Variant::Boolean: + value = bool(data[1 + instr.dataIdx]); + break; + case QQmlScript::Variant::Number: + value = QByteArray(data + 1 + instr.dataIdx).toDouble(); + break; + case QQmlScript::Variant::String: + value = QString::fromUtf8(data + 1 + instr.dataIdx); + break; + default: + Q_ASSERT("Format error in ListInstruction"); + } + + e1.model->setOrCreateProperty(e1.elementIndex, name, value); + setRoles = true; + } + break; + + case ListInstruction::Set: + { + DataStackElement e; + e.name = QString::fromUtf8(data + instr.dataIdx); + stack.push(e); + } + break; + } + } + + if (setRoles == false) + qmlInfo(obj) << "All ListElement declarations are empty, no roles can be created unless dynamicRoles is set."; +} + +bool QQmlListModelParser::definesEmptyList(const QString &s) +{ + if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { + for (int i=1; i +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + + +class QQmlListModelWorkerAgent; +class ListModel; +class ListLayout; + +class Q_QML_PRIVATE_EXPORT QQmlListModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(bool dynamicRoles READ dynamicRoles WRITE setDynamicRoles) + +public: + QQmlListModel(QObject *parent=0); + ~QQmlListModel(); + + QModelIndex index(int row, int column, const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QHash roleNames() const; + + QVariant data(int index, int role) const; + int count() const; + + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(QQmlV8Function *args); + Q_INVOKABLE void append(QQmlV8Function *args); + Q_INVOKABLE void insert(QQmlV8Function *args); + Q_INVOKABLE QQmlV8Handle get(int index) const; + Q_INVOKABLE void set(int index, const QQmlV8Handle &); + Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + Q_INVOKABLE void sync(); + + QQmlListModelWorkerAgent *agent(); + + bool dynamicRoles() const { return m_dynamicRoles; } + void setDynamicRoles(bool enableDynamicRoles); + +Q_SIGNALS: + void countChanged(); + +private: + friend class QQmlListModelParser; + friend class QQmlListModelWorkerAgent; + friend class ModelObject; + friend class ModelNodeMetaObject; + friend class ListModel; + friend class ListElement; + friend class DynamicRoleModelNode; + friend class DynamicRoleModelNodeMetaObject; + + // Constructs a flat list model for a worker agent + QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent); + QQmlListModel(const QQmlListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent=0); + + QV8Engine *engine() const; + + inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); } + + QQmlListModelWorkerAgent *m_agent; + mutable QV8Engine *m_engine; + bool m_mainThread; + bool m_primary; + + bool m_dynamicRoles; + + ListLayout *m_layout; + ListModel *m_listModel; + + QVector m_modelObjects; + QVector m_roles; + int m_uid; + + struct ElementSync + { + ElementSync() : src(0), target(0) {} + + DynamicRoleModelNode *src; + DynamicRoleModelNode *target; + }; + + int getUid() const { return m_uid; } + + static void sync(QQmlListModel *src, QQmlListModel *target, QHash *targetModelHash); + static QQmlListModel *createWithOwner(QQmlListModel *newOwner); + + void emitItemsChanged(int index, int count, const QVector &roles); + void emitItemsRemoved(int index, int count); + void emitItemsInserted(int index, int count); + void emitItemsMoved(int from, int to, int n); +}; + +// ### FIXME +class QQmlListElement : public QObject +{ +Q_OBJECT +}; + +class QQmlListModelParser : public QQmlCustomParser +{ +public: + QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} + QByteArray compile(const QList &); + void setCustomData(QObject *, const QByteArray &); + +private: + struct ListInstruction + { + enum { Push, Pop, Value, Set } type; + int dataIdx; + }; + struct ListModelData + { + int dataOffset; + int instrCount; + ListInstruction *instructions() const; + }; + bool compileProperty(const QQmlCustomParserProperty &prop, QList &instr, QByteArray &data); + + bool definesEmptyList(const QString &); + + QString listElementTypeName; + + struct DataStackElement + { + DataStackElement() : model(0), elementIndex(0) {} + + QString name; + ListModel *model; + int elementIndex; + }; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlListModel) +QML_DECLARE_TYPE(QQmlListElement) + +#endif // QQMLLISTMODEL_H diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h new file mode 100644 index 0000000000..0190081320 --- /dev/null +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLLISTMODEL_P_P_H +#define QQMLLISTMODEL_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqmllistmodel_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + + +class DynamicRoleModelNode; + +class DynamicRoleModelNodeMetaObject : public QQmlOpenMetaObject +{ +public: + DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object); + ~DynamicRoleModelNodeMetaObject(); + + bool m_enabled; + +protected: + void propertyWrite(int index); + void propertyWritten(int index); + +private: + DynamicRoleModelNode *m_owner; +}; + +class DynamicRoleModelNode : public QObject +{ + Q_OBJECT +public: + DynamicRoleModelNode(QQmlListModel *owner, int uid); + + static DynamicRoleModelNode *create(const QVariantMap &obj, QQmlListModel *owner); + + void updateValues(const QVariantMap &object, QVector &roles); + + QVariant getValue(const QString &name) + { + return m_meta->value(name.toUtf8()); + } + + bool setValue(const QByteArray &name, const QVariant &val) + { + return m_meta->setValue(name, val); + } + + void setNodeUpdatesEnabled(bool enable) + { + m_meta->m_enabled = enable; + } + + int getUid() const + { + return m_uid; + } + + static void sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash *targetModelHash); + +private: + QQmlListModel *m_owner; + int m_uid; + DynamicRoleModelNodeMetaObject *m_meta; + + friend class DynamicRoleModelNodeMetaObject; +}; + +class ModelObject; + +class ModelNodeMetaObject : public QQmlOpenMetaObject +{ +public: + ModelNodeMetaObject(ModelObject *object); + ~ModelNodeMetaObject(); + + bool m_enabled; + +protected: + void propertyWritten(int index); + +private: + + ModelObject *m_obj; +}; + +class ModelObject : public QObject +{ + Q_OBJECT +public: + ModelObject(QQmlListModel *model, int elementIndex); + + void setValue(const QByteArray &name, const QVariant &val, bool force) + { + if (force) { + QVariant existingValue = m_meta->value(name); + if (existingValue.isValid()) { + (*m_meta)[name] = QVariant(); + } + } + m_meta->setValue(name, val); + } + + void setNodeUpdatesEnabled(bool enable) + { + m_meta->m_enabled = enable; + } + + void updateValues(); + void updateValues(const QVector &roles); + + QQmlListModel *m_model; + int m_elementIndex; + +private: + ModelNodeMetaObject *m_meta; +}; + +class ListLayout +{ +public: + ListLayout() : currentBlock(0), currentBlockOffset(0) {} + ListLayout(const ListLayout *other); + ~ListLayout(); + + class Role + { + public: + + Role() : type(Invalid), blockIndex(-1), blockOffset(-1), index(-1), subLayout(0) {} + explicit Role(const Role *other); + ~Role(); + + // This enum must be kept in sync with the roleTypeNames variable in qdeclarativelistmodel.cpp + enum DataType + { + Invalid = -1, + + String, + Number, + Bool, + List, + QObject, + VariantMap, + DateTime, + + MaxDataType + }; + + QString name; + DataType type; + int blockIndex; + int blockOffset; + int index; + ListLayout *subLayout; + }; + + const Role *getRoleOrCreate(const QString &key, const QVariant &data); + const Role &getRoleOrCreate(v8::Handle key, Role::DataType type); + const Role &getRoleOrCreate(const QString &key, Role::DataType type); + + const Role &getExistingRole(int index) { return *roles.at(index); } + const Role *getExistingRole(const QString &key); + const Role *getExistingRole(v8::Handle key); + + int roleCount() const { return roles.count(); } + + static void sync(ListLayout *src, ListLayout *target); + +private: + const Role &createRole(const QString &key, Role::DataType type); + + int currentBlock; + int currentBlockOffset; + QVector roles; + QStringHash roleHash; +}; + +class ListElement +{ +public: + + ListElement(); + ListElement(int existingUid); + ~ListElement(); + + static void sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash *targetModelHash); + + enum + { + BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelObject *) + }; + +private: + + void destroy(ListLayout *layout); + + int setVariantProperty(const ListLayout::Role &role, const QVariant &d); + + int setJsProperty(const ListLayout::Role &role, v8::Handle d, QV8Engine *eng); + + int setStringProperty(const ListLayout::Role &role, const QString &s); + int setDoubleProperty(const ListLayout::Role &role, double n); + int setBoolProperty(const ListLayout::Role &role, bool b); + int setListProperty(const ListLayout::Role &role, ListModel *m); + int setQObjectProperty(const ListLayout::Role &role, QObject *o); + int setVariantMapProperty(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng); + int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m); + int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt); + + void setStringPropertyFast(const ListLayout::Role &role, const QString &s); + void setDoublePropertyFast(const ListLayout::Role &role, double n); + void setBoolPropertyFast(const ListLayout::Role &role, bool b); + void setQObjectPropertyFast(const ListLayout::Role &role, QObject *o); + void setListPropertyFast(const ListLayout::Role &role, ListModel *m); + void setVariantMapFast(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng); + void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt); + + void clearProperty(const ListLayout::Role &role); + + QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV8Engine *eng); + ListModel *getListProperty(const ListLayout::Role &role); + QString *getStringProperty(const ListLayout::Role &role); + QObject *getQObjectProperty(const ListLayout::Role &role); + QQmlGuard *getGuardProperty(const ListLayout::Role &role); + QVariantMap *getVariantMapProperty(const ListLayout::Role &role); + QDateTime *getDateTimeProperty(const ListLayout::Role &role); + + inline char *getPropertyMemory(const ListLayout::Role &role); + + int getUid() const { return uid; } + + char data[BLOCK_SIZE]; + ListElement *next; + + int uid; + ModelObject *m_objectCache; + + friend class ListModel; +}; + +class ListModel +{ +public: + + ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid); + ~ListModel() {} + + void destroy(); + + int setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data); + int setExistingProperty(int uid, const QString &key, v8::Handle data, QV8Engine *eng); + + QVariant getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV8Engine *eng); + ListModel *getListProperty(int elementIndex, const ListLayout::Role &role); + + int roleCount() const + { + return m_layout->roleCount(); + } + + const ListLayout::Role &getExistingRole(int index) + { + return m_layout->getExistingRole(index); + } + + const ListLayout::Role &getOrCreateListRole(const QString &name) + { + return m_layout->getRoleOrCreate(name, ListLayout::Role::List); + } + + int elementCount() const + { + return elements.count(); + } + + void set(int elementIndex, v8::Handle object, QVector *roles, QV8Engine *eng); + void set(int elementIndex, v8::Handle object, QV8Engine *eng); + + int append(v8::Handle object, QV8Engine *eng); + void insert(int elementIndex, v8::Handle object, QV8Engine *eng); + + void clear(); + void remove(int index, int count); + + int appendElement(); + void insertElement(int index); + + void move(int from, int to, int n); + + int getUid() const { return m_uid; } + + static void sync(ListModel *src, ListModel *target, QHash *srcModelHash); + + ModelObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex); + +private: + QPODVector elements; + ListLayout *m_layout; + int m_uid; + + QQmlListModel *m_modelCache; + + struct ElementSync + { + ElementSync() : src(0), target(0) {} + + ListElement *src; + ListElement *target; + }; + + void newElement(int index); + + void updateCacheIndices(); + + friend class ListElement; + friend class QQmlListModelWorkerAgent; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(ListModel *); + +#endif // QQUICKLISTMODEL_P_P_H + diff --git a/src/qml/types/qqmllistmodelworkeragent.cpp b/src/qml/types/qqmllistmodelworkeragent.cpp new file mode 100644 index 0000000000..9554e6d1e5 --- /dev/null +++ b/src/qml/types/qqmllistmodelworkeragent.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmllistmodelworkeragent_p.h" +#include "qqmllistmodel_p_p.h" +#include +#include +#include + +#include +#include +#include + + +QT_BEGIN_NAMESPACE + + +void QQmlListModelWorkerAgent::Data::clearChange(int uid) +{ + for (int i=0 ; i < changes.count() ; ++i) { + if (changes[i].modelUid == uid) { + changes.removeAt(i); + --i; + } + } +} + +void QQmlListModelWorkerAgent::Data::insertChange(int uid, int index, int count) +{ + Change c = { uid, Change::Inserted, index, count, 0, QVector() }; + changes << c; +} + +void QQmlListModelWorkerAgent::Data::removeChange(int uid, int index, int count) +{ + Change c = { uid, Change::Removed, index, count, 0, QVector() }; + changes << c; +} + +void QQmlListModelWorkerAgent::Data::moveChange(int uid, int index, int count, int to) +{ + Change c = { uid, Change::Moved, index, count, to, QVector() }; + changes << c; +} + +void QQmlListModelWorkerAgent::Data::changedChange(int uid, int index, int count, const QVector &roles) +{ + Change c = { uid, Change::Changed, index, count, 0, roles }; + changes << c; +} + +QQmlListModelWorkerAgent::QQmlListModelWorkerAgent(QQmlListModel *model) +: m_ref(1), m_orig(model), m_copy(new QQmlListModel(model, this)) +{ +} + +QQmlListModelWorkerAgent::~QQmlListModelWorkerAgent() +{ + mutex.lock(); + syncDone.wakeAll(); + mutex.unlock(); +} + +void QQmlListModelWorkerAgent::setV8Engine(QV8Engine *eng) +{ + m_copy->m_engine = eng; +} + +void QQmlListModelWorkerAgent::addref() +{ + m_ref.ref(); +} + +void QQmlListModelWorkerAgent::release() +{ + bool del = !m_ref.deref(); + + if (del) + deleteLater(); +} + +void QQmlListModelWorkerAgent::modelDestroyed() +{ + m_orig = 0; +} + +int QQmlListModelWorkerAgent::count() const +{ + return m_copy->count(); +} + +void QQmlListModelWorkerAgent::clear() +{ + m_copy->clear(); +} + +void QQmlListModelWorkerAgent::remove(QQmlV8Function *args) +{ + m_copy->remove(args); +} + +void QQmlListModelWorkerAgent::append(QQmlV8Function *args) +{ + m_copy->append(args); +} + +void QQmlListModelWorkerAgent::insert(QQmlV8Function *args) +{ + m_copy->insert(args); +} + +QQmlV8Handle QQmlListModelWorkerAgent::get(int index) const +{ + return m_copy->get(index); +} + +void QQmlListModelWorkerAgent::set(int index, const QQmlV8Handle &value) +{ + m_copy->set(index, value); +} + +void QQmlListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value) +{ + m_copy->setProperty(index, property, value); +} + +void QQmlListModelWorkerAgent::move(int from, int to, int count) +{ + m_copy->move(from, to, count); +} + +void QQmlListModelWorkerAgent::sync() +{ + Sync *s = new Sync; + s->data = data; + s->list = m_copy; + data.changes.clear(); + + mutex.lock(); + QCoreApplication::postEvent(this, s); + syncDone.wait(&mutex); + mutex.unlock(); +} + +bool QQmlListModelWorkerAgent::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + bool cc = false; + QMutexLocker locker(&mutex); + if (m_orig) { + Sync *s = static_cast(e); + const QList &changes = s->data.changes; + + cc = m_orig->count() != s->list->count(); + + QHash targetModelDynamicHash; + QHash targetModelStaticHash; + + Q_ASSERT(m_orig->m_dynamicRoles == s->list->m_dynamicRoles); + if (m_orig->m_dynamicRoles) + QQmlListModel::sync(s->list, m_orig, &targetModelDynamicHash); + else + ListModel::sync(s->list->m_listModel, m_orig->m_listModel, &targetModelStaticHash); + + for (int ii = 0; ii < changes.count(); ++ii) { + const Change &change = changes.at(ii); + + QQmlListModel *model = 0; + if (m_orig->m_dynamicRoles) { + model = targetModelDynamicHash.value(change.modelUid); + } else { + ListModel *lm = targetModelStaticHash.value(change.modelUid); + if (lm) + model = lm->m_modelCache; + } + + if (model) { + switch (change.type) { + case Change::Inserted: + model->beginInsertRows( + QModelIndex(), change.index, change.index + change.count - 1); + model->endInsertRows(); + break; + case Change::Removed: + model->beginRemoveRows( + QModelIndex(), change.index, change.index + change.count - 1); + model->endRemoveRows(); + break; + case Change::Moved: + model->beginMoveRows( + QModelIndex(), + change.index, + change.index + change.count - 1, + QModelIndex(), + change.to > change.index ? change.to + change.count : change.to); + model->endMoveRows(); + break; + case Change::Changed: + emit model->dataChanged( + model->createIndex(change.index, 0), + model->createIndex(change.index + change.count - 1, 0), + change.roles); + break; + } + } + } + } + + syncDone.wakeAll(); + locker.unlock(); + + if (cc) + emit m_orig->countChanged(); + return true; + } + + return QObject::event(e); +} + +QT_END_NAMESPACE + diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qml/types/qqmllistmodelworkeragent_p.h new file mode 100644 index 0000000000..614017069c --- /dev/null +++ b/src/qml/types/qqmllistmodelworkeragent_p.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLISTMODELWORKERAGENT_P_H +#define QQUICKLISTMODELWORKERAGENT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + + +class QQmlListModel; + +class QQmlListModelWorkerAgent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int count READ count) + +public: + QQmlListModelWorkerAgent(QQmlListModel *); + ~QQmlListModelWorkerAgent(); + void setV8Engine(QV8Engine *eng); + + void addref(); + void release(); + + int count() const; + + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(QQmlV8Function *args); + Q_INVOKABLE void append(QQmlV8Function *args); + Q_INVOKABLE void insert(QQmlV8Function *args); + Q_INVOKABLE QQmlV8Handle get(int index) const; + Q_INVOKABLE void set(int index, const QQmlV8Handle &); + Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + Q_INVOKABLE void sync(); + + struct VariantRef + { + VariantRef() : a(0) {} + VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); } + VariantRef(QQmlListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); } + ~VariantRef() { if (a) a->release(); } + + VariantRef &operator=(const VariantRef &o) { + if (o.a) o.a->addref(); + if (a) a->release(); a = o.a; + return *this; + } + + QQmlListModelWorkerAgent *a; + }; + void modelDestroyed(); +protected: + virtual bool event(QEvent *); + +private: + friend class QQuickWorkerScriptEnginePrivate; + friend class QQmlListModel; + + struct Change + { + int modelUid; + enum { Inserted, Removed, Moved, Changed } type; + int index; // Inserted/Removed/Moved/Changed + int count; // Inserted/Removed/Moved/Changed + int to; // Moved + QVector roles; + }; + + struct Data + { + QList changes; + + void clearChange(int uid); + void insertChange(int uid, int index, int count); + void removeChange(int uid, int index, int count); + void moveChange(int uid, int index, int count, int to); + void changedChange(int uid, int index, int count, const QVector &roles); + }; + Data data; + + struct Sync : public QEvent { + Sync() : QEvent(QEvent::User) {} + Data data; + QQmlListModel *list; + }; + + QAtomicInt m_ref; + QQmlListModel *m_orig; + QQmlListModel *m_copy; + QMutex mutex; + QWaitCondition syncDone; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlListModelWorkerAgent::VariantRef) + +#endif // QQUICKLISTMODELWORKERAGENT_P_H + diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp new file mode 100644 index 0000000000..4f6b0a5580 --- /dev/null +++ b/src/qml/types/qqmlmodelsmodule.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlmodelsmodule_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +void QQmlModelsModule::defineModule() +{ + const char uri[] = "QtQml.Models"; + + qmlRegisterType(uri, 2, 1, "ListElement"); + qmlRegisterCustomType(uri, 2, 1, "ListModel", new QQmlListModelParser); + qmlRegisterType(uri, 2, 1, "DelegateModel"); + qmlRegisterType(uri, 2, 1, "DelegateModelGroup"); + qmlRegisterType(uri, 2, 1, "ObjectModel"); +} + +QT_END_NAMESPACE diff --git a/src/qml/types/qqmlmodelsmodule_p.h b/src/qml/types/qqmlmodelsmodule_p.h new file mode 100644 index 0000000000..6e72dadf8b --- /dev/null +++ b/src/qml/types/qqmlmodelsmodule_p.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLMODELSMODULE_H +#define QQMLMODELSMODULE_H + +#include + +QT_BEGIN_NAMESPACE + +class Q_QML_PRIVATE_EXPORT QQmlModelsModule +{ +public: + static void defineModule(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/types/qqmlobjectmodel.cpp b/src/qml/types/qqmlobjectmodel.cpp new file mode 100644 index 0000000000..7f7bf92fa3 --- /dev/null +++ b/src/qml/types/qqmlobjectmodel.cpp @@ -0,0 +1,269 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlobjectmodel_p.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +QHash QQmlObjectModelAttached::attachedProperties; + + +class QQmlObjectModelPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQmlObjectModel) +public: + class Item { + public: + Item(QObject *i) : item(i), ref(0) {} + + void addRef() { ++ref; } + bool deref() { return --ref == 0; } + + QObject *item; + int ref; + }; + + QQmlObjectModelPrivate() : QObjectPrivate() {} + + static void children_append(QQmlListProperty *prop, QObject *item) { + static_cast(prop->data)->children.append(Item(item)); + static_cast(prop->data)->itemAppended(); + static_cast(prop->data)->emitChildrenChanged(); + } + + static int children_count(QQmlListProperty *prop) { + return static_cast(prop->data)->children.count(); + } + + static QObject *children_at(QQmlListProperty *prop, int index) { + return static_cast(prop->data)->children.at(index).item; + } + + static void children_clear(QQmlListProperty *prop) { + static_cast(prop->data)->itemCleared(static_cast(prop->data)->children); + static_cast(prop->data)->children.clear(); + static_cast(prop->data)->emitChildrenChanged(); + } + + void itemAppended() { + Q_Q(QQmlObjectModel); + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.last().item); + attached->setIndex(children.count()-1); + QQmlChangeSet changeSet; + changeSet.insert(children.count() - 1, 1); + emit q->modelUpdated(changeSet, false); + emit q->countChanged(); + } + + void itemCleared(const QList &children) { + Q_Q(QQmlObjectModel); + foreach (const Item &child, children) + emit q->destroyingItem(child.item); + emit q->countChanged(); + } + + void emitChildrenChanged() { + Q_Q(QQmlObjectModel); + emit q->childrenChanged(); + } + + int indexOf(QObject *item) const { + for (int i = 0; i < children.count(); ++i) + if (children.at(i).item == item) + return i; + return -1; + } + + + QList children; +}; + + +/*! + \qmltype ObjectModel + \instantiates QQmlObjectModel + \inqmlmodule QtQml.Models 2 + \ingroup qtquick-models + \brief Defines a set of items to be used as a model + + A ObjectModel contains the visual items to be used in a view. + When a ObjectModel is used in a view, the view does not require + a delegate since the ObjectModel already contains the visual + delegate (items). + + An item can determine its index within the + model via the \l{ObjectModel::index}{index} attached property. + + The example below places three colored rectangles in a ListView. + \code + import QtQuick 2.0 + + Rectangle { + ObjectModel { + id: itemModel + Rectangle { height: 30; width: 80; color: "red" } + Rectangle { height: 30; width: 80; color: "green" } + Rectangle { height: 30; width: 80; color: "blue" } + } + + ListView { + anchors.fill: parent + model: itemModel + } + } + \endcode + + \image visualitemmodel.png + + \sa {quick/views/objectmodel}{ObjectModel example} +*/ +/*! + \qmltype VisualItemModel + \instantiates QQmlObjectModel + \inqmlmodule QtQuick 2 + \brief Defines a set of objects to be used as a model + + The VisualItemModel type encapsulates contains the objects to be used + as a model. + + This element is now primarily available as ObjectModel in the QtQml.Models module. + VisualItemModel continues to be provided, with the same implementation, in QtQuick for + compatibility reasons. + + For full details about the type, see the \l ObjectModel documentation. + + \sa {QtQml.Models2::ObjectModel} +*/ + +QQmlObjectModel::QQmlObjectModel(QObject *parent) + : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent) +{ +} + +/*! + \qmlattachedproperty int QtQml.Models2::ObjectModel::index + This attached property holds the index of this delegate's item within the model. + + It is attached to each instance of the delegate. +*/ + +QQmlListProperty QQmlObjectModel::children() +{ + Q_D(QQmlObjectModel); + return QQmlListProperty(this, + d, + d->children_append, + d->children_count, + d->children_at, + d->children_clear); +} + +/*! + \qmlproperty int QtQml.Models2::ObjectModel::count + + The number of items in the model. This property is readonly. +*/ +int QQmlObjectModel::count() const +{ + Q_D(const QQmlObjectModel); + return d->children.count(); +} + +bool QQmlObjectModel::isValid() const +{ + return true; +} + +QObject *QQmlObjectModel::object(int index, bool) +{ + Q_D(QQmlObjectModel); + QQmlObjectModelPrivate::Item &item = d->children[index]; + item.addRef(); + if (item.ref == 1) { + emit initItem(index, item.item); + emit createdItem(index, item.item); + } + return item.item; +} + +QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item) +{ + Q_D(QQmlObjectModel); + int idx = d->indexOf(item); + if (idx >= 0) { + if (!d->children[idx].deref()) + return QQmlInstanceModel::Referenced; + } + return 0; +} + +QString QQmlObjectModel::stringValue(int index, const QString &name) +{ + Q_D(QQmlObjectModel); + if (index < 0 || index >= d->children.count()) + return QString(); + return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString(); +} + +int QQmlObjectModel::indexOf(QObject *item, QObject *) const +{ + Q_D(const QQmlObjectModel); + return d->indexOf(item); +} + +QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj) +{ + return QQmlObjectModelAttached::properties(obj); +} + +QT_END_NAMESPACE + diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h new file mode 100644 index 0000000000..59a4a551a7 --- /dev/null +++ b/src/qml/types/qqmlobjectmodel_p.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLINSTANCEMODEL_P_H +#define QQMLINSTANCEMODEL_P_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QObject; +class QQmlChangeSet; + +class Q_QML_PRIVATE_EXPORT QQmlInstanceModel : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int count READ count NOTIFY countChanged) + +public: + virtual ~QQmlInstanceModel() {} + + enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 }; + Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag) + + virtual int count() const = 0; + virtual bool isValid() const = 0; + virtual QObject *object(int index, bool asynchronous=false) = 0; + virtual ReleaseFlags release(QObject *object) = 0; + virtual void cancel(int) {} + virtual QString stringValue(int, const QString &) = 0; + virtual void setWatchedRoles(QList roles) = 0; + + virtual int indexOf(QObject *object, QObject *objectContext) const = 0; + +Q_SIGNALS: + void countChanged(); + void modelUpdated(const QQmlChangeSet &changeSet, bool reset); + void createdItem(int index, QObject *object); + void initItem(int index, QObject *object); + void destroyingItem(QObject *object); + +protected: + QQmlInstanceModel(QObjectPrivate &dd, QObject *parent = 0) + : QObject(dd, parent) {} + +private: + Q_DISABLE_COPY(QQmlInstanceModel) +}; + +class QQmlObjectModelAttached; +class QQmlObjectModelPrivate; +class Q_QML_PRIVATE_EXPORT QQmlObjectModel : public QQmlInstanceModel +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlObjectModel) + + Q_PROPERTY(QQmlListProperty children READ children NOTIFY childrenChanged DESIGNABLE false) + Q_CLASSINFO("DefaultProperty", "children") + +public: + QQmlObjectModel(QObject *parent=0); + virtual ~QQmlObjectModel() {} + + virtual int count() const; + virtual bool isValid() const; + virtual QObject *object(int index, bool asynchronous=false); + virtual ReleaseFlags release(QObject *object); + virtual QString stringValue(int index, const QString &role); + virtual void setWatchedRoles(QList) {} + + virtual int indexOf(QObject *object, QObject *objectContext) const; + + QQmlListProperty children(); + + static QQmlObjectModelAttached *qmlAttachedProperties(QObject *obj); + +Q_SIGNALS: + void childrenChanged(); + +private: + Q_DISABLE_COPY(QQmlObjectModel) +}; + +class QQmlObjectModelAttached : public QObject +{ + Q_OBJECT + +public: + QQmlObjectModelAttached(QObject *parent) + : QObject(parent), m_index(0) {} + ~QQmlObjectModelAttached() { + attachedProperties.remove(parent()); + } + + Q_PROPERTY(int index READ index NOTIFY indexChanged) + int index() const { return m_index; } + void setIndex(int idx) { + if (m_index != idx) { + m_index = idx; + emit indexChanged(); + } + } + + static QQmlObjectModelAttached *properties(QObject *obj) { + QQmlObjectModelAttached *rv = attachedProperties.value(obj); + if (!rv) { + rv = new QQmlObjectModelAttached(obj); + attachedProperties.insert(obj, rv); + } + return rv; + } + +Q_SIGNALS: + void indexChanged(); + +public: + int m_index; + + static QHash attachedProperties; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlInstanceModel) +QML_DECLARE_TYPE(QQmlObjectModel) +QML_DECLARE_TYPEINFO(QQmlObjectModel, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQMLINSTANCEMODEL_P_H diff --git a/src/qml/types/qqmltimer.cpp b/src/qml/types/qqmltimer.cpp new file mode 100644 index 0000000000..a1cb8532f7 --- /dev/null +++ b/src/qml/types/qqmltimer.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmltimer_p.h" + +#include +#include "private/qpauseanimationjob_p.h" +#include + +#include + +QT_BEGIN_NAMESPACE + + + +class QQmlTimerPrivate : public QObjectPrivate, public QAnimationJobChangeListener +{ + Q_DECLARE_PUBLIC(QQmlTimer) +public: + QQmlTimerPrivate() + : interval(1000), running(false), repeating(false), triggeredOnStart(false) + , classBegun(false), componentComplete(false), firstTick(true) {} + + virtual void animationFinished(QAbstractAnimationJob *); + virtual void animationCurrentLoopChanged(QAbstractAnimationJob *) { Q_Q(QQmlTimer); q->ticked(); } + + int interval; + QPauseAnimationJob pause; + bool running : 1; + bool repeating : 1; + bool triggeredOnStart : 1; + bool classBegun : 1; + bool componentComplete : 1; + bool firstTick : 1; +}; + +/*! + \qmltype Timer + \instantiates QQmlTimer + \inqmlmodule QtQml 2 + \ingroup qtquick-interceptors + \brief Triggers a handler at a specified interval + + A Timer can be used to trigger an action either once, or repeatedly + at a given interval. + + Here is a Timer that shows the current date and time, and updates + the text every 500 milliseconds. It uses the JavaScript \c Date + object to access the current time. + + \qml + import QtQuick 2.0 + + Item { + Timer { + interval: 500; running: true; repeat: true + onTriggered: time.text = Date().toString() + } + + Text { id: time } + } + \endqml + + The Timer type is synchronized with the animation timer. Since the animation + timer is usually set to 60fps, the resolution of Timer will be + at best 16ms. + + If the Timer is running and one of its properties is changed, the + elapsed time will be reset. For example, if a Timer with interval of + 1000ms has its \e repeat property changed 500ms after starting, the + elapsed time will be reset to 0, and the Timer will be triggered + 1000ms later. + + \sa {declarative/toys/clocks}{Clocks example} +*/ + +QQmlTimer::QQmlTimer(QObject *parent) + : QObject(*(new QQmlTimerPrivate), parent) +{ + Q_D(QQmlTimer); + d->pause.addAnimationChangeListener(d, QAbstractAnimationJob::Completion | QAbstractAnimationJob::CurrentLoop); + d->pause.setLoopCount(1); + d->pause.setDuration(d->interval); +} + +/*! + \qmlproperty int QtQml2::Timer::interval + + Sets the \a interval between triggers, in milliseconds. + + The default interval is 1000 milliseconds. +*/ +void QQmlTimer::setInterval(int interval) +{ + Q_D(QQmlTimer); + if (interval != d->interval) { + d->interval = interval; + update(); + emit intervalChanged(); + } +} + +int QQmlTimer::interval() const +{ + Q_D(const QQmlTimer); + return d->interval; +} + +/*! + \qmlproperty bool QtQml2::Timer::running + + If set to true, starts the timer; otherwise stops the timer. + For a non-repeating timer, \a running is set to false after the + timer has been triggered. + + \a running defaults to false. + + \sa repeat +*/ +bool QQmlTimer::isRunning() const +{ + Q_D(const QQmlTimer); + return d->running; +} + +void QQmlTimer::setRunning(bool running) +{ + Q_D(QQmlTimer); + if (d->running != running) { + d->running = running; + d->firstTick = true; + emit runningChanged(); + update(); + } +} + +/*! + \qmlproperty bool QtQml2::Timer::repeat + + If \a repeat is true the timer is triggered repeatedly at the + specified interval; otherwise, the timer will trigger once at the + specified interval and then stop (i.e. running will be set to false). + + \a repeat defaults to false. + + \sa running +*/ +bool QQmlTimer::isRepeating() const +{ + Q_D(const QQmlTimer); + return d->repeating; +} + +void QQmlTimer::setRepeating(bool repeating) +{ + Q_D(QQmlTimer); + if (repeating != d->repeating) { + d->repeating = repeating; + update(); + emit repeatChanged(); + } +} + +/*! + \qmlproperty bool QtQml2::Timer::triggeredOnStart + + When a timer is started, the first trigger is usually after the specified + interval has elapsed. It is sometimes desirable to trigger immediately + when the timer is started; for example, to establish an initial + state. + + If \a triggeredOnStart is true, the timer is triggered immediately + when started, and subsequently at the specified interval. Note that if + \e repeat is set to false, the timer is triggered twice; once on start, + and again at the interval. + + \a triggeredOnStart defaults to false. + + \sa running +*/ +bool QQmlTimer::triggeredOnStart() const +{ + Q_D(const QQmlTimer); + return d->triggeredOnStart; +} + +void QQmlTimer::setTriggeredOnStart(bool triggeredOnStart) +{ + Q_D(QQmlTimer); + if (d->triggeredOnStart != triggeredOnStart) { + d->triggeredOnStart = triggeredOnStart; + update(); + emit triggeredOnStartChanged(); + } +} + +/*! + \qmlmethod QtQml2::Timer::start() + \brief Starts the timer + + If the timer is already running, calling this method has no effect. The + \c running property will be true following a call to \c start(). +*/ +void QQmlTimer::start() +{ + setRunning(true); +} + +/*! + \qmlmethod QtQml2::Timer::stop() + \brief Stops the timer + + If the timer is not running, calling this method has no effect. The + \c running property will be false following a call to \c stop(). +*/ +void QQmlTimer::stop() +{ + setRunning(false); +} + +/*! + \qmlmethod QtQml2::Timer::restart() + \brief Restarts the timer + + If the Timer is not running it will be started, otherwise it will be + stopped, reset to initial state and started. The \c running property + will be true following a call to \c restart(). +*/ +void QQmlTimer::restart() +{ + setRunning(false); + setRunning(true); +} + +void QQmlTimer::update() +{ + Q_D(QQmlTimer); + if (d->classBegun && !d->componentComplete) + return; + d->pause.stop(); + if (d->running) { + d->pause.setCurrentTime(0); + d->pause.setLoopCount(d->repeating ? -1 : 1); + d->pause.setDuration(d->interval); + d->pause.start(); + if (d->triggeredOnStart && d->firstTick) { + QCoreApplication::removePostedEvents(this, QEvent::MetaCall); + QMetaObject::invokeMethod(this, "ticked", Qt::QueuedConnection); + } + } +} + +void QQmlTimer::classBegin() +{ + Q_D(QQmlTimer); + d->classBegun = true; +} + +void QQmlTimer::componentComplete() +{ + Q_D(QQmlTimer); + d->componentComplete = true; + update(); +} + +/*! + \qmlsignal QtQml2::Timer::onTriggered() + + This handler is called when the Timer is triggered. +*/ +void QQmlTimer::ticked() +{ + Q_D(QQmlTimer); + if (d->running && (d->pause.currentTime() > 0 || (d->triggeredOnStart && d->firstTick))) + emit triggered(); + d->firstTick = false; +} + +void QQmlTimerPrivate::animationFinished(QAbstractAnimationJob *) +{ + Q_Q(QQmlTimer); + if (repeating || !running) + return; + running = false; + firstTick = false; + emit q->triggered(); + emit q->runningChanged(); +} + +QT_END_NAMESPACE diff --git a/src/qml/types/qqmltimer_p.h b/src/qml/types/qqmltimer_p.h new file mode 100644 index 0000000000..c625522851 --- /dev/null +++ b/src/qml/types/qqmltimer_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTIMER_H +#define QQMLTIMER_H + +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QQmlTimerPrivate; +class Q_QML_PRIVATE_EXPORT QQmlTimer : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlTimer) + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged) + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool repeat READ isRepeating WRITE setRepeating NOTIFY repeatChanged) + Q_PROPERTY(bool triggeredOnStart READ triggeredOnStart WRITE setTriggeredOnStart NOTIFY triggeredOnStartChanged) + Q_PROPERTY(QObject *parent READ parent CONSTANT) + +public: + QQmlTimer(QObject *parent=0); + + void setInterval(int interval); + int interval() const; + + bool isRunning() const; + void setRunning(bool running); + + bool isRepeating() const; + void setRepeating(bool repeating); + + bool triggeredOnStart() const; + void setTriggeredOnStart(bool triggeredOnStart); + +protected: + void classBegin(); + void componentComplete(); + +public Q_SLOTS: + void start(); + void stop(); + void restart(); + +Q_SIGNALS: + void triggered(); + void runningChanged(); + void intervalChanged(); + void repeatChanged(); + void triggeredOnStartChanged(); + +private: + void update(); + +private Q_SLOTS: + void ticked(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlTimer) + +#endif diff --git a/src/qml/types/qquickpackage.cpp b/src/qml/types/qquickpackage.cpp new file mode 100644 index 0000000000..e885524b27 --- /dev/null +++ b/src/qml/types/qquickpackage.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpackage_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Package + \instantiates QQuickPackage + \inqmlmodule QtQuick 2 + \ingroup qtquick-views + \brief Specifies a collection of named items + + The Package class is used in conjunction with + VisualDataModel to enable delegates with a shared context + to be provided to multiple views. + + Any item within a Package may be assigned a name via the + \l{Package::name}{Package.name} attached property. + + The example below creates a Package containing two named items; + \e list and \e grid. The third item in the package (the \l Rectangle) is parented to whichever + delegate it should appear in. This allows an item to move + between views. + + \snippet quick/views/package/Delegate.qml 0 + + These named items are used as the delegates by the two views who + reference the special \l{VisualDataModel::parts} property to select + a model which provides the chosen delegate. + + \snippet quick/views/package/view.qml 0 + + \sa {quick/views/package}{Package example}, {quick/demos/photoviewer}{Photo Viewer example}, QtQml +*/ + +/*! + \qmlattachedproperty string QtQuick2::Package::name + This attached property holds the name of an item within a Package. +*/ + + +class QQuickPackagePrivate : public QObjectPrivate +{ +public: + QQuickPackagePrivate() {} + + struct DataGuard : public QQmlGuard + { + DataGuard(QObject *obj, QList *l) : list(l) { (QQmlGuard&)*this = obj; } + QList *list; + void objectDestroyed(QObject *) { + // we assume priv will always be destroyed after objectDestroyed calls + list->removeOne(*this); + } + }; + + QList dataList; + static void data_append(QQmlListProperty *prop, QObject *o) { + QList *list = static_cast *>(prop->data); + list->append(DataGuard(o, list)); + } + static void data_clear(QQmlListProperty *prop) { + QList *list = static_cast *>(prop->data); + list->clear(); + } + static QObject *data_at(QQmlListProperty *prop, int index) { + QList *list = static_cast *>(prop->data); + return list->at(index); + } + static int data_count(QQmlListProperty *prop) { + QList *list = static_cast *>(prop->data); + return list->count(); + } +}; + +QHash QQuickPackageAttached::attached; + +QQuickPackageAttached::QQuickPackageAttached(QObject *parent) +: QObject(parent) +{ + attached.insert(parent, this); +} + +QQuickPackageAttached::~QQuickPackageAttached() +{ + attached.remove(parent()); +} + +QString QQuickPackageAttached::name() const +{ + return _name; +} + +void QQuickPackageAttached::setName(const QString &n) +{ + _name = n; +} + +QQuickPackage::QQuickPackage(QObject *parent) + : QObject(*(new QQuickPackagePrivate), parent) +{ +} + +QQuickPackage::~QQuickPackage() +{ +} + +QQmlListProperty QQuickPackage::data() +{ + Q_D(QQuickPackage); + return QQmlListProperty(this, &d->dataList, QQuickPackagePrivate::data_append, + QQuickPackagePrivate::data_count, + QQuickPackagePrivate::data_at, + QQuickPackagePrivate::data_clear); +} + +bool QQuickPackage::hasPart(const QString &name) +{ + Q_D(QQuickPackage); + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); + if (a && a->name() == name) + return true; + } + return false; +} + +QObject *QQuickPackage::part(const QString &name) +{ + Q_D(QQuickPackage); + if (name.isEmpty() && !d->dataList.isEmpty()) + return d->dataList.at(0); + + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); + if (a && a->name() == name) + return obj; + } + + if (name == QLatin1String("default") && !d->dataList.isEmpty()) + return d->dataList.at(0); + + return 0; +} + +QQuickPackageAttached *QQuickPackage::qmlAttachedProperties(QObject *o) +{ + return new QQuickPackageAttached(o); +} + + + +QT_END_NAMESPACE diff --git a/src/qml/types/qquickpackage_p.h b/src/qml/types/qquickpackage_p.h new file mode 100644 index 0000000000..9427c886a8 --- /dev/null +++ b/src/qml/types/qquickpackage_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPACKAGE_H +#define QQUICKPACKAGE_H + +#include + +QT_BEGIN_NAMESPACE + +class QQuickPackagePrivate; +class QQuickPackageAttached; +class Q_AUTOTEST_EXPORT QQuickPackage : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickPackage) + + Q_CLASSINFO("DefaultProperty", "data") + Q_PROPERTY(QQmlListProperty data READ data) + +public: + QQuickPackage(QObject *parent=0); + virtual ~QQuickPackage(); + + QQmlListProperty data(); + + QObject *part(const QString & = QString()); + bool hasPart(const QString &); + + static QQuickPackageAttached *qmlAttachedProperties(QObject *); +}; + +class QQuickPackageAttached : public QObject +{ +Q_OBJECT +Q_PROPERTY(QString name READ name WRITE setName) +public: + QQuickPackageAttached(QObject *parent); + virtual ~QQuickPackageAttached(); + + QString name() const; + void setName(const QString &n); + + static QHash attached; +private: + QString _name; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPackage) +QML_DECLARE_TYPEINFO(QQuickPackage, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKPACKAGE_H diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp new file mode 100644 index 0000000000..ad09e1ec0c --- /dev/null +++ b/src/qml/types/qquickworkerscript.cpp @@ -0,0 +1,740 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickworkerscript_p.h" +#include "qqmllistmodel_p.h" +#include "qqmllistmodelworkeragent_p.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qqmlnetworkaccessmanagerfactory.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class WorkerDataEvent : public QEvent +{ +public: + enum Type { WorkerData = QEvent::User }; + + WorkerDataEvent(int workerId, const QByteArray &data); + virtual ~WorkerDataEvent(); + + int workerId() const; + QByteArray data() const; + +private: + int m_id; + QByteArray m_data; +}; + +class WorkerLoadEvent : public QEvent +{ +public: + enum Type { WorkerLoad = WorkerDataEvent::WorkerData + 1 }; + + WorkerLoadEvent(int workerId, const QUrl &url); + + int workerId() const; + QUrl url() const; + +private: + int m_id; + QUrl m_url; +}; + +class WorkerRemoveEvent : public QEvent +{ +public: + enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 }; + + WorkerRemoveEvent(int workerId); + + int workerId() const; + +private: + int m_id; +}; + +class WorkerErrorEvent : public QEvent +{ +public: + enum Type { WorkerError = WorkerRemoveEvent::WorkerRemove + 1 }; + + WorkerErrorEvent(const QQmlError &error); + + QQmlError error() const; + +private: + QQmlError m_error; +}; + +class QQuickWorkerScriptEnginePrivate : public QObject +{ + Q_OBJECT +public: + enum WorkerEventTypes { + WorkerDestroyEvent = QEvent::User + 100 + }; + + QQuickWorkerScriptEnginePrivate(QQmlEngine *eng); + + class WorkerEngine : public QV8Engine + { + public: + WorkerEngine(QQuickWorkerScriptEnginePrivate *parent); + ~WorkerEngine(); + + void init(); + virtual QNetworkAccessManager *networkAccessManager(); + + QQuickWorkerScriptEnginePrivate *p; + + v8::Local sendFunction(int id); + void callOnMessage(v8::Handle object, v8::Handle arg); + private: + v8::Persistent onmessage; + v8::Persistent createsend; + QNetworkAccessManager *accessManager; + }; + + WorkerEngine *workerEngine; + static QQuickWorkerScriptEnginePrivate *get(QV8Engine *e) { + return static_cast(e)->p; + } + + QQmlEngine *qmlengine; + + QMutex m_lock; + QWaitCondition m_wait; + + struct WorkerScript { + WorkerScript(); + ~WorkerScript(); + + int id; + QUrl source; + bool initialized; + QQuickWorkerScript *owner; + v8::Persistent object; + }; + + QHash workers; + v8::Handle getWorker(WorkerScript *); + + int m_nextId; + + static v8::Handle sendMessage(const v8::Arguments &args); + +signals: + void stopThread(); + +protected: + virtual bool event(QEvent *); + +private: + void processMessage(int, const QByteArray &); + void processLoad(int, const QUrl &); + void reportScriptException(WorkerScript *, const QQmlError &error); +}; + +QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent) +: QV8Engine(0), p(parent), accessManager(0) +{ +} + +QQuickWorkerScriptEnginePrivate::WorkerEngine::~WorkerEngine() +{ + qPersistentDispose(createsend); + qPersistentDispose(onmessage); + delete accessManager; +} + +void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() +{ + initQmlGlobalObject(); +#define CALL_ONMESSAGE_SCRIPT \ + "(function(object, message) { "\ + "var isfunction = false; "\ + "try { "\ + "isfunction = object.WorkerScript.onMessage instanceof Function; "\ + "} catch (e) {}" \ + "if (isfunction) "\ + "object.WorkerScript.onMessage(message); "\ + "})" + +#define SEND_MESSAGE_CREATE_SCRIPT \ + "(function(method, engine) { "\ + "return (function(id) { "\ + "return (function(message) { "\ + "if (arguments.length) method(engine, id, message); "\ + "}); "\ + "}); "\ + "})" + + v8::HandleScope handle_scope; + v8::Context::Scope scope(context()); + + { + v8::Local onmessagescript = v8::Script::New(v8::String::New(CALL_ONMESSAGE_SCRIPT)); + onmessage = qPersistentNew(v8::Handle::Cast(onmessagescript->Run())); + } + { + v8::Local createsendscript = v8::Script::New(v8::String::New(SEND_MESSAGE_CREATE_SCRIPT)); + v8::Local createsendconstructor = v8::Local::Cast(createsendscript->Run()); + + v8::Handle args[] = { + V8FUNCTION(QQuickWorkerScriptEnginePrivate::sendMessage, this) + }; + v8::Local createsendvalue = createsendconstructor->Call(global(), 1, args); + + createsend = qPersistentNew(v8::Handle::Cast(createsendvalue)); + } +} + +// Requires handle and context scope +v8::Local QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(int id) +{ + v8::Handle args[] = { v8::Integer::New(id) }; + return v8::Local::Cast(createsend->Call(global(), 1, args)); +} + +// Requires handle and context scope +void QQuickWorkerScriptEnginePrivate::WorkerEngine::callOnMessage(v8::Handle object, + v8::Handle arg) +{ + v8::Handle args[] = { object, arg }; + onmessage->Call(global(), 2, args); +} + +QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerEngine::networkAccessManager() +{ + if (!accessManager) { + if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) { + accessManager = p->qmlengine->networkAccessManagerFactory()->create(p); + } else { + accessManager = new QNetworkAccessManager(p); + } + } + return accessManager; +} + +QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine) +: workerEngine(0), qmlengine(engine), m_nextId(0) +{ +} + +v8::Handle QQuickWorkerScriptEnginePrivate::sendMessage(const v8::Arguments &args) +{ + WorkerEngine *engine = (WorkerEngine*)V8ENGINE(); + + int id = args[1]->Int32Value(); + + QByteArray data = QV8Worker::serialize(args[2], engine); + + QMutexLocker locker(&engine->p->m_lock); + WorkerScript *script = engine->p->workers.value(id); + if (!script) + return v8::Undefined(); + + if (script->owner) + QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data)); + + return v8::Undefined(); +} + +// Requires handle scope and context scope +v8::Handle QQuickWorkerScriptEnginePrivate::getWorker(WorkerScript *script) +{ + if (!script->initialized) { + script->initialized = true; + + script->object = qPersistentNew(workerEngine->contextWrapper()->urlScope(script->source)); + + workerEngine->contextWrapper()->setReadOnly(script->object, false); + + v8::Local api = v8::Object::New(); + api->Set(v8::String::New("sendMessage"), workerEngine->sendFunction(script->id)); + + script->object->Set(v8::String::New("WorkerScript"), api); + + workerEngine->contextWrapper()->setReadOnly(script->object, true); + } + + return script->object; +} + +bool QQuickWorkerScriptEnginePrivate::event(QEvent *event) +{ + if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { + WorkerDataEvent *workerEvent = static_cast(event); + processMessage(workerEvent->workerId(), workerEvent->data()); + return true; + } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) { + WorkerLoadEvent *workerEvent = static_cast(event); + processLoad(workerEvent->workerId(), workerEvent->url()); + return true; + } else if (event->type() == (QEvent::Type)WorkerDestroyEvent) { + emit stopThread(); + return true; + } else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) { + WorkerRemoveEvent *workerEvent = static_cast(event); + workers.remove(workerEvent->workerId()); + return true; + } else { + return QObject::event(event); + } +} + +void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &data) +{ + WorkerScript *script = workers.value(id); + if (!script) + return; + + v8::HandleScope handle_scope; + v8::Context::Scope scope(workerEngine->context()); + + v8::Handle value = QV8Worker::deserialize(data, workerEngine); + + v8::TryCatch tc; + workerEngine->callOnMessage(script->object, value); + + if (tc.HasCaught()) { + QQmlError error; + QQmlExpressionPrivate::exceptionToError(tc.Message(), error); + reportScriptException(script, error); + } +} + +void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) +{ + if (url.isRelative()) + return; + + QString fileName = QQmlFile::urlToLocalFileOrQrc(url); + + QFile f(fileName); + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QString sourceCode = QString::fromUtf8(data); + QQmlScript::Parser::extractPragmas(sourceCode); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(workerEngine->context()); + + WorkerScript *script = workers.value(id); + if (!script) + return; + script->source = url; + v8::Handle activation = getWorker(script); + if (activation.IsEmpty()) + return; + + // XXX ??? + // workerEngine->baseUrl = url; + + v8::TryCatch tc; + v8::Local program = workerEngine->qmlModeCompile(sourceCode, url.toString()); + + if (!tc.HasCaught()) + program->Run(activation); + + if (tc.HasCaught()) { + QQmlError error; + QQmlExpressionPrivate::exceptionToError(tc.Message(), error); + reportScriptException(script, error); + } + } else { + qWarning().nospace() << "WorkerScript: Cannot find source file " << url.toString(); + } +} + +void QQuickWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script, + const QQmlError &error) +{ + QQuickWorkerScriptEnginePrivate *p = QQuickWorkerScriptEnginePrivate::get(workerEngine); + + QMutexLocker locker(&p->m_lock); + if (script->owner) + QCoreApplication::postEvent(script->owner, new WorkerErrorEvent(error)); +} + +WorkerDataEvent::WorkerDataEvent(int workerId, const QByteArray &data) +: QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data) +{ +} + +WorkerDataEvent::~WorkerDataEvent() +{ +} + +int WorkerDataEvent::workerId() const +{ + return m_id; +} + +QByteArray WorkerDataEvent::data() const +{ + return m_data; +} + +WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url) +: QEvent((QEvent::Type)WorkerLoad), m_id(workerId), m_url(url) +{ +} + +int WorkerLoadEvent::workerId() const +{ + return m_id; +} + +QUrl WorkerLoadEvent::url() const +{ + return m_url; +} + +WorkerRemoveEvent::WorkerRemoveEvent(int workerId) +: QEvent((QEvent::Type)WorkerRemove), m_id(workerId) +{ +} + +int WorkerRemoveEvent::workerId() const +{ + return m_id; +} + +WorkerErrorEvent::WorkerErrorEvent(const QQmlError &error) +: QEvent((QEvent::Type)WorkerError), m_error(error) +{ +} + +QQmlError WorkerErrorEvent::error() const +{ + return m_error; +} + +QQuickWorkerScriptEngine::QQuickWorkerScriptEngine(QQmlEngine *parent) +: QThread(parent), d(new QQuickWorkerScriptEnginePrivate(parent)) +{ + d->m_lock.lock(); + connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection); + start(QThread::LowestPriority); + d->m_wait.wait(&d->m_lock); + d->moveToThread(this); + d->m_lock.unlock(); +} + +QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine() +{ + d->m_lock.lock(); + QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QQuickWorkerScriptEnginePrivate::WorkerDestroyEvent)); + d->m_lock.unlock(); + + //We have to force to cleanup the main thread's event queue here + //to make sure the main GUI release all pending locks/wait conditions which + //some worker script/agent are waiting for (QQmlListModelWorkerAgent::sync() for example). + while (!isFinished()) { + // We can't simply wait here, because the worker thread will not terminate + // until the main thread processes the last data event it generates + QCoreApplication::processEvents(); + yieldCurrentThread(); + } + + d->deleteLater(); +} + +QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript() +: id(-1), initialized(false), owner(0) +{ +} + +QQuickWorkerScriptEnginePrivate::WorkerScript::~WorkerScript() +{ + qPersistentDispose(object); +} + +int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner) +{ + typedef QQuickWorkerScriptEnginePrivate::WorkerScript WorkerScript; + WorkerScript *script = new WorkerScript; + + script->id = d->m_nextId++; + script->owner = owner; + + d->m_lock.lock(); + d->workers.insert(script->id, script); + d->m_lock.unlock(); + + return script->id; +} + +void QQuickWorkerScriptEngine::removeWorkerScript(int id) +{ + QQuickWorkerScriptEnginePrivate::WorkerScript* script = d->workers.value(id); + if (script) { + script->owner = 0; + QCoreApplication::postEvent(d, new WorkerRemoveEvent(id)); + } +} + +void QQuickWorkerScriptEngine::executeUrl(int id, const QUrl &url) +{ + QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url)); +} + +void QQuickWorkerScriptEngine::sendMessage(int id, const QByteArray &data) +{ + QCoreApplication::postEvent(d, new WorkerDataEvent(id, data)); +} + +void QQuickWorkerScriptEngine::run() +{ + d->m_lock.lock(); + + d->workerEngine = new QQuickWorkerScriptEnginePrivate::WorkerEngine(d); + d->workerEngine->init(); + + d->m_wait.wakeAll(); + + d->m_lock.unlock(); + + exec(); + + qDeleteAll(d->workers); + d->workers.clear(); + + delete d->workerEngine; d->workerEngine = 0; +} + + +/*! + \qmltype WorkerScript + \instantiates QQuickWorkerScript + \ingroup qtquick-threading + \inqmlmodule QtQuick 2 + \brief Enables the use of threads in a Qt Quick application + + Use WorkerScript to run operations in a new thread. + This is useful for running operations in the background so + that the main GUI thread is not blocked. + + Messages can be passed between the new thread and the parent thread + using \l sendMessage() and the \l {WorkerScript::onMessage}{onMessage()} handler. + + An example: + + \snippet qml/workerscript/workerscript.qml 0 + + The above worker script specifies a JavaScript file, "script.js", that handles + the operations to be performed in the new thread. Here is \c script.js: + + \quotefile qml/workerscript/script.js + + When the user clicks anywhere within the rectangle, \c sendMessage() is + called, triggering the \tt WorkerScript.onMessage() handler in + \tt script.js. This in turn sends a reply message that is then received + by the \tt onMessage() handler of \tt myWorker. + + + \section3 Restrictions + + Since the \c WorkerScript.onMessage() function is run in a separate thread, the + JavaScript file is evaluated in a context separate from the main QML engine. This means + that unlike an ordinary JavaScript file that is imported into QML, the \c script.js + in the above example cannot access the properties, methods or other attributes + of the QML item, nor can it access any context properties set on the QML object + through QQmlContext. + + Additionally, there are restrictions on the types of values that can be passed to and + from the worker script. See the sendMessage() documentation for details. + + Worker script can not use \l {qtqml-javascript-imports.html}{.import} syntax. + + \sa {declarative/threading/workerscript}{WorkerScript example}, + {declarative/threading/threadedlistmodel}{Threaded ListModel example} +*/ +QQuickWorkerScript::QQuickWorkerScript(QObject *parent) +: QObject(parent), m_engine(0), m_scriptId(-1), m_componentComplete(true) +{ +} + +QQuickWorkerScript::~QQuickWorkerScript() +{ + if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId); +} + +/*! + \qmlproperty url WorkerScript::source + + This holds the url of the JavaScript file that implements the + \tt WorkerScript.onMessage() handler for threaded operations. +*/ +QUrl QQuickWorkerScript::source() const +{ + return m_source; +} + +void QQuickWorkerScript::setSource(const QUrl &source) +{ + if (m_source == source) + return; + + m_source = source; + + if (engine()) + m_engine->executeUrl(m_scriptId, m_source); + + emit sourceChanged(); +} + +/*! + \qmlmethod WorkerScript::sendMessage(jsobject message) + + Sends the given \a message to a worker script handler in another + thread. The other worker script handler can receive this message + through the onMessage() handler. + + The \c message object may only contain values of the following + types: + + \list + \li boolean, number, string + \li JavaScript objects and arrays + \li ListModel objects (any other type of QObject* is not allowed) + \endlist + + All objects and arrays are copied to the \c message. With the exception + of ListModel objects, any modifications by the other thread to an object + passed in \c message will not be reflected in the original object. +*/ +void QQuickWorkerScript::sendMessage(QQmlV8Function *args) +{ + if (!engine()) { + qWarning("QQuickWorkerScript: Attempt to send message before WorkerScript establishment"); + return; + } + + v8::Handle argument = v8::Undefined(); + if (args->Length() != 0) + argument = (*args)[0]; + + m_engine->sendMessage(m_scriptId, QV8Worker::serialize(argument, args->engine())); +} + +void QQuickWorkerScript::classBegin() +{ + m_componentComplete = false; +} + +QQuickWorkerScriptEngine *QQuickWorkerScript::engine() +{ + if (m_engine) return m_engine; + if (m_componentComplete) { + QQmlEngine *engine = qmlEngine(this); + if (!engine) { + qWarning("QQuickWorkerScript: engine() called without qmlEngine() set"); + return 0; + } + + m_engine = QQmlEnginePrivate::get(engine)->getWorkerScriptEngine(); + m_scriptId = m_engine->registerWorkerScript(this); + + if (m_source.isValid()) + m_engine->executeUrl(m_scriptId, m_source); + + return m_engine; + } + return 0; +} + +void QQuickWorkerScript::componentComplete() +{ + m_componentComplete = true; + engine(); // Get it started now. +} + +/*! + \qmlsignal WorkerScript::onMessage(jsobject msg) + + This handler is called when a message \a msg is received from a worker + script in another thread through a call to sendMessage(). +*/ + +bool QQuickWorkerScript::event(QEvent *event) +{ + if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { + QQmlEngine *engine = qmlEngine(this); + if (engine) { + WorkerDataEvent *workerEvent = static_cast(event); + QV8Engine *v8engine = QQmlEnginePrivate::get(engine)->v8engine(); + v8::HandleScope handle_scope; + v8::Context::Scope scope(v8engine->context()); + v8::Handle value = QV8Worker::deserialize(workerEvent->data(), v8engine); + emit message(QQmlV8Handle::fromHandle(value)); + } + return true; + } else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) { + WorkerErrorEvent *workerEvent = static_cast(event); + QQmlEnginePrivate::warning(qmlEngine(this), workerEvent->error()); + return true; + } else { + return QObject::event(event); + } +} + +QT_END_NAMESPACE + +#include + diff --git a/src/qml/types/qquickworkerscript_p.h b/src/qml/types/qquickworkerscript_p.h new file mode 100644 index 0000000000..1ab5208e45 --- /dev/null +++ b/src/qml/types/qquickworkerscript_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKWORKERSCRIPT_P_H +#define QQUICKWORKERSCRIPT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqml.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + + +class QQuickWorkerScript; +class QQuickWorkerScriptEnginePrivate; +class QQuickWorkerScriptEngine : public QThread +{ +Q_OBJECT +public: + QQuickWorkerScriptEngine(QQmlEngine *parent = 0); + virtual ~QQuickWorkerScriptEngine(); + + int registerWorkerScript(QQuickWorkerScript *); + void removeWorkerScript(int); + void executeUrl(int, const QUrl &); + void sendMessage(int, const QByteArray &); + +protected: + virtual void run(); + +private: + QQuickWorkerScriptEnginePrivate *d; +}; + +class QQmlV8Function; +class QQmlV8Handle; +class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + + Q_INTERFACES(QQmlParserStatus) +public: + QQuickWorkerScript(QObject *parent = 0); + virtual ~QQuickWorkerScript(); + + QUrl source() const; + void setSource(const QUrl &); + +public slots: + void sendMessage(QQmlV8Function*); + +signals: + void sourceChanged(); + void message(const QQmlV8Handle &messageObject); + +protected: + virtual void classBegin(); + virtual void componentComplete(); + virtual bool event(QEvent *); + +private: + QQuickWorkerScriptEngine *engine(); + QQuickWorkerScriptEngine *m_engine; + int m_scriptId; + QUrl m_source; + bool m_componentComplete; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickWorkerScript) + +#endif // QQUICKWORKERSCRIPT_P_H diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri new file mode 100644 index 0000000000..22e62ea8de --- /dev/null +++ b/src/qml/types/types.pri @@ -0,0 +1,26 @@ +SOURCES += \ + $$PWD/qqmlbind.cpp \ + $$PWD/qqmlconnections.cpp \ + $$PWD/qqmldelegatemodel.cpp \ + $$PWD/qqmllistmodel.cpp \ + $$PWD/qqmllistmodelworkeragent.cpp \ + $$PWD/qqmlmodelsmodule.cpp \ + $$PWD/qqmlobjectmodel.cpp \ + $$PWD/qqmltimer.cpp \ + $$PWD/qquickpackage.cpp \ + $$PWD/qquickworkerscript.cpp + +HEADERS += \ + $$PWD/qqmlbind_p.h \ + $$PWD/qqmlconnections_p.h \ + $$PWD/qqmldelegatemodel_p.h \ + $$PWD/qqmldelegatemodel_p_p.h \ + $$PWD/qqmllistmodel_p.h \ + $$PWD/qqmllistmodel_p_p.h \ + $$PWD/qqmllistmodelworkeragent_p.h \ + $$PWD/qqmlmodelsmodule_p.h \ + $$PWD/qqmlobjectmodel_p.h \ + $$PWD/qqmltimer_p.h \ + $$PWD/qquickpackage_p.h \ + $$PWD/qquickworkerscript_p.h + -- cgit v1.2.3