diff options
Diffstat (limited to 'src/declarative/fx/qfxvisualitemmodel.cpp')
-rw-r--r-- | src/declarative/fx/qfxvisualitemmodel.cpp | 690 |
1 files changed, 690 insertions, 0 deletions
diff --git a/src/declarative/fx/qfxvisualitemmodel.cpp b/src/declarative/fx/qfxvisualitemmodel.cpp new file mode 100644 index 0000000..145e750 --- /dev/null +++ b/src/declarative/fx/qfxvisualitemmodel.cpp @@ -0,0 +1,690 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlistmodelinterface.h" +#include "qfxitem.h" +#include <qmlcontext.h> +#include <qmlexpression.h> +#include "qmlpackage.h" +#include "qhash.h" +#include "qlist.h" +#include "private/qobject_p.h" +#include "qmlopenmetaobject.h" +#include "qmllistaccessor.h" +#include "qfxvisualitemmodel.h" + + +QT_BEGIN_NAMESPACE +QML_DECLARE_TYPE(QListModelInterface); + +class QFxVisualItemModelParts; +class QFxVisualItemModelData; +class QFxVisualItemModelPrivate : public QObjectPrivate +{ +public: + QFxVisualItemModelPrivate(QmlContext *); + + QListModelInterface *m_listModelInterface; + QAbstractItemModel *m_abstractItemModel; + QFxVisualItemModel *m_visualItemModel; + QString m_part; + + QmlComponent *m_delegate; + QmlContext *m_context; + QList<int> m_roles; + QHash<int,QString> m_roleNames; + + QHash<int, QObject *> m_cache; + QHash<QObject *, QmlPackage*> m_packaged; + QHash<QmlPackage*, int> m_packageRef; + + QFxVisualItemModelParts *m_parts; + friend class QFxVisualItemParts; + + QFxVisualItemModelData *data(QObject *item); + + QVariant m_modelVariant; + QmlListAccessor *m_modelList; + + int modelCount() const { + if(m_visualItemModel) + return m_visualItemModel->count(); + if(m_listModelInterface) + return m_listModelInterface->count(); + if(m_abstractItemModel) + return m_abstractItemModel->rowCount(); + if(m_modelList) + return m_modelList->count(); + return 0; + } +}; + +class QFxVisualItemModelDataMetaObject : public QmlOpenMetaObject +{ +public: + QFxVisualItemModelDataMetaObject(QObject *parent) + : QmlOpenMetaObject(parent) {} + + virtual QVariant propertyCreated(int, QMetaPropertyBuilder &); + virtual int createProperty(const char *, const char *); + +private: + friend class QFxVisualItemModelData; + QList<int> roles; +}; + +class QFxVisualItemModelData : public QObject +{ +Q_OBJECT +public: + QFxVisualItemModelData(int index, QFxVisualItemModelPrivate *model); + + Q_PROPERTY(int index READ index NOTIFY indexChanged); + int index() const; + void setIndex(int index); + + int count() const; + int role(int) const; + void setValue(int, const QVariant &); + +Q_SIGNALS: + void indexChanged(); + +private: + friend class QFxVisualItemModelDataMetaObject; + int m_index; + QFxVisualItemModelPrivate *m_model; + QFxVisualItemModelDataMetaObject *m_meta; +}; + +int QFxVisualItemModelData::count() const +{ + return m_meta->count(); +} + +int QFxVisualItemModelData::role(int id) const +{ + Q_ASSERT(id >= 0 && id < count()); + return m_meta->roles.at(id); +} + +void QFxVisualItemModelData::setValue(int id, const QVariant &val) +{ + m_meta->setValue(id, val); +} + +int QFxVisualItemModelDataMetaObject::createProperty(const char *name, const char *type) +{ + QFxVisualItemModelData *data = + static_cast<QFxVisualItemModelData *>(object()); + + if ((!data->m_model->m_listModelInterface || !data->m_model->m_abstractItemModel) + && data->m_model->m_modelList) { + if (!qstrcmp(name, "modelData")) + return QmlOpenMetaObject::createProperty(name, type); + } else { + const QLatin1String sname(name); + for(QHash<int, QString>::ConstIterator iter = data->m_model->m_roleNames.begin(); + iter != data->m_model->m_roleNames.end(); ++iter) { + + if(*iter == sname) + return QmlOpenMetaObject::createProperty(name, type); + } + } + return -1; +} + +QVariant +QFxVisualItemModelDataMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) +{ + prop.setWritable(false); + + QFxVisualItemModelData *data = + static_cast<QFxVisualItemModelData *>(object()); + QString name = QLatin1String(prop.name()); + if ((!data->m_model->m_listModelInterface || !data->m_model->m_abstractItemModel) + && data->m_model->m_modelList) { + return data->m_model->m_modelList->at(data->m_index); + } else if (data->m_model->m_listModelInterface) { + for(QHash<int, QString>::ConstIterator iter = data->m_model->m_roleNames.begin(); + iter != data->m_model->m_roleNames.end(); ++iter) { + + if(*iter == name) { + roles.append(iter.key()); + QHash<int,QVariant> values = data->m_model->m_listModelInterface->data(data->m_index, QList<int>() << iter.key()); + if (values.isEmpty()) + return QVariant(); + else + return *values.begin(); + } + } + } else if (data->m_model->m_abstractItemModel) { + for(QHash<int, QString>::ConstIterator iter = data->m_model->m_roleNames.begin(); + iter != data->m_model->m_roleNames.end(); ++iter) { + + if(*iter == name) { + roles.append(iter.key()); + QModelIndex index = data->m_model->m_abstractItemModel->index(data->m_index, 0); + return data->m_model->m_abstractItemModel->data(index, iter.key()); + } + } + } + Q_ASSERT(!"Can never be reached"); + return QVariant(); +} + +QFxVisualItemModelData::QFxVisualItemModelData(int index, + QFxVisualItemModelPrivate *model) +: m_index(index), m_model(model), + m_meta(new QFxVisualItemModelDataMetaObject(this)) +{ +} + +int QFxVisualItemModelData::index() const +{ + return m_index; +} + +// This is internal only - it should not be set from qml +void QFxVisualItemModelData::setIndex(int index) +{ + m_index = index; + emit indexChanged(); +} + +class QFxVisualItemModelPartsMetaObject : public QmlOpenMetaObject +{ +public: + QFxVisualItemModelPartsMetaObject(QObject *parent) + : QmlOpenMetaObject(parent) {} + + virtual QVariant propertyCreated(int, QMetaPropertyBuilder &); +}; + +class QFxVisualItemModelParts : public QObject +{ +Q_OBJECT +public: + QFxVisualItemModelParts(QFxVisualItemModel *parent); + +private: + friend class QFxVisualItemModelPartsMetaObject; + QFxVisualItemModel *model; +}; + +QVariant +QFxVisualItemModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) +{ + prop.setWritable(false); + + QFxVisualItemModel *m = new QFxVisualItemModel; + m->setParent(object()); + m->setPart(QLatin1String(prop.name())); + m->setModel(QVariant::fromValue(static_cast<QFxVisualItemModelParts *>(object())->model)); + + QVariant var = QVariant::fromValue((QObject *)m); + return var; +} + +QFxVisualItemModelParts::QFxVisualItemModelParts(QFxVisualItemModel *parent) +: QObject(parent), model(parent) +{ + new QFxVisualItemModelPartsMetaObject(this); +} + +QFxVisualItemModelPrivate::QFxVisualItemModelPrivate(QmlContext *ctxt) +: m_listModelInterface(0), m_abstractItemModel(0), m_visualItemModel(0), m_delegate(0) +, m_context(ctxt), m_parts(0), m_modelList(0) +{ +} + +QFxVisualItemModelData *QFxVisualItemModelPrivate::data(QObject *item) +{ + QList<QFxVisualItemModelData *> dataList = + item->findChildren<QFxVisualItemModelData *>(); + Q_ASSERT(dataList.count() == 1); + return dataList.first(); +} + +QFxVisualItemModel::QFxVisualItemModel() +: QObject(*(new QFxVisualItemModelPrivate(QmlContext::activeContext()))) +{ +} + +QFxVisualItemModel::QFxVisualItemModel(QmlContext *ctxt) +: QObject(*(new QFxVisualItemModelPrivate(ctxt))) +{ +} + +QFxVisualItemModel::~QFxVisualItemModel() +{ + Q_D(QFxVisualItemModel); + if(d->m_modelList) + delete d->m_modelList; +} + +QVariant QFxVisualItemModel::model() const +{ + Q_D(const QFxVisualItemModel); + return d->m_modelVariant; +} + +void QFxVisualItemModel::setModel(const QVariant &model) +{ + Q_D(QFxVisualItemModel); + d->m_modelVariant = model; + if(d->m_listModelInterface) { + // Assume caller has released all items. + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsChanged(int,int,QList<int>)), + this, SLOT(_q_itemsChanged(int,int,QList<int>))); + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsInserted(int,int)), + this, SLOT(_q_itemsInserted(int,int))); + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsRemoved(int,int)), + this, SLOT(_q_itemsRemoved(int,int))); + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsMoved(int,int,int)), + this, SLOT(_q_itemsMoved(int,int,int))); + d->m_listModelInterface = 0; + } else if (d->m_abstractItemModel) { + QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsInserted(const QModelIndex &,int,int)), + this, SLOT(_q_rowsInserted(const QModelIndex &,int,int))); + QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsRemoved(const QModelIndex &,int,int)), + this, SLOT(_q_rowsRemoved(const QModelIndex &,int,int))); + QObject::disconnect(d->m_abstractItemModel, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)), + this, SLOT(_q_dataChanged(const QModelIndex&,const QModelIndex&))); + } else if(d->m_visualItemModel) { + QObject::disconnect(d->m_visualItemModel, SIGNAL(itemsInserted(int,int)), + this, SIGNAL(itemsInserted(int,int))); + QObject::disconnect(d->m_visualItemModel, SIGNAL(itemsRemoved(int,int)), + this, SIGNAL(itemsRemoved(int,int))); + QObject::disconnect(d->m_visualItemModel, SIGNAL(itemsMoved(int,int,int)), + this, SIGNAL(itemsMoved(int,int,int))); + d->m_visualItemModel = 0; + } + + QObject *object = qvariant_cast<QObject *>(model); + if (object && (d->m_listModelInterface = qobject_cast<QListModelInterface *>(object))) { + d->m_roles.clear(); + d->m_roleNames.clear(); + if(d->m_listModelInterface) { + d->m_roles = d->m_listModelInterface->roles(); + for(int ii = 0; ii < d->m_roles.count(); ++ii) + d->m_roleNames.insert(d->m_roles.at(ii), + d->m_listModelInterface->toString(d->m_roles.at(ii))); + } + + QObject::connect(d->m_listModelInterface, SIGNAL(itemsChanged(int,int,QList<int>)), + this, SLOT(_q_itemsChanged(int,int,QList<int>))); + QObject::connect(d->m_listModelInterface, SIGNAL(itemsInserted(int,int)), + this, SLOT(_q_itemsInserted(int,int))); + QObject::connect(d->m_listModelInterface, SIGNAL(itemsRemoved(int,int)), + this, SLOT(_q_itemsRemoved(int,int))); + QObject::connect(d->m_listModelInterface, SIGNAL(itemsMoved(int,int,int)), + this, SLOT(_q_itemsMoved(int,int,int))); + + if(d->m_delegate && d->m_listModelInterface->count()) + emit itemsInserted(0, d->m_listModelInterface->count()); + return; + } else if (object && (d->m_abstractItemModel = qobject_cast<QAbstractItemModel *>(object))) { + d->m_roles.clear(); + d->m_roleNames.clear(); + for (QHash<int,QByteArray>::const_iterator it = d->m_abstractItemModel->roleNames().begin(); + it != d->m_abstractItemModel->roleNames().end(); ++it) { + d->m_roles.append(it.key()); + d->m_roleNames.insert(it.key(), QLatin1String(*it)); + } + QObject::connect(d->m_abstractItemModel, SIGNAL(rowsInserted(const QModelIndex &,int,int)), + this, SLOT(_q_rowsInserted(const QModelIndex &,int,int))); + QObject::connect(d->m_abstractItemModel, SIGNAL(rowsRemoved(const QModelIndex &,int,int)), + this, SLOT(_q_rowsRemoved(const QModelIndex &,int,int))); + QObject::connect(d->m_abstractItemModel, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)), + this, SLOT(_q_dataChanged(const QModelIndex&,const QModelIndex&))); + return; + } + if ((d->m_visualItemModel = qvariant_cast<QFxVisualItemModel *>(model))) { + QObject::connect(d->m_visualItemModel, SIGNAL(itemsInserted(int,int)), + this, SIGNAL(itemsInserted(int,int))); + QObject::connect(d->m_visualItemModel, SIGNAL(itemsRemoved(int,int)), + this, SIGNAL(itemsRemoved(int,int))); + QObject::connect(d->m_visualItemModel, SIGNAL(itemsMoved(int,int,int)), + this, SIGNAL(itemsMoved(int,int,int))); + return; + } + if (!d->m_modelList) + d->m_modelList = new QmlListAccessor; + d->m_modelList->setList(model); + if(d->m_delegate && d->modelCount()) + emit itemsInserted(0, d->modelCount()); +} + +QmlComponent *QFxVisualItemModel::delegate() const +{ + Q_D(const QFxVisualItemModel); + return d->m_delegate; +} + +void QFxVisualItemModel::setDelegate(QmlComponent *delegate) +{ + Q_D(QFxVisualItemModel); + d->m_delegate = delegate; + + if(d->modelCount()) + emit itemsInserted(0, d->modelCount()); +} + +QString QFxVisualItemModel::part() const +{ + Q_D(const QFxVisualItemModel); + return d->m_part; +} + +void QFxVisualItemModel::setPart(const QString &part) +{ + Q_D(QFxVisualItemModel); + d->m_part = part; +} + +int QFxVisualItemModel::count() const +{ + Q_D(const QFxVisualItemModel); + return d->modelCount(); +} + +QFxItem *QFxVisualItemModel::item(int index, bool complete) +{ + Q_D(QFxVisualItemModel); + if(d->m_visualItemModel) + return d->m_visualItemModel->item(index, d->m_part.toLatin1(), complete); + return item(index, QByteArray(), complete); +} + +void QFxVisualItemModel::release(QFxItem *item) +{ + Q_D(QFxVisualItemModel); + if (d->m_visualItemModel) { + d->m_visualItemModel->release(item); + return; + } + item->setItemParent(0); + QObject *obj = item; + + if (QmlPackage *package = d->m_packaged.value(item)) { + static_cast<QObject*>(item)->setParent(package); + d->m_packaged.remove(item); + //XXX Inefficient + for (QHash<QObject *, QmlPackage *>::Iterator iter = d->m_packaged.begin(); + iter != d->m_packaged.end(); ++iter) { + if (*iter == package) + return; + } + obj = package; // fall through and delete + } + + //XXX Inefficient + for (QHash<int, QObject *>::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ++iter) { + if (*iter == obj) { + delete obj; + d->m_cache.erase(iter); + return; + } + } +} + +QObject *QFxVisualItemModel::parts() +{ + Q_D(QFxVisualItemModel); + if(!d->m_parts) + d->m_parts = new QFxVisualItemModelParts(this); + return d->m_parts; +} + +QFxItem *QFxVisualItemModel::item(int index, const QByteArray &viewId, bool complete) +{ + Q_D(QFxVisualItemModel); + if(d->m_visualItemModel) + return d->m_visualItemModel->item(index, viewId, complete); + + if(d->modelCount() <= 0 || !d->m_delegate) + return 0; + + QObject *nobj = 0; + if(d->m_cache.contains(index)) { + nobj = d->m_cache[index]; + } else { + QmlContext *ctxt = new QmlContext(d->m_context); + QFxVisualItemModelData *data = new QFxVisualItemModelData(index, d); + ctxt->setContextProperty(QLatin1String("model"), data); + ctxt->addDefaultObject(data); + nobj = d->m_delegate->beginCreate(ctxt); + if (complete) + d->m_delegate->completeCreate(); + ctxt->setParent(nobj); + data->setParent(nobj); + + d->m_cache.insert(index, nobj); + } + + QFxItem *item = qobject_cast<QFxItem *>(nobj); + if (!item) { + QmlPackage *package = qobject_cast<QmlPackage *>(nobj); + if(package) { + QObject *o = package->part(QLatin1String(viewId)); + item = qobject_cast<QFxItem *>(o); + d->m_packaged[o] = package; + } + } + + return item; +} + +void QFxVisualItemModel::completeItem() +{ + Q_D(QFxVisualItemModel); + if(d->m_visualItemModel) { + d->m_visualItemModel->completeItem(); + return; + } + + d->m_delegate->completeCreate(); +} + +QVariant QFxVisualItemModel::evaluate(int index, const QString &expression, QObject *objectContext) +{ + Q_D(QFxVisualItemModel); + if (d->m_visualItemModel) + return d->m_visualItemModel->evaluate(index, expression, objectContext); + + if((!d->m_listModelInterface && !d->m_abstractItemModel) || !d->m_delegate) + return QVariant(); + + QVariant value; + if(d->m_cache.contains(index)) { + QObject *nobj = d->m_cache[index]; + QFxItem *item = qobject_cast<QFxItem *>(nobj); + if (item) { + QmlExpression e(item->itemContext(), expression, objectContext); + e.setTrackChange(false); + value = e.value(); + } + } else { + QmlContext *ctxt = new QmlContext(d->m_context); + QFxVisualItemModelData *data = new QFxVisualItemModelData(index, d); + ctxt->addDefaultObject(data); + QmlExpression e(ctxt, expression, objectContext); + e.setTrackChange(false); + value = e.value(); + delete data; + delete ctxt; + } + + return value; +} + +void QFxVisualItemModel::_q_itemsChanged(int index, int count, + const QList<int> &roles) +{ + Q_D(QFxVisualItemModel); + // XXX - highly inefficient + for(int ii = index; ii < index + count; ++ii) { + + if(d->m_cache.contains(ii)) { + + QObject *item = d->m_cache[ii]; + QFxVisualItemModelData *data = d->data(item); + + for(int prop = 0; prop < data->count(); ++prop) { + + int role = data->role(prop); + if(roles.contains(role)) { + if (d->m_listModelInterface) { + data->setValue(prop, *d->m_listModelInterface->data(ii, QList<int>() << role).begin()); + } else if (d->m_abstractItemModel) { + QModelIndex index = d->m_abstractItemModel->index(ii, 0); + data->setValue(prop, d->m_abstractItemModel->data(index, role)); + } + } + } + } + + } +} + +void QFxVisualItemModel::_q_itemsInserted(int index, int count) +{ + Q_D(QFxVisualItemModel); + // XXX - highly inefficient + QHash<int, QObject *> items; + for(QHash<int, QObject *>::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ) { + + if(iter.key() >= index) { + QObject *item = *iter; + int index = iter.key() + count; + iter = d->m_cache.erase(iter); + + items.insert(index, item); + + QFxVisualItemModelData *data = d->data(item); + data->setIndex(index); + } else { + ++iter; + } + } + d->m_cache.unite(items); + + emit itemsInserted(index, count); +} + +void QFxVisualItemModel::_q_itemsRemoved(int index, int count) +{ + Q_D(QFxVisualItemModel); + // XXX - highly inefficient + QHash<int, QObject *> items; + for(QHash<int, QObject *>::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ) { + if(iter.key() >= index && iter.key() < index + count) { + QObject *item = *iter; + iter = d->m_cache.erase(iter); + items.insertMulti(-1, item); //XXX perhaps better to maintain separately + QFxVisualItemModelData *data = d->data(item); + data->setIndex(-1); + } else if(iter.key() >= index + count) { + QObject *item = *iter; + int index = iter.key() - count; + iter = d->m_cache.erase(iter); + items.insert(index, item); + QFxVisualItemModelData *data = d->data(item); + data->setIndex(index); + } else { + ++iter; + } + } + + d->m_cache.unite(items); + emit itemsRemoved(index, count); +} + +void QFxVisualItemModel::_q_itemsMoved(int from, int to, int count) +{ + Q_D(QFxVisualItemModel); + // XXX - highly inefficient + QHash<int, QObject *> items; + for(QHash<int, QObject *>::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ) { + + if(iter.key() >= from && iter.key() < from + count) { + QObject *item = *iter; + int index = iter.key() - from + to; + iter = d->m_cache.erase(iter); + + items.insert(index, item); + + QFxVisualItemModelData *data = d->data(item); + data->setIndex(index); + } else { + ++iter; + } + } + d->m_cache.unite(items); + + emit itemsMoved(from, to, count); +} + +void QFxVisualItemModel::_q_rowsInserted(const QModelIndex &, int begin, int end) +{ + _q_itemsInserted(begin, end - begin + 1); +} + +void QFxVisualItemModel::_q_rowsRemoved(const QModelIndex &, int begin, int end) +{ + _q_itemsRemoved(begin, end - begin + 1); +} + +void QFxVisualItemModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end) +{ + Q_D(QFxVisualItemModel); + _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, d->m_roles); +} + +QML_DEFINE_TYPE(QFxVisualItemModel,VisualModel); + +QT_END_NAMESPACE +#include "qfxvisualitemmodel.moc" |