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 | |
47 | QT_BEGIN_NAMESPACE |
48 | |
49 | |
50 | class QQmlOpenMetaObjectTypePrivate |
51 | { |
52 | public: |
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 | |
67 | QQmlOpenMetaObjectType::QQmlOpenMetaObjectType(const QMetaObject *base, QQmlEngine *engine) |
68 | : QQmlCleanup(engine), d(new QQmlOpenMetaObjectTypePrivate) |
69 | { |
70 | d->engine = engine; |
71 | d->init(metaObj: base); |
72 | } |
73 | |
74 | QQmlOpenMetaObjectType::~QQmlOpenMetaObjectType() |
75 | { |
76 | if (d->mem) |
77 | free(ptr: d->mem); |
78 | if (d->cache) |
79 | d->cache->release(); |
80 | delete d; |
81 | } |
82 | |
83 | void QQmlOpenMetaObjectType::clear() |
84 | { |
85 | d->engine = nullptr; |
86 | } |
87 | |
88 | int QQmlOpenMetaObjectType::propertyOffset() const |
89 | { |
90 | return d->propertyOffset; |
91 | } |
92 | |
93 | int QQmlOpenMetaObjectType::signalOffset() const |
94 | { |
95 | return d->signalOffset; |
96 | } |
97 | |
98 | int QQmlOpenMetaObjectType::propertyCount() const |
99 | { |
100 | return d->names.count(); |
101 | } |
102 | |
103 | QByteArray QQmlOpenMetaObjectType::propertyName(int idx) const |
104 | { |
105 | Q_ASSERT(idx >= 0 && idx < d->names.count()); |
106 | |
107 | return d->mob.property(index: idx).name(); |
108 | } |
109 | |
110 | QMetaObject *QQmlOpenMetaObjectType::metaObject() const |
111 | { |
112 | return d->mem; |
113 | } |
114 | |
115 | void 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(signature: "__" + QByteArray::number(id) + "()" ); |
121 | QMetaPropertyBuilder build = d->mob.addProperty(name, type: "QVariant" , notifierId: id); |
122 | propertyCreated(id, build); |
123 | d->names.insert(akey: name, avalue: id); |
124 | } |
125 | free(ptr: 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 | |
137 | int QQmlOpenMetaObjectType::createProperty(const QByteArray &name) |
138 | { |
139 | int id = d->mob.propertyCount(); |
140 | d->mob.addSignal(signature: "__" + QByteArray::number(id) + "()" ); |
141 | QMetaPropertyBuilder build = d->mob.addProperty(name, type: "QVariant" , notifierId: id); |
142 | propertyCreated(id, build); |
143 | free(ptr: d->mem); |
144 | d->mem = d->mob.toMetaObject(); |
145 | d->names.insert(akey: name, avalue: 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 | |
158 | void QQmlOpenMetaObjectType::propertyCreated(int id, QMetaPropertyBuilder &builder) |
159 | { |
160 | if (d->referers.count()) |
161 | (*d->referers.begin())->propertyCreated(id, builder); |
162 | } |
163 | |
164 | void 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 | |
180 | class QQmlOpenMetaObjectPrivate |
181 | { |
182 | public: |
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(type: m_value.userType()) & QMetaType::PointerToQObject |
195 | && qobjectTracker.isNull()) |
196 | return QVariant::fromValue<QObject*>(value: 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(type: 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(asize: idx + 1); |
211 | data[idx].setValue(value); |
212 | } |
213 | |
214 | inline Property &propertyRef(int idx) { |
215 | if (data.count() <= idx) |
216 | data.resize(asize: 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 | |
248 | QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, const QMetaObject *base, bool automatic) |
249 | : d(new QQmlOpenMetaObjectPrivate(this, automatic, obj)) |
250 | { |
251 | d->type.adopt(other: new QQmlOpenMetaObjectType(base ? base : obj->metaObject(), nullptr)); |
252 | d->type->d->referers.insert(value: this); |
253 | |
254 | QObjectPrivate *op = QObjectPrivate::get(o: obj); |
255 | d->parent = static_cast<QAbstractDynamicMetaObject *>(op->metaObject); |
256 | *static_cast<QMetaObject *>(this) = *d->type->d->mem; |
257 | op->metaObject = this; |
258 | } |
259 | |
260 | QQmlOpenMetaObject::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(value: this); |
265 | |
266 | QObjectPrivate *op = QObjectPrivate::get(o: obj); |
267 | d->parent = static_cast<QAbstractDynamicMetaObject *>(op->metaObject); |
268 | *static_cast<QMetaObject *>(this) = *d->type->d->mem; |
269 | op->metaObject = this; |
270 | } |
271 | |
272 | QQmlOpenMetaObject::~QQmlOpenMetaObject() |
273 | { |
274 | if (d->parent) |
275 | delete d->parent; |
276 | d->type->d->referers.remove(value: this); |
277 | delete d; |
278 | } |
279 | |
280 | QQmlOpenMetaObjectType *QQmlOpenMetaObject::type() const |
281 | { |
282 | return d->type.data(); |
283 | } |
284 | |
285 | void QQmlOpenMetaObject::emitPropertyNotification(const QByteArray &propertyName) |
286 | { |
287 | QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(akey: propertyName); |
288 | if (iter == d->type->d->names.constEnd()) |
289 | return; |
290 | activate(sender: d->object, signal_index: *iter + d->type->d->signalOffset, argv: nullptr); |
291 | } |
292 | |
293 | int 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(idx: propId); |
303 | } else if (c == QMetaObject::WriteProperty) { |
304 | if (propId >= d->data.count() || d->data.at(i: propId).value() != *reinterpret_cast<QVariant *>(a[0])) { |
305 | propertyWrite(propId); |
306 | d->setPropertyValue(idx: propId, value: propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0]))); |
307 | propertyWritten(propId); |
308 | activate(sender: o, signal_index: d->type->d->signalOffset + propId, argv: nullptr); |
309 | } |
310 | } |
311 | return -1; |
312 | } else { |
313 | if (d->parent) |
314 | return d->parent->metaCall(o, c, id: id, a); |
315 | else |
316 | return o->qt_metacall(c, id, a); |
317 | } |
318 | } |
319 | |
320 | QAbstractDynamicMetaObject *QQmlOpenMetaObject::parent() const |
321 | { |
322 | return d->parent; |
323 | } |
324 | |
325 | QVariant QQmlOpenMetaObject::value(int id) const |
326 | { |
327 | return d->propertyValue(idx: id); |
328 | } |
329 | |
330 | void QQmlOpenMetaObject::setValue(int id, const QVariant &value) |
331 | { |
332 | d->setPropertyValue(idx: id, value: propertyWriteValue(id, value)); |
333 | activate(sender: d->object, signal_index: id + d->type->d->signalOffset, argv: nullptr); |
334 | } |
335 | |
336 | QVariant QQmlOpenMetaObject::value(const QByteArray &name) const |
337 | { |
338 | QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(akey: name); |
339 | if (iter == d->type->d->names.cend()) |
340 | return QVariant(); |
341 | |
342 | return d->propertyValue(idx: *iter); |
343 | } |
344 | |
345 | QVariant &QQmlOpenMetaObject::valueRef(const QByteArray &name) |
346 | { |
347 | QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(akey: name); |
348 | Q_ASSERT(iter != d->type->d->names.cend()); |
349 | |
350 | return d->propertyValueRef(idx: *iter); |
351 | } |
352 | |
353 | bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val, bool force) |
354 | { |
355 | QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(akey: 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(idx: id) == val) |
366 | return false; |
367 | |
368 | d->setPropertyValue(idx: id, value: val); |
369 | activate(sender: d->object, signal_index: id + d->type->d->signalOffset, argv: 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() |
377 | bool QQmlOpenMetaObject::hasValue(int id) const |
378 | { |
379 | return d->hasProperty(idx: id); |
380 | } |
381 | |
382 | void 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(object: d->object, create: 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 | |
403 | int 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(object: 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 | |
420 | void QQmlOpenMetaObject::propertyRead(int) |
421 | { |
422 | } |
423 | |
424 | void QQmlOpenMetaObject::propertyWrite(int) |
425 | { |
426 | } |
427 | |
428 | QVariant QQmlOpenMetaObject::propertyWriteValue(int, const QVariant &value) |
429 | { |
430 | return value; |
431 | } |
432 | |
433 | void QQmlOpenMetaObject::propertyWritten(int) |
434 | { |
435 | } |
436 | |
437 | void QQmlOpenMetaObject::propertyCreated(int, QMetaPropertyBuilder &) |
438 | { |
439 | } |
440 | |
441 | QVariant QQmlOpenMetaObject::initialValue(int) |
442 | { |
443 | return QVariant(); |
444 | } |
445 | |
446 | int QQmlOpenMetaObject::count() const |
447 | { |
448 | return d->type->d->names.count(); |
449 | } |
450 | |
451 | QByteArray 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(index: idx).name(); |
456 | } |
457 | |
458 | QObject *QQmlOpenMetaObject::object() const |
459 | { |
460 | return d->object; |
461 | } |
462 | |
463 | QT_END_NAMESPACE |
464 | |