1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qqmlopenmetaobject_p.h"
41#include <private/qqmlpropertycache_p.h>
42#include <private/qqmldata_p.h>
43#include <private/qmetaobjectbuilder_p.h>
44#include <qqmlengine.h>
45#include <qdebug.h>
46
47QT_BEGIN_NAMESPACE
48
49
50class QQmlOpenMetaObjectTypePrivate
51{
52public:
53 QQmlOpenMetaObjectTypePrivate() : mem(nullptr), cache(nullptr), engine(nullptr) {}
54
55 void init(const QMetaObject *metaObj);
56
57 int propertyOffset;
58 int signalOffset;
59 QHash<QByteArray, int> names;
60 QMetaObjectBuilder mob;
61 QMetaObject *mem;
62 QQmlPropertyCache *cache;
63 QQmlEngine *engine;
64 QSet<QQmlOpenMetaObject*> referers;
65};
66
67QQmlOpenMetaObjectType::QQmlOpenMetaObjectType(const QMetaObject *base, QQmlEngine *engine)
68 : QQmlCleanup(engine), d(new QQmlOpenMetaObjectTypePrivate)
69{
70 d->engine = engine;
71 d->init(base);
72}
73
74QQmlOpenMetaObjectType::~QQmlOpenMetaObjectType()
75{
76 if (d->mem)
77 free(d->mem);
78 if (d->cache)
79 d->cache->release();
80 delete d;
81}
82
83void QQmlOpenMetaObjectType::clear()
84{
85 d->engine = nullptr;
86}
87
88int QQmlOpenMetaObjectType::propertyOffset() const
89{
90 return d->propertyOffset;
91}
92
93int QQmlOpenMetaObjectType::signalOffset() const
94{
95 return d->signalOffset;
96}
97
98int QQmlOpenMetaObjectType::propertyCount() const
99{
100 return d->names.count();
101}
102
103QByteArray QQmlOpenMetaObjectType::propertyName(int idx) const
104{
105 Q_ASSERT(idx >= 0 && idx < d->names.count());
106
107 return d->mob.property(idx).name();
108}
109
110QMetaObject *QQmlOpenMetaObjectType::metaObject() const
111{
112 return d->mem;
113}
114
115void QQmlOpenMetaObjectType::createProperties(const QVector<QByteArray> &names)
116{
117 for (int i = 0; i < names.count(); ++i) {
118 const QByteArray &name = names.at(i);
119 const int id = d->mob.propertyCount();
120 d->mob.addSignal("__" + QByteArray::number(id) + "()");
121 QMetaPropertyBuilder build = d->mob.addProperty(name, "QVariant", id);
122 propertyCreated(id, build);
123 d->names.insert(name, id);
124 }
125 free(d->mem);
126 d->mem = d->mob.toMetaObject();
127 QSet<QQmlOpenMetaObject*>::iterator it = d->referers.begin();
128 while (it != d->referers.end()) {
129 QQmlOpenMetaObject *omo = *it;
130 *static_cast<QMetaObject *>(omo) = *d->mem;
131 if (d->cache)
132 d->cache->update(omo);
133 ++it;
134 }
135}
136
137int QQmlOpenMetaObjectType::createProperty(const QByteArray &name)
138{
139 int id = d->mob.propertyCount();
140 d->mob.addSignal("__" + QByteArray::number(id) + "()");
141 QMetaPropertyBuilder build = d->mob.addProperty(name, "QVariant", id);
142 propertyCreated(id, build);
143 free(d->mem);
144 d->mem = d->mob.toMetaObject();
145 d->names.insert(name, id);
146 QSet<QQmlOpenMetaObject*>::iterator it = d->referers.begin();
147 while (it != d->referers.end()) {
148 QQmlOpenMetaObject *omo = *it;
149 *static_cast<QMetaObject *>(omo) = *d->mem;
150 if (d->cache)
151 d->cache->update(omo);
152 ++it;
153 }
154
155 return d->propertyOffset + id;
156}
157
158void QQmlOpenMetaObjectType::propertyCreated(int id, QMetaPropertyBuilder &builder)
159{
160 if (d->referers.count())
161 (*d->referers.begin())->propertyCreated(id, builder);
162}
163
164void QQmlOpenMetaObjectTypePrivate::init(const QMetaObject *metaObj)
165{
166 if (!mem) {
167 mob.setSuperClass(metaObj);
168 mob.setClassName(metaObj->className());
169 mob.setFlags(QMetaObjectBuilder::DynamicMetaObject);
170
171 mem = mob.toMetaObject();
172
173 propertyOffset = mem->propertyOffset();
174 signalOffset = mem->methodOffset();
175 }
176}
177
178//----------------------------------------------------------------------------
179
180class QQmlOpenMetaObjectPrivate
181{
182public:
183 QQmlOpenMetaObjectPrivate(QQmlOpenMetaObject *_q, bool _autoCreate, QObject *obj)
184 : q(_q), object(obj), autoCreate(_autoCreate) {}
185
186 struct Property {
187 private:
188 QVariant m_value;
189 QPointer<QObject> qobjectTracker;
190 public:
191 bool valueSet = false;
192
193 QVariant value() const {
194 if (QMetaType::typeFlags(m_value.userType()) & QMetaType::PointerToQObject
195 && qobjectTracker.isNull())
196 return QVariant::fromValue<QObject*>(nullptr);
197 return m_value;
198 }
199 QVariant &valueRef() { return m_value; }
200 void setValue(const QVariant &v) {
201 m_value = v;
202 valueSet = true;
203 if (QMetaType::typeFlags(v.userType()) & QMetaType::PointerToQObject)
204 qobjectTracker = m_value.value<QObject*>();
205 }
206 };
207
208 inline void setPropertyValue(int idx, const QVariant &value) {
209 if (data.count() <= idx)
210 data.resize(idx + 1);
211 data[idx].setValue(value);
212 }
213
214 inline Property &propertyRef(int idx) {
215 if (data.count() <= idx)
216 data.resize(idx + 1);
217 Property &prop = data[idx];
218 if (!prop.valueSet)
219 prop.setValue(q->initialValue(idx));
220 return prop;
221 }
222
223 inline QVariant propertyValue(int idx) {
224 auto &prop = propertyRef(idx);
225 return prop.value();
226 }
227
228 inline QVariant &propertyValueRef(int idx) {
229 auto &prop = propertyRef(idx);
230 return prop.valueRef();
231 }
232
233 inline bool hasProperty(int idx) const {
234 if (idx >= data.count())
235 return false;
236 return data[idx].valueSet;
237 }
238
239 QQmlOpenMetaObject *q;
240 QAbstractDynamicMetaObject *parent = nullptr;
241 QVector<Property> data;
242 QObject *object;
243 QQmlRefPointer<QQmlOpenMetaObjectType> type;
244 bool autoCreate;
245 bool cacheProperties = false;
246};
247
248QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, const QMetaObject *base, bool automatic)
249: d(new QQmlOpenMetaObjectPrivate(this, automatic, obj))
250{
251 d->type.adopt(new QQmlOpenMetaObjectType(base ? base : obj->metaObject(), nullptr));
252 d->type->d->referers.insert(this);
253
254 QObjectPrivate *op = QObjectPrivate::get(obj);
255 d->parent = static_cast<QAbstractDynamicMetaObject *>(op->metaObject);
256 *static_cast<QMetaObject *>(this) = *d->type->d->mem;
257 op->metaObject = this;
258}
259
260QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, QQmlOpenMetaObjectType *type, bool automatic)
261: d(new QQmlOpenMetaObjectPrivate(this, automatic, obj))
262{
263 d->type = type;
264 d->type->d->referers.insert(this);
265
266 QObjectPrivate *op = QObjectPrivate::get(obj);
267 d->parent = static_cast<QAbstractDynamicMetaObject *>(op->metaObject);
268 *static_cast<QMetaObject *>(this) = *d->type->d->mem;
269 op->metaObject = this;
270}
271
272QQmlOpenMetaObject::~QQmlOpenMetaObject()
273{
274 if (d->parent)
275 delete d->parent;
276 d->type->d->referers.remove(this);
277 delete d;
278}
279
280QQmlOpenMetaObjectType *QQmlOpenMetaObject::type() const
281{
282 return d->type.data();
283}
284
285void QQmlOpenMetaObject::emitPropertyNotification(const QByteArray &propertyName)
286{
287 QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(propertyName);
288 if (iter == d->type->d->names.constEnd())
289 return;
290 activate(d->object, *iter + d->type->d->signalOffset, nullptr);
291}
292
293int QQmlOpenMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void **a)
294{
295 Q_ASSERT(d->object == o);
296
297 if (( c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty)
298 && id >= d->type->d->propertyOffset) {
299 int propId = id - d->type->d->propertyOffset;
300 if (c == QMetaObject::ReadProperty) {
301 propertyRead(propId);
302 *reinterpret_cast<QVariant *>(a[0]) = d->propertyValue(propId);
303 } else if (c == QMetaObject::WriteProperty) {
304 if (propId >= d->data.count() || d->data.at(propId).value() != *reinterpret_cast<QVariant *>(a[0])) {
305 propertyWrite(propId);
306 d->setPropertyValue(propId, propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0])));
307 propertyWritten(propId);
308 activate(o, d->type->d->signalOffset + propId, nullptr);
309 }
310 }
311 return -1;
312 } else {
313 if (d->parent)
314 return d->parent->metaCall(o, c, id, a);
315 else
316 return o->qt_metacall(c, id, a);
317 }
318}
319
320QAbstractDynamicMetaObject *QQmlOpenMetaObject::parent() const
321{
322 return d->parent;
323}
324
325QVariant QQmlOpenMetaObject::value(int id) const
326{
327 return d->propertyValue(id);
328}
329
330void QQmlOpenMetaObject::setValue(int id, const QVariant &value)
331{
332 d->setPropertyValue(id, propertyWriteValue(id, value));
333 activate(d->object, id + d->type->d->signalOffset, nullptr);
334}
335
336QVariant QQmlOpenMetaObject::value(const QByteArray &name) const
337{
338 QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name);
339 if (iter == d->type->d->names.cend())
340 return QVariant();
341
342 return d->propertyValue(*iter);
343}
344
345QVariant &QQmlOpenMetaObject::valueRef(const QByteArray &name)
346{
347 QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name);
348 Q_ASSERT(iter != d->type->d->names.cend());
349
350 return d->propertyValueRef(*iter);
351}
352
353bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val, bool force)
354{
355 QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name);
356
357 int id = -1;
358 if (iter == d->type->d->names.cend()) {
359 id = createProperty(name.constData(), "") - d->type->d->propertyOffset;
360 } else {
361 id = *iter;
362 }
363
364 if (id >= 0) {
365 if (!force && d->propertyValue(id) == val)
366 return false;
367
368 d->setPropertyValue(id, val);
369 activate(d->object, id + d->type->d->signalOffset, nullptr);
370 return true;
371 }
372
373 return false;
374}
375
376// returns true if this value has been initialized by a call to either value() or setValue()
377bool QQmlOpenMetaObject::hasValue(int id) const
378{
379 return d->hasProperty(id);
380}
381
382void QQmlOpenMetaObject::setCached(bool c)
383{
384 if (c == d->cacheProperties || !d->type->d->engine)
385 return;
386
387 d->cacheProperties = c;
388
389 QQmlData *qmldata = QQmlData::get(d->object, true);
390 if (d->cacheProperties) {
391 if (!d->type->d->cache)
392 d->type->d->cache = new QQmlPropertyCache(this);
393 qmldata->propertyCache = d->type->d->cache;
394 d->type->d->cache->addref();
395 } else {
396 if (d->type->d->cache)
397 d->type->d->cache->release();
398 qmldata->propertyCache = nullptr;
399 }
400}
401
402
403int QQmlOpenMetaObject::createProperty(const char *name, const char *)
404{
405 if (d->autoCreate) {
406 int result = d->type->createProperty(name);
407
408 if (QQmlData *ddata = QQmlData::get(d->object, /*create*/false)) {
409 if (ddata->propertyCache) {
410 ddata->propertyCache->release();
411 ddata->propertyCache = nullptr;
412 }
413 }
414
415 return result;
416 } else
417 return -1;
418}
419
420void QQmlOpenMetaObject::propertyRead(int)
421{
422}
423
424void QQmlOpenMetaObject::propertyWrite(int)
425{
426}
427
428QVariant QQmlOpenMetaObject::propertyWriteValue(int, const QVariant &value)
429{
430 return value;
431}
432
433void QQmlOpenMetaObject::propertyWritten(int)
434{
435}
436
437void QQmlOpenMetaObject::propertyCreated(int, QMetaPropertyBuilder &)
438{
439}
440
441QVariant QQmlOpenMetaObject::initialValue(int)
442{
443 return QVariant();
444}
445
446int QQmlOpenMetaObject::count() const
447{
448 return d->type->d->names.count();
449}
450
451QByteArray QQmlOpenMetaObject::name(int idx) const
452{
453 Q_ASSERT(idx >= 0 && idx < d->type->d->names.count());
454
455 return d->type->d->mob.property(idx).name();
456}
457
458QObject *QQmlOpenMetaObject::object() const
459{
460 return d->object;
461}
462
463QT_END_NAMESPACE
464