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

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 "modeladapter.h"
0019 
0020 // Qt
0021 #include <QtCore/QVariant>
0022 #include <QtCore/QAbstractItemModel>
0023 #include <QtCore/QSharedPointer>
0024 #include <QtCore/QDebug>
0025 
0026 // KQuickItemViews
0027 #include "viewport.h"
0028 #include "viewbase.h"
0029 #include "selectionadapter.h"
0030 #include "contextadapterfactory.h"
0031 #include "private/selectionadapter_p.h"
0032 #include "private/statetracker/viewitem_p.h"
0033 #include "private/statetracker/content_p.h"
0034 #include "private/statetracker/model_p.h"
0035 #include "private/viewport_p.h"
0036 #include "abstractitemadapter.h"
0037 #include "proxies/sizehintproxymodel.h"
0038 
0039 using QSharedItemModel = QSharedPointer<QAbstractItemModel>;
0040 
0041 class ModelAdapterPrivate : public QObject
0042 {
0043     Q_OBJECT
0044 public:
0045     enum class Mode {
0046         NONE,
0047         SMART_POINTER,
0048         RAW
0049     } m_Mode {Mode::NONE};
0050 
0051     QSharedItemModel        m_pModelPtr           {       };
0052     QAbstractItemModel     *m_pRawModel           {nullptr};
0053     QQmlComponent          *m_pDelegate           {nullptr};
0054     Viewport               *m_pViewport           {nullptr};
0055     SelectionAdapter       *m_pSelectionManager   {nullptr};
0056     ViewBase               *m_pView               {nullptr};
0057     ContextAdapterFactory  *m_pRoleContextFactory {nullptr};
0058 
0059     bool m_Collapsable {true };
0060     bool m_AutoExpand  {false};
0061     int  m_MaxDepth    { -1  };
0062     int  m_CacheBuffer { 10  };
0063     int  m_PoolSize    { 10  };
0064 
0065     int m_ExpandedCount { 999 }; //TODO
0066 
0067     ModelAdapter::RecyclingMode m_RecyclingMode {
0068         ModelAdapter::RecyclingMode::NoRecycling
0069     };
0070 
0071     // Helpers
0072     void setModelCommon(QAbstractItemModel* m, QAbstractItemModel* old);
0073 
0074     ModelAdapter *q_ptr;
0075 
0076 public Q_SLOTS:
0077     void slotContentChanged();
0078     void slotDestroyed();
0079 };
0080 
0081 ModelAdapter::ModelAdapter(ViewBase* parent) : QObject(parent),
0082     d_ptr(new ModelAdapterPrivate())
0083 {
0084     Q_ASSERT(parent);
0085     d_ptr->q_ptr = this;
0086     d_ptr->m_pView = parent;
0087     d_ptr->m_pSelectionManager = new SelectionAdapter(this);
0088 
0089     selectionAdapter()->s_ptr->setView(parent);
0090     contextAdapterFactory()->addContextExtension(selectionAdapter()->contextExtension());
0091 
0092     selectionAdapter()->s_ptr->setViewport(d_ptr->m_pViewport = new Viewport(this));
0093 
0094     connect(d_ptr->m_pViewport, &Viewport::contentChanged,
0095         d_ptr, &ModelAdapterPrivate::slotContentChanged);
0096 }
0097 
0098 ModelAdapter::~ModelAdapter()
0099 {
0100     delete d_ptr;
0101 }
0102 
0103 QVariant ModelAdapter::model() const
0104 {
0105     switch(d_ptr->m_Mode) {
0106         case ModelAdapterPrivate::Mode::SMART_POINTER:
0107             return QVariant::fromValue(d_ptr->m_pModelPtr);
0108         case ModelAdapterPrivate::Mode::RAW:
0109             return QVariant::fromValue(d_ptr->m_pRawModel);
0110         case ModelAdapterPrivate::Mode::NONE:
0111         default:
0112             return {};
0113     }
0114 }
0115 
0116 void ModelAdapterPrivate::setModelCommon(QAbstractItemModel* m, QAbstractItemModel* old)
0117 {
0118     if (old)
0119         disconnect(old, &QObject::destroyed, this, &ModelAdapterPrivate::slotDestroyed);
0120 
0121     q_ptr->selectionAdapter()->s_ptr->setModel(m);
0122 
0123     if (auto f = m_pRoleContextFactory)
0124         f->setModel(m);
0125 
0126     if (m)
0127         connect(m, &QObject::destroyed, this, &ModelAdapterPrivate::slotDestroyed);
0128 }
0129 
0130 void ModelAdapter::setModel(const QVariant& var)
0131 {
0132     QSharedItemModel oldPtr;
0133     QAbstractItemModel* oldM = oldPtr ? oldPtr.data() : d_ptr->m_pRawModel;
0134 
0135     if (d_ptr->m_pModelPtr)
0136         oldPtr = d_ptr->m_pModelPtr;
0137 
0138     if (var.isNull()) {
0139         if ((!d_ptr->m_pRawModel) && (!d_ptr->m_pModelPtr))
0140             return;
0141 
0142         emit modelAboutToChange(nullptr, oldM);
0143         d_ptr->m_Mode = ModelAdapterPrivate::Mode::NONE;
0144         d_ptr->m_pRawModel = nullptr;
0145         d_ptr->m_pModelPtr = nullptr;
0146         d_ptr->setModelCommon(nullptr, oldM);
0147         emit modelChanged(nullptr, oldM);
0148     }
0149     else if (auto m = var.value<QSharedItemModel >()) {
0150         if (m == d_ptr->m_pModelPtr)
0151             return;
0152 
0153         // Use .data() is "safe", this call holds a reference, but the user
0154         // users should "not" store it. Obviously they all will, but at least
0155         // they have to watch the signal and replace it in time. If they don't,
0156         // then it may crash but the bug is on the consumer side.
0157         emit modelAboutToChange(m.data(), oldM);
0158         d_ptr->m_Mode = ModelAdapterPrivate::Mode::SMART_POINTER;
0159         d_ptr->m_pRawModel = nullptr;
0160         d_ptr->m_pModelPtr = m;
0161         d_ptr->setModelCommon(m.data(), oldM);
0162         emit modelChanged(m.data(), oldM);
0163 
0164     }
0165     else if (auto m = var.value<QAbstractItemModel*>()) {
0166         if (m == d_ptr->m_pRawModel)
0167             return;
0168 
0169         emit modelAboutToChange(m, oldM);
0170         d_ptr->m_Mode = ModelAdapterPrivate::Mode::RAW;
0171         d_ptr->m_pRawModel = m;
0172         d_ptr->m_pModelPtr = nullptr;
0173         d_ptr->setModelCommon(m, oldPtr ? oldPtr.data() : oldM);
0174         emit modelChanged(m, oldM);
0175     }
0176     else {
0177         Q_ASSERT(false);
0178         qWarning() << "Cannot convert" << var << "to a model";
0179         setModel({});
0180     }
0181 }
0182 
0183 void ModelAdapter::setDelegate(QQmlComponent* delegate)
0184 {
0185     if (delegate == d_ptr->m_pDelegate)
0186         return;
0187 
0188     d_ptr->m_pDelegate = delegate;
0189     emit delegateChanged(delegate);
0190 
0191     if (d_ptr->m_Mode != ModelAdapterPrivate::Mode::NONE && d_ptr->m_pViewport) {
0192         //FIXME handle this properly
0193         auto content = d_ptr->m_pViewport->s_ptr->m_pReflector;
0194 
0195         content->modelTracker()
0196             << StateTracker::Model::Action::POPULATE
0197             << StateTracker::Model::Action::ENABLE;
0198     }
0199 }
0200 
0201 QQmlComponent* ModelAdapter::delegate() const
0202 {
0203     return d_ptr->m_pDelegate;
0204 }
0205 
0206 bool ModelAdapter::isEmpty() const
0207 {
0208     switch(d_ptr->m_Mode) {
0209         case ModelAdapterPrivate::Mode::SMART_POINTER:
0210             return d_ptr->m_pModelPtr->rowCount() == 0;
0211         case ModelAdapterPrivate::Mode::RAW:
0212             return d_ptr->m_pRawModel->rowCount() == 0;
0213         case ModelAdapterPrivate::Mode::NONE:
0214         default:
0215             return true;
0216     }
0217 }
0218 
0219 bool ModelAdapter::isCollapsable() const
0220 {
0221     return d_ptr->m_Collapsable;
0222 }
0223 
0224 void ModelAdapter::setCollapsable(bool value)
0225 {
0226     d_ptr->m_Collapsable = value;
0227 }
0228 
0229 bool ModelAdapter::isAutoExpand() const
0230 {
0231     return d_ptr->m_AutoExpand;
0232 }
0233 
0234 void ModelAdapter::setAutoExpand(bool value)
0235 {
0236     d_ptr->m_AutoExpand = value;
0237 }
0238 
0239 int ModelAdapter::maxDepth() const
0240 {
0241     return d_ptr->m_MaxDepth;
0242 }
0243 
0244 void ModelAdapter::setMaxDepth(int depth)
0245 {
0246     d_ptr->m_MaxDepth = depth;
0247 }
0248 
0249 int ModelAdapter::cacheBuffer() const
0250 {
0251     return d_ptr->m_CacheBuffer;
0252 }
0253 
0254 void ModelAdapter::setCacheBuffer(int value)
0255 {
0256     d_ptr->m_CacheBuffer = std::min(1, value);
0257 }
0258 
0259 int ModelAdapter::poolSize() const
0260 {
0261     return d_ptr->m_PoolSize;
0262 }
0263 
0264 void ModelAdapter::setPoolSize(int value)
0265 {
0266     d_ptr->m_PoolSize = value;
0267 }
0268 
0269 ModelAdapter::RecyclingMode ModelAdapter::recyclingMode() const
0270 {
0271     return d_ptr->m_RecyclingMode;
0272 }
0273 
0274 void ModelAdapter::setRecyclingMode(ModelAdapter::RecyclingMode mode)
0275 {
0276     d_ptr->m_RecyclingMode = mode;
0277 }
0278 
0279 void ModelAdapter::setSelectionAdapter(SelectionAdapter* v)
0280 {
0281     d_ptr->m_pSelectionManager = v;
0282 }
0283 
0284 SelectionAdapter* ModelAdapter::selectionAdapter() const
0285 {
0286     return d_ptr->m_pSelectionManager;
0287 }
0288 
0289 ContextAdapterFactory* ModelAdapter::contextAdapterFactory() const
0290 {
0291     if (!d_ptr->m_pRoleContextFactory) {
0292         d_ptr->m_pRoleContextFactory = new ContextAdapterFactory();
0293 
0294         if (auto m = rawModel())
0295             d_ptr->m_pRoleContextFactory->setModel(m);
0296     }
0297 
0298     return d_ptr->m_pRoleContextFactory;
0299 }
0300 
0301 QVector<Viewport*> ModelAdapter::viewports() const
0302 {
0303     return {d_ptr->m_pViewport};
0304 }
0305 
0306 void ModelAdapterPrivate::slotContentChanged()
0307 {
0308     emit q_ptr->contentChanged();
0309 }
0310 
0311 void ModelAdapterPrivate::slotDestroyed()
0312 {
0313     q_ptr->setModel(
0314         QVariant::fromValue((QAbstractItemModel*)nullptr)
0315     );
0316 }
0317 
0318 QAbstractItemModel *ModelAdapter::rawModel() const
0319 {
0320     switch(d_ptr->m_Mode) {
0321         case ModelAdapterPrivate::Mode::SMART_POINTER:
0322             return d_ptr->m_pModelPtr.data();
0323         case ModelAdapterPrivate::Mode::RAW:
0324             return d_ptr->m_pRawModel;
0325         case ModelAdapterPrivate::Mode::NONE:
0326         default:
0327             return nullptr;
0328     }
0329 }
0330 
0331 ViewBase *ModelAdapter::view() const
0332 {
0333     return d_ptr->m_pView;
0334 }
0335 
0336 bool ModelAdapter::isCollapsed() const
0337 {
0338     // Having no expanded elements allows some optimizations to kick-in such
0339     // as total size using multiplications when the size is uniform.
0340     return d_ptr->m_ExpandedCount == 0;
0341 }
0342 
0343 #include <modeladapter.moc>