File indexing completed on 2024-04-14 04:36:55
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 "qmodelindexbinder.h" 0019 0020 // Qt 0021 #include <QQmlEngine> 0022 #include <QQmlContext> 0023 #include <QtCore/QTimer> 0024 0025 // KQuickItemViews 0026 #include "qmodelindexwatcher.h" 0027 #include <adapters/contextadapter.h> 0028 0029 class QModelIndexBinderPrivate : public QObject 0030 { 0031 Q_OBJECT 0032 public: 0033 enum class Mode { 0034 CONTAINER, 0035 ATTACHED, 0036 }; 0037 0038 QByteArray m_Role { }; 0039 QByteArray m_Prop { }; 0040 QObject *m_pContent {nullptr}; 0041 int m_Delay { 0 }; 0042 QTimer *m_pTimer {nullptr}; 0043 bool m_AutoSave { true }; 0044 QQmlContext *m_pCTX {nullptr}; 0045 ContextAdapter *m_pAdapter {nullptr}; 0046 QModelIndexWatcher *m_pWatcher {nullptr}; 0047 bool m_isBinded { false }; 0048 Mode m_Mode; 0049 0050 // Helper 0051 void bind(); 0052 0053 QModelIndexBinder *q_ptr; 0054 0055 public Q_SLOTS: 0056 void loadWatcher(); 0057 void slotModelPropChanged(); 0058 void slotObjectPropChanged(); 0059 }; 0060 0061 QModelIndexBinder::QModelIndexBinder(QQuickItem *parent) : 0062 QQuickItem(parent), d_ptr(new QModelIndexBinderPrivate()) 0063 { 0064 d_ptr->q_ptr = this; 0065 d_ptr->m_Mode = QModelIndexBinderPrivate::Mode::CONTAINER; 0066 0067 //TODO actually track the context, it could be reparented 0068 QTimer::singleShot(0, d_ptr, SLOT(loadWatcher())); 0069 } 0070 0071 QModelIndexBinder::QModelIndexBinder(QObject *parent) : 0072 QQuickItem(nullptr), d_ptr(new QModelIndexBinderPrivate()) 0073 { 0074 QObject::setParent(parent); 0075 0076 d_ptr->q_ptr = this; 0077 d_ptr->m_Mode = QModelIndexBinderPrivate::Mode::ATTACHED; 0078 0079 //TODO actually track the context, it could be reparented 0080 QTimer::singleShot(0, d_ptr, SLOT(loadWatcher())); 0081 _setObject(parent); 0082 } 0083 0084 0085 QModelIndexBinder::~QModelIndexBinder() 0086 { 0087 delete d_ptr; 0088 } 0089 0090 QString QModelIndexBinder::modelRole() const 0091 { 0092 return d_ptr->m_Role; 0093 } 0094 0095 void QModelIndexBinder::setModelRole(const QString& role) 0096 { 0097 d_ptr->m_Role = role.toLatin1(); 0098 emit changed(); 0099 d_ptr->bind(); 0100 } 0101 0102 QString QModelIndexBinder::objectProperty() const 0103 { 0104 return d_ptr->m_Prop; 0105 } 0106 0107 void QModelIndexBinder::setObjectProperty(const QString& op) 0108 { 0109 d_ptr->m_Prop = op.toLatin1(); 0110 } 0111 0112 QObject *QModelIndexBinder::_object() const 0113 { 0114 return d_ptr->m_pContent; 0115 } 0116 0117 void QModelIndexBinder::_setObject(QObject *o) 0118 { 0119 // If the object is a QQuickItem, add it to the view 0120 if (d_ptr->m_Mode == QModelIndexBinderPrivate::Mode::CONTAINER) { 0121 if (auto w = qobject_cast<QQuickItem*>(o)) { 0122 w->setParentItem(this); 0123 } 0124 } 0125 0126 d_ptr->m_pContent = o; 0127 emit changed(); 0128 d_ptr->bind(); 0129 } 0130 0131 int QModelIndexBinder::delay() const 0132 { 0133 return d_ptr->m_Delay; 0134 } 0135 0136 void QModelIndexBinder::setDelay(int d) 0137 { 0138 d_ptr->m_Delay = d; 0139 emit changed(); 0140 } 0141 0142 bool QModelIndexBinder::autoSave() const 0143 { 0144 return d_ptr->m_AutoSave; 0145 } 0146 0147 void QModelIndexBinder::setAutoSave(bool v) 0148 { 0149 d_ptr->m_AutoSave = v; 0150 emit changed(); 0151 } 0152 0153 bool QModelIndexBinder::isSynchronized() const 0154 { 0155 //TODO 0156 return true; 0157 } 0158 0159 void QModelIndexBinder::reset() const 0160 { 0161 //TODO 0162 } 0163 0164 bool QModelIndexBinder::applyNow() const 0165 { 0166 //TODO 0167 return true; 0168 } 0169 0170 void QModelIndexBinderPrivate::loadWatcher() 0171 { 0172 if (m_pWatcher) 0173 return; 0174 0175 // Container mode 0176 m_pCTX = QQmlEngine::contextForObject(q_ptr); 0177 0178 // Attached mode 0179 if ((!m_pCTX) || m_pContent) 0180 m_pCTX = QQmlEngine::contextForObject(m_pContent); 0181 0182 Q_ASSERT(m_pCTX); 0183 0184 if (!m_pCTX) 0185 return; 0186 0187 auto v = m_pCTX->contextProperty("_modelIndexWatcher"); 0188 auto c = m_pCTX->contextProperty("_contextAdapter"); 0189 0190 m_pAdapter = qvariant_cast<ContextAdapter*>(c); 0191 m_pWatcher = qobject_cast<QModelIndexWatcher*>( 0192 qvariant_cast<QObject*>(v) 0193 ); 0194 0195 Q_ASSERT(m_pAdapter); 0196 Q_ASSERT(m_pWatcher); 0197 0198 bind(); 0199 } 0200 0201 void QModelIndexBinderPrivate::bind() 0202 { 0203 if (m_isBinded || m_Role.isEmpty() || m_Prop.isEmpty() || (!m_pContent) || (!m_pWatcher) || (!m_pAdapter)) 0204 return; 0205 0206 const auto co = m_pAdapter->contextObject(); 0207 0208 // Find the properties on both side 0209 const int objPropId = m_pContent->metaObject()->indexOfProperty(m_Prop); 0210 const int rolePropId = co->metaObject()->indexOfProperty(m_Role); 0211 0212 if (rolePropId == -1) 0213 qWarning() << "Role" << m_Role << "not found"; 0214 0215 if (objPropId == -1) 0216 qWarning() << "Property" << m_Prop << "not found"; 0217 0218 Q_ASSERT(objPropId != -1 && rolePropId != -1); 0219 0220 auto metaProp = m_pContent->metaObject()->property(objPropId); 0221 auto metaRole = co->metaObject()->property(rolePropId); 0222 0223 // Connect to the metaSlots 0224 auto metaSlotProp = metaObject()->method(metaObject()->indexOfMethod("slotObjectPropChanged()")); 0225 auto metaSlotRole = metaObject()->method(metaObject()->indexOfMethod("slotModelPropChanged()")); 0226 0227 // Set the initial value before connecting 0228 slotModelPropChanged(); 0229 0230 connect(m_pContent, metaProp.notifySignal(), this, metaSlotProp); 0231 connect(co , metaRole.notifySignal(), this, metaSlotRole); 0232 } 0233 0234 void QModelIndexBinderPrivate::slotModelPropChanged() 0235 { 0236 const auto role = m_pAdapter->contextObject()->property(m_Role); 0237 0238 //HACK this is a bug in ContextAdapterFactory 0239 if (!role.isValid()) 0240 return; 0241 0242 const auto prop = m_pContent->property(m_Prop); 0243 0244 // Some widgets may not try to detect if the value **really** changes and 0245 // emit signals anyway. 0246 if (role != m_pContent->property(m_Prop)) 0247 m_pContent->setProperty(m_Prop, role); 0248 } 0249 0250 void QModelIndexBinderPrivate::slotObjectPropChanged() 0251 { 0252 const auto role = m_pAdapter->contextObject()->property(m_Role); 0253 0254 Q_ASSERT(role.isValid()); 0255 //HACK this is a bug in ContextAdapterFactory 0256 if (!role.isValid()) 0257 return; 0258 0259 const auto prop = m_pContent->property(m_Prop); 0260 0261 if (role != prop) 0262 m_pAdapter->contextObject()->setProperty(m_Role, prop); //FIXME fix the model::setData support 0263 } 0264 0265 QModelIndexBinder *QModelIndexBinder::qmlAttachedProperties(QObject *object) 0266 { 0267 return new QModelIndexBinder(object); 0268 } 0269 0270 #include <qmodelindexbinder.moc>