diff options
Diffstat (limited to 'src/qmlmodels/qqmlobjectmodel.cpp')
-rw-r--r-- | src/qmlmodels/qqmlobjectmodel.cpp | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/src/qmlmodels/qqmlobjectmodel.cpp b/src/qmlmodels/qqmlobjectmodel.cpp new file mode 100644 index 0000000000..7409178616 --- /dev/null +++ b/src/qmlmodels/qqmlobjectmodel.cpp @@ -0,0 +1,431 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlobjectmodel_p.h" + +#include <QtCore/qcoreapplication.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlinfo.h> + +#include <private/qqmlchangeset_p.h> +#include <private/qqmlglobal_p.h> +#include <private/qobject_p.h> +#include <private/qpodvector_p.h> + +#include <QtCore/qhash.h> +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE + +QHash<QObject*, QQmlObjectModelAttached*> 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(), moveId(0) {} + + static void children_append(QQmlListProperty<QObject> *prop, QObject *item) { + int index = static_cast<QQmlObjectModelPrivate *>(prop->data)->children.count(); + static_cast<QQmlObjectModelPrivate *>(prop->data)->insert(index, item); + } + + static int children_count(QQmlListProperty<QObject> *prop) { + return static_cast<QQmlObjectModelPrivate *>(prop->data)->children.count(); + } + + static QObject *children_at(QQmlListProperty<QObject> *prop, int index) { + return static_cast<QQmlObjectModelPrivate *>(prop->data)->children.at(index).item; + } + + static void children_clear(QQmlListProperty<QObject> *prop) { + static_cast<QQmlObjectModelPrivate *>(prop->data)->clear(); + } + + void insert(int index, QObject *item) { + Q_Q(QQmlObjectModel); + children.insert(index, Item(item)); + for (int i = index; i < children.count(); ++i) { + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); + attached->setIndex(i); + } + QQmlChangeSet changeSet; + changeSet.insert(index, 1); + emit q->modelUpdated(changeSet, false); + emit q->countChanged(); + emit q->childrenChanged(); + } + + void move(int from, int to, int n) { + Q_Q(QQmlObjectModel); + 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<QQmlObjectModelPrivate::Item, 4> store; + for (int i = 0; i < to - from; ++i) + store.append(children[from + n + i]); + for (int i = 0; i < n; ++i) + store.append(children[from + i]); + + for (int i = 0; i < store.count(); ++i) { + children[from + i] = store[i]; + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(from + i).item); + attached->setIndex(from + i); + } + + QQmlChangeSet changeSet; + changeSet.move(from, to, n, ++moveId); + emit q->modelUpdated(changeSet, false); + emit q->childrenChanged(); + } + + void remove(int index, int n) { + Q_Q(QQmlObjectModel); + for (int i = index; i < index + n; ++i) { + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); + attached->setIndex(-1); + } + children.erase(children.begin() + index, children.begin() + index + n); + for (int i = index; i < children.count(); ++i) { + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); + attached->setIndex(i); + } + QQmlChangeSet changeSet; + changeSet.remove(index, n); + emit q->modelUpdated(changeSet, false); + emit q->countChanged(); + emit q->childrenChanged(); + } + + void clear() { + Q_Q(QQmlObjectModel); + for (const Item &child : qAsConst(children)) + emit q->destroyingItem(child.item); + remove(0, children.count()); + } + + int indexOf(QObject *item) const { + for (int i = 0; i < children.count(); ++i) + if (children.at(i).item == item) + return i; + return -1; + } + + uint moveId; + QList<Item> children; +}; + + +/*! + \qmltype ObjectModel + \instantiates QQmlObjectModel + \inqmlmodule QtQml.Models + \ingroup qtquick-models + \brief Defines a set of items to be used as a model. + + An ObjectModel contains the visual items to be used in a view. + When an 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 + import QtQml.Models 2.1 + + 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 objectmodel.png + + \sa {Qt Quick Examples - Views} +*/ + +QQmlObjectModel::QQmlObjectModel(QObject *parent) + : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent) +{ +} + +/*! + \qmlattachedproperty int QtQml.Models::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<QObject> QQmlObjectModel::children() +{ + Q_D(QQmlObjectModel); + return QQmlListProperty<QObject>(this, + d, + d->children_append, + d->children_count, + d->children_at, + d->children_clear); +} + +/*! + \qmlproperty int QtQml.Models::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, QQmlIncubator::IncubationMode) +{ + 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 nullptr; +} + +QVariant QQmlObjectModel::variantValue(int index, const QString &role) +{ + Q_D(QQmlObjectModel); + if (index < 0 || index >= d->children.count()) + return QString(); + return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(role); +} + +QQmlIncubator::Status QQmlObjectModel::incubationStatus(int) +{ + return QQmlIncubator::Ready; +} + +int QQmlObjectModel::indexOf(QObject *item, QObject *) const +{ + Q_D(const QQmlObjectModel); + return d->indexOf(item); +} + +QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj) +{ + return QQmlObjectModelAttached::properties(obj); +} + +/*! + \qmlmethod object QtQml.Models::ObjectModel::get(int index) + \since 5.6 + + Returns the item at \a index in the model. This allows the item + to be accessed or modified from JavaScript: + + \code + Component.onCompleted: { + objectModel.append(objectComponent.createObject()) + console.log(objectModel.get(0).objectName); + objectModel.get(0).objectName = "first"; + } + \endcode + + The \a index must be an element in the list. + + \sa append() +*/ +QObject *QQmlObjectModel::get(int index) const +{ + Q_D(const QQmlObjectModel); + if (index < 0 || index >= d->children.count()) + return nullptr; + return d->children.at(index).item; +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::append(object item) + \since 5.6 + + Appends a new \a item to the end of the model. + + \code + objectModel.append(objectComponent.createObject()) + \endcode + + \sa insert(), remove() +*/ +void QQmlObjectModel::append(QObject *object) +{ + Q_D(QQmlObjectModel); + d->insert(count(), object); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::insert(int index, object item) + \since 5.6 + + Inserts a new \a item to the model at position \a index. + + \code + objectModel.insert(2, objectComponent.createObject()) + \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 append(), remove() +*/ +void QQmlObjectModel::insert(int index, QObject *object) +{ + Q_D(QQmlObjectModel); + if (index < 0 || index > count()) { + qmlWarning(this) << tr("insert: index %1 out of range").arg(index); + return; + } + d->insert(index, object); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::move(int from, int to, int n = 1) + \since 5.6 + + Moves \e 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 model: + + \code + objectModel.move(0, objectModel.count - 3, 3) + \endcode + + \sa append() +*/ +void QQmlObjectModel::move(int from, int to, int n) +{ + Q_D(QQmlObjectModel); + if (n <= 0 || from == to) + return; + if (from < 0 || to < 0 || from + n > count() || to + n > count()) { + qmlWarning(this) << tr("move: out of range"); + return; + } + d->move(from, to, n); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::remove(int index, int n = 1) + \since 5.6 + + Removes \e n items at \a index from the model. + + \sa clear() +*/ +void QQmlObjectModel::remove(int index, int n) +{ + Q_D(QQmlObjectModel); + if (index < 0 || n <= 0 || index + n > count()) { + qmlWarning(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+n).arg(count()); + return; + } + d->remove(index, n); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::clear() + \since 5.6 + + Clears all items from the model. + + \sa append(), remove() +*/ +void QQmlObjectModel::clear() +{ + Q_D(QQmlObjectModel); + d->clear(); +} + +QT_END_NAMESPACE + +#include "moc_qqmlobjectmodel_p.cpp" |