File indexing completed on 2024-04-28 04:41:52

0001 /***************************************************************************
0002  *   Copyright (C) 2018 by Emmanuel Lepage Vallee                          *
0003  *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@kde.org>             *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 3 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0017  **************************************************************************/
0018 #include "indexview.h"
0019 
0020 // Qt
0021 #include <QQmlEngine>
0022 #include <QQmlContext>
0023 
0024 // KQuickItemViews
0025 #include <contextadapterfactory.h>
0026 #include <adapters/contextadapter.h>
0027 #include <extensions/contextextension.h>
0028 #include <qmodelindexwatcher.h>
0029 #include <qmodelindexbinder.h>
0030 
0031 /// Make QModelIndexBinder life easier
0032 class MIWContextExtension final : public ContextExtension
0033 {
0034 public:
0035     virtual ~MIWContextExtension() {}
0036     virtual QVector<QByteArray>& propertyNames() const override;
0037     virtual QVariant getProperty(AbstractItemAdapter* item, uint id, const QModelIndex& index) const override;
0038 
0039     IndexViewPrivate *d_ptr;
0040 };
0041 
0042 class IndexViewPrivate : public QObject
0043 {
0044     Q_OBJECT
0045 public:
0046     QQmlComponent         *m_pComponent {nullptr};
0047     ContextAdapter        *m_pCTX       {nullptr};
0048     ContextAdapterFactory *m_pFactory   {nullptr};
0049     QQuickItem            *m_pItem      {nullptr};
0050     MIWContextExtension   *m_pExt       {nullptr};
0051     QModelIndexWatcher    *m_pWatcher   {nullptr};
0052 
0053     // Helper
0054     void initDelegate();
0055     void initContext();
0056 
0057     IndexView *q_ptr;
0058 
0059 public Q_SLOTS:
0060     void slotDismiss();
0061     void slotDataChanged(const QVector<int> &roles);
0062 };
0063 
0064 IndexView::IndexView(QQuickItem *parent) : QQuickItem(parent),
0065     d_ptr(new IndexViewPrivate())
0066 {
0067     d_ptr->q_ptr = this;
0068     d_ptr->m_pWatcher = new QModelIndexWatcher(this);
0069     connect(d_ptr->m_pWatcher, &QModelIndexWatcher::removed,
0070         d_ptr, &IndexViewPrivate::slotDismiss);
0071     connect(d_ptr->m_pWatcher, &QModelIndexWatcher::dataChanged,
0072         d_ptr, &IndexViewPrivate::slotDataChanged);
0073 }
0074 
0075 IndexView::~IndexView()
0076 {
0077     if (d_ptr->m_pItem)
0078         delete d_ptr->m_pItem;
0079 
0080     if (d_ptr->m_pCTX)
0081         delete d_ptr->m_pCTX;
0082 
0083     if (d_ptr->m_pFactory)
0084         delete d_ptr->m_pFactory;
0085 
0086     if (d_ptr->m_pExt)
0087         delete d_ptr->m_pExt;
0088 
0089     delete d_ptr->m_pWatcher;
0090     delete d_ptr;
0091 }
0092 
0093 void IndexView::setDelegate(QQmlComponent* delegate)
0094 {
0095     if (delegate == d_ptr->m_pComponent)
0096         return;
0097 
0098     if (d_ptr->m_pItem)
0099         delete d_ptr->m_pItem;
0100 
0101     d_ptr->m_pComponent = delegate;
0102     emit delegateChanged(delegate);
0103 
0104     d_ptr->initDelegate();
0105 }
0106 
0107 QQmlComponent* IndexView::delegate() const
0108 {
0109     return d_ptr->m_pComponent;
0110 }
0111 
0112 QModelIndex IndexView::modelIndex() const
0113 {
0114     return d_ptr->m_pWatcher->modelIndex();
0115 }
0116 
0117 QAbstractItemModel *IndexView::model() const
0118 {
0119     return d_ptr->m_pWatcher->model();
0120 }
0121 
0122 void IndexView::setModelIndex(const QModelIndex &index)
0123 {
0124     if (index == modelIndex())
0125         return;
0126 
0127     // Disconnect old models
0128     if (model() && model() != index.model())
0129         d_ptr->slotDismiss();
0130 
0131 
0132     if (model() != index.model()) {
0133         if (!d_ptr->m_pFactory) {
0134             d_ptr->m_pExt        = new MIWContextExtension  ();
0135             d_ptr->m_pFactory    = new ContextAdapterFactory();
0136             d_ptr->m_pExt->d_ptr = d_ptr;
0137             d_ptr->m_pFactory->addContextExtension(d_ptr->m_pExt);
0138         }
0139 
0140         d_ptr->m_pFactory->setModel(const_cast<QAbstractItemModel*>(index.model()));
0141 
0142         if (d_ptr->m_pItem) {
0143             delete d_ptr->m_pItem;
0144             d_ptr->m_pItem = nullptr;
0145         }
0146 
0147         d_ptr->m_pCTX = nullptr;
0148     }
0149 
0150     d_ptr->m_pWatcher->setModelIndex(index);
0151 
0152     d_ptr->initContext();
0153 
0154     d_ptr->m_pCTX->setModelIndex(index);
0155 
0156     d_ptr->initDelegate();
0157     emit indexChanged();
0158 }
0159 
0160 void IndexViewPrivate::initContext()
0161 {
0162     if (m_pCTX)
0163         return;
0164 
0165     Q_ASSERT(m_pFactory);
0166 
0167     const auto ctx = QQmlEngine::contextForObject(q_ptr);
0168     m_pCTX = m_pFactory->createAdapter(ctx);
0169     Q_ASSERT(m_pCTX && m_pCTX->context()->parentContext() == ctx);
0170 }
0171 
0172 void IndexViewPrivate::slotDismiss()
0173 {
0174     if (m_pItem) {
0175         delete m_pItem;
0176         m_pItem = nullptr;
0177     }
0178 
0179     if (m_pCTX) {
0180         delete m_pCTX;
0181         m_pCTX = nullptr;
0182     }
0183 
0184     emit q_ptr->indexChanged();
0185 }
0186 
0187 void IndexViewPrivate::slotDataChanged(const QVector<int> &roles)
0188 {
0189     m_pCTX->updateRoles(roles);
0190 }
0191 
0192 void IndexViewPrivate::initDelegate()
0193 {
0194     if (m_pItem || (!m_pComponent) || !q_ptr->modelIndex().isValid())
0195         return;
0196 
0197     initContext();
0198 
0199     m_pItem = qobject_cast<QQuickItem *>(m_pComponent->create(m_pCTX->context()));
0200 
0201     // It will happen when the QML itself is invalid
0202     if (!m_pItem)
0203         return;
0204 
0205     const auto ctx = QQmlEngine::contextForObject(q_ptr);
0206     Q_ASSERT(ctx);
0207 
0208     ctx->engine()->setObjectOwnership(m_pItem, QQmlEngine::CppOwnership);
0209     m_pItem->setParentItem(q_ptr);
0210 }
0211 
0212 QVector<QByteArray>& MIWContextExtension::propertyNames() const
0213 {
0214     static QVector<QByteArray> ret { "_modelIndexWatcher", "_contextAdapter" };
0215     return ret;
0216 }
0217 
0218 QVariant MIWContextExtension::getProperty(AbstractItemAdapter* item, uint id, const QModelIndex& index) const
0219 {
0220     Q_UNUSED(item)
0221     Q_UNUSED(index)
0222     return id ? QVariant::fromValue(d_ptr->m_pCTX) : QVariant::fromValue(d_ptr->m_pWatcher);
0223 }
0224 
0225 #include <indexview.moc>