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 QtQuick 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 "qqmldesignermetaobject_p.h"
41
42#include <QSharedPointer>
43#include <QMetaProperty>
44#include <QtCore/private/qnumeric_p.h>
45#include <QDebug>
46
47#include <private/qqmlengine_p.h>
48
49QT_BEGIN_NAMESPACE
50
51static QHash<QDynamicMetaObjectData *, bool> nodeInstanceMetaObjectList;
52static void (*notifyPropertyChangeCallBack)(QObject*, const QQuickDesignerSupport::PropertyName &propertyName) = nullptr;
53
54struct MetaPropertyData {
55 inline QPair<QVariant, bool> &getDataRef(int idx) {
56 while (m_data.count() <= idx)
57 m_data << QPair<QVariant, bool>(QVariant(), false);
58 return m_data[idx];
59 }
60
61 inline QVariant &getData(int idx) {
62 QPair<QVariant, bool> &prop = getDataRef(idx);
63 if (!prop.second) {
64 prop.first = QVariant();
65 prop.second = true;
66 }
67 return prop.first;
68 }
69
70 inline bool hasData(int idx) const {
71 if (idx >= m_data.count())
72 return false;
73 return m_data[idx].second;
74 }
75
76 inline int count() { return m_data.count(); }
77
78 QVector<QPair<QVariant, bool> > m_data;
79};
80
81static QQmlPropertyCache *cacheForObject(QObject *object, QQmlEngine *engine)
82{
83 QQmlVMEMetaObject *metaObject = QQmlVMEMetaObject::get(object);
84 if (metaObject)
85 return metaObject->cache.data();
86
87 return QQmlEnginePrivate::get(engine)->cache(object);
88}
89
90QQmlDesignerMetaObject* QQmlDesignerMetaObject::getNodeInstanceMetaObject(QObject *object, QQmlEngine *engine)
91{
92 //Avoid setting up multiple MetaObjects on the same QObject
93 QObjectPrivate *op = QObjectPrivate::get(object);
94 QDynamicMetaObjectData *parent = op->metaObject;
95 if (nodeInstanceMetaObjectList.contains(parent))
96 return static_cast<QQmlDesignerMetaObject *>(parent);
97
98 // we just create one and the ownership goes automatically to the object in nodeinstance see init method
99
100 QQmlData *ddata = QQmlData::get(object, false);
101
102 const bool hadVMEMetaObject = ddata ? ddata->hasVMEMetaObject : false;
103 QQmlDesignerMetaObject *mo = new QQmlDesignerMetaObject(object, engine);
104 //If our parent is not a VMEMetaObject we just set the flag to false again
105 if (ddata)
106 ddata->hasVMEMetaObject = hadVMEMetaObject;
107 return mo;
108}
109
110void QQmlDesignerMetaObject::init(QObject *object, QQmlEngine *engine)
111{
112 //Creating QQmlOpenMetaObjectType
113 m_type = new QQmlOpenMetaObjectType(metaObjectParent(), engine);
114 m_type->addref();
115 //Assigning type to this
116 copyTypeMetaObject();
117
118 //Assign this to object
119 QObjectPrivate *op = QObjectPrivate::get(object);
120 op->metaObject = this;
121
122 cache = QQmlEnginePrivate::get(engine)->cache(this);
123
124 nodeInstanceMetaObjectList.insert(this, true);
125 hasAssignedMetaObjectData = true;
126}
127
128QQmlDesignerMetaObject::QQmlDesignerMetaObject(QObject *object, QQmlEngine *engine)
129 : QQmlVMEMetaObject(engine->handle(), object, cacheForObject(object, engine), /*qml compilation unit*/nullptr, /*qmlObjectId*/-1),
130 m_context(engine->contextForObject(object)),
131 m_data(new MetaPropertyData)
132{
133 init(object, engine);
134
135 QQmlData *ddata = QQmlData::get(object, false);
136 //Assign cache to object
137 if (ddata && ddata->propertyCache) {
138 cache->setParent(ddata->propertyCache);
139 cache->invalidate(this);
140 ddata->propertyCache->release();
141 ddata->propertyCache = cache.data();
142 ddata->propertyCache->addref();
143 }
144
145}
146
147QQmlDesignerMetaObject::~QQmlDesignerMetaObject()
148{
149 m_type->release();
150
151 nodeInstanceMetaObjectList.remove(this);
152}
153
154void QQmlDesignerMetaObject::createNewDynamicProperty(const QString &name)
155{
156 int id = m_type->createProperty(name.toUtf8());
157 copyTypeMetaObject();
158 setValue(id, QVariant());
159 Q_ASSERT(id >= 0);
160 Q_UNUSED(id);
161
162 //Updating cache
163 QQmlPropertyCache *oldParent = cache->parent();
164 QQmlEnginePrivate::get(m_context->engine())->cache(this)->invalidate(this);
165 cache->setParent(oldParent);
166
167 QQmlProperty property(myObject(), name, m_context);
168 Q_ASSERT(property.isValid());
169}
170
171void QQmlDesignerMetaObject::setValue(int id, const QVariant &value)
172{
173 QPair<QVariant, bool> &prop = m_data->getDataRef(id);
174 prop.first = propertyWriteValue(id, value);
175 prop.second = true;
176 QMetaObject::activate(myObject(), id + m_type->signalOffset(), nullptr);
177}
178
179QVariant QQmlDesignerMetaObject::propertyWriteValue(int, const QVariant &value)
180{
181 return value;
182}
183
184const QAbstractDynamicMetaObject *QQmlDesignerMetaObject::dynamicMetaObjectParent() const
185{
186 if (QQmlVMEMetaObject::parent.isT1())
187 return QQmlVMEMetaObject::parent.asT1()->toDynamicMetaObject(QQmlVMEMetaObject::object);
188 else
189 return nullptr;
190}
191
192const QMetaObject *QQmlDesignerMetaObject::metaObjectParent() const
193{
194 if (QQmlVMEMetaObject::parent.isT1())
195 return QQmlVMEMetaObject::parent.asT1()->toDynamicMetaObject(QQmlVMEMetaObject::object);
196
197 return QQmlVMEMetaObject::parent.asT2();
198}
199
200int QQmlDesignerMetaObject::propertyOffset() const
201{
202 return cache->propertyOffset();
203}
204
205int QQmlDesignerMetaObject::openMetaCall(QObject *o, QMetaObject::Call call, int id, void **a)
206{
207 if ((call == QMetaObject::ReadProperty || call == QMetaObject::WriteProperty)
208 && id >= m_type->propertyOffset()) {
209 int propId = id - m_type->propertyOffset();
210 if (call == QMetaObject::ReadProperty) {
211 //propertyRead(propId);
212 *reinterpret_cast<QVariant *>(a[0]) = m_data->getData(propId);
213 } else if (call == QMetaObject::WriteProperty) {
214 if (propId <= m_data->count() || m_data->m_data[propId].first != *reinterpret_cast<QVariant *>(a[0])) {
215 //propertyWrite(propId);
216 QPair<QVariant, bool> &prop = m_data->getDataRef(propId);
217 prop.first = propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0]));
218 prop.second = true;
219 //propertyWritten(propId);
220 activate(myObject(), m_type->signalOffset() + propId, nullptr);
221 }
222 }
223 return -1;
224 } else {
225 QAbstractDynamicMetaObject *directParent = parent();
226 if (directParent)
227 return directParent->metaCall(o, call, id, a);
228 else
229 return myObject()->qt_metacall(call, id, a);
230 }
231}
232
233int QQmlDesignerMetaObject::metaCall(QObject *o, QMetaObject::Call call, int id, void **a)
234{
235 Q_ASSERT(myObject() == o);
236
237 int metaCallReturnValue = -1;
238
239 const QMetaProperty propertyById = QQmlVMEMetaObject::property(id);
240
241 if (call == QMetaObject::WriteProperty
242 && propertyById.userType() == QMetaType::QVariant
243 && reinterpret_cast<QVariant *>(a[0])->type() == QVariant::Double
244 && qt_is_nan(reinterpret_cast<QVariant *>(a[0])->toDouble())) {
245 return -1;
246 }
247
248 if (call == QMetaObject::WriteProperty
249 && propertyById.userType() == QMetaType::Double
250 && qt_is_nan(*reinterpret_cast<double*>(a[0]))) {
251 return -1;
252 }
253
254 if (call == QMetaObject::WriteProperty
255 && propertyById.userType() == QMetaType::Float
256 && qt_is_nan(*reinterpret_cast<float*>(a[0]))) {
257 return -1;
258 }
259
260 QVariant oldValue;
261
262 if (call == QMetaObject::WriteProperty && !propertyById.hasNotifySignal())
263 {
264 oldValue = propertyById.read(myObject());
265 }
266
267 QAbstractDynamicMetaObject *directParent = parent();
268 if (directParent && id < directParent->propertyOffset()) {
269 metaCallReturnValue = directParent->metaCall(o, call, id, a);
270 } else {
271 openMetaCall(o, call, id, a);
272 }
273
274
275 if (call == QMetaObject::WriteProperty
276 && !propertyById.hasNotifySignal()
277 && oldValue != propertyById.read(myObject()))
278 notifyPropertyChange(id);
279
280 return metaCallReturnValue;
281}
282
283void QQmlDesignerMetaObject::notifyPropertyChange(int id)
284{
285 const QMetaProperty propertyById = property(id);
286
287 if (id < propertyOffset()) {
288 if (notifyPropertyChangeCallBack)
289 notifyPropertyChangeCallBack(myObject(), propertyById.name());
290 } else {
291 if (notifyPropertyChangeCallBack)
292 notifyPropertyChangeCallBack(myObject(), name(id - propertyOffset()));
293 }
294}
295
296int QQmlDesignerMetaObject::count() const
297{
298 return m_type->propertyCount();
299}
300
301QByteArray QQmlDesignerMetaObject::name(int idx) const
302{
303 return m_type->propertyName(idx);
304}
305
306void QQmlDesignerMetaObject::copyTypeMetaObject()
307{
308 *static_cast<QMetaObject *>(this) = *m_type->metaObject();
309}
310
311void QQmlDesignerMetaObject::registerNotifyPropertyChangeCallBack(void (*callback)(QObject *, const QQuickDesignerSupport::PropertyName &))
312{
313 notifyPropertyChangeCallBack = callback;
314}
315
316QT_END_NAMESPACE
317