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 "qquickdesignersupportmetainfo_p.h"
41#include "qquickdesignersupportproperties_p.h"
42
43#include "qquickdesignercustomobjectdata_p.h"
44
45#include <QGlobalStatic>
46#include <QQmlContext>
47#include <QQmlEngine>
48
49#include <private/qqmlbinding_p.h>
50
51QT_BEGIN_NAMESPACE
52
53typedef QHash<QObject*, QQuickDesignerCustomObjectData*> CustomObjectDataHash;
54Q_GLOBAL_STATIC(CustomObjectDataHash, s_designerObjectToDataHash)
55
56struct HandleDestroyedFunctor {
57 QQuickDesignerCustomObjectData *data;
58 void operator()() { data->handleDestroyed(); }
59};
60
61QQuickDesignerCustomObjectData::QQuickDesignerCustomObjectData(QObject *object)
62 : m_object(object)
63{
64 if (object) {
65 populateResetHashes();
66 s_designerObjectToDataHash()->insert(object, this);
67
68 HandleDestroyedFunctor functor;
69 functor.data = this;
70 QObject::connect(object, &QObject::destroyed, functor);
71 }
72}
73
74void QQuickDesignerCustomObjectData::registerData(QObject *object)
75{
76 new QQuickDesignerCustomObjectData(object);
77}
78
79QQuickDesignerCustomObjectData *QQuickDesignerCustomObjectData::get(QObject *object)
80{
81 return s_designerObjectToDataHash()->value(object);
82}
83
84QVariant QQuickDesignerCustomObjectData::getResetValue(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName)
85{
86 QQuickDesignerCustomObjectData* data = get(object);
87
88 if (data)
89 return data->getResetValue(propertyName);
90
91 return QVariant();
92}
93
94void QQuickDesignerCustomObjectData::doResetProperty(QObject *object, QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName)
95{
96 QQuickDesignerCustomObjectData* data = get(object);
97
98 if (data)
99 data->doResetProperty(context, propertyName);
100}
101
102bool QQuickDesignerCustomObjectData::hasValidResetBinding(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName)
103{
104 QQuickDesignerCustomObjectData* data = get(object);
105
106 if (data)
107 return data->hasValidResetBinding(propertyName);
108
109 return false;
110}
111
112bool QQuickDesignerCustomObjectData::hasBindingForProperty(QObject *object,
113 QQmlContext *context,
114 const QQuickDesignerSupport::PropertyName &propertyName,
115 bool *hasChanged)
116{
117 QQuickDesignerCustomObjectData* data = get(object);
118
119 if (data)
120 return data->hasBindingForProperty(context, propertyName, hasChanged);
121
122 return false;
123}
124
125void QQuickDesignerCustomObjectData::setPropertyBinding(QObject *object,
126 QQmlContext *context,
127 const QQuickDesignerSupport::PropertyName &propertyName,
128 const QString &expression)
129{
130 QQuickDesignerCustomObjectData* data = get(object);
131
132 if (data)
133 data->setPropertyBinding(context, propertyName, expression);
134}
135
136void QQuickDesignerCustomObjectData::keepBindingFromGettingDeleted(QObject *object,
137 QQmlContext *context,
138 const QQuickDesignerSupport::PropertyName &propertyName)
139{
140 QQuickDesignerCustomObjectData* data = get(object);
141
142 if (data)
143 data->keepBindingFromGettingDeleted(context, propertyName);
144}
145
146void QQuickDesignerCustomObjectData::populateResetHashes()
147{
148 const QQuickDesignerSupport::PropertyNameList propertyNameList =
149 QQuickDesignerSupportProperties::propertyNameListForWritableProperties(object());
150
151 const QMetaObject *mo = object()->metaObject();
152 QByteArrayList deferredPropertyNames;
153 const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames");
154 if (namesIndex != -1) {
155 QMetaClassInfo classInfo = mo->classInfo(namesIndex);
156 deferredPropertyNames = QByteArray(classInfo.value()).split(',');
157 }
158
159 for (const QQuickDesignerSupport::PropertyName &propertyName : propertyNameList) {
160
161 if (deferredPropertyNames.contains(propertyName))
162 continue;
163
164 QQmlProperty property(object(), QString::fromUtf8(propertyName), QQmlEngine::contextForObject(object()));
165
166 QQmlAbstractBinding::Ptr binding = QQmlAbstractBinding::Ptr(QQmlPropertyPrivate::binding(property));
167
168 if (binding) {
169 m_resetBindingHash.insert(propertyName, binding);
170 } else if (property.isWritable()) {
171 m_resetValueHash.insert(propertyName, property.read());
172 }
173 }
174}
175
176QObject *QQuickDesignerCustomObjectData::object() const
177{
178 return m_object;
179}
180
181QVariant QQuickDesignerCustomObjectData::getResetValue(const QQuickDesignerSupport::PropertyName &propertyName) const
182{
183 return m_resetValueHash.value(propertyName);
184}
185
186void QQuickDesignerCustomObjectData::doResetProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName)
187{
188 QQmlProperty property(object(), QString::fromUtf8(propertyName), context);
189
190 if (!property.isValid())
191 return;
192
193 QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(property);
194 if (binding && !(hasValidResetBinding(propertyName) && getResetBinding(propertyName) == binding)) {
195 binding->setEnabled(false, nullptr);
196 }
197
198
199 if (hasValidResetBinding(propertyName)) {
200 QQmlAbstractBinding *binding = getResetBinding(propertyName);
201
202#if defined(QT_NO_DYNAMIC_CAST)
203 QQmlBinding *qmlBinding = static_cast<QQmlBinding*>(binding);
204#else
205 QQmlBinding *qmlBinding = dynamic_cast<QQmlBinding*>(binding);
206#endif
207 if (qmlBinding)
208 qmlBinding->setTarget(property);
209 QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding);
210 if (qmlBinding)
211 qmlBinding->update();
212
213 } else if (property.isResettable()) {
214 property.reset();
215 } else if (property.propertyTypeCategory() == QQmlProperty::List) {
216 QQmlListReference list = qvariant_cast<QQmlListReference>(property.read());
217
218 if (!QQuickDesignerSupportProperties::hasFullImplementedListInterface(list)) {
219 qWarning() << "Property list interface not fully implemented for Class " << property.property().typeName() << " in property " << property.name() << "!";
220 return;
221 }
222
223 list.clear();
224 } else if (property.isWritable()) {
225 if (property.read() == getResetValue(propertyName))
226 return;
227
228 property.write(getResetValue(propertyName));
229 }
230}
231
232bool QQuickDesignerCustomObjectData::hasValidResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const
233{
234 return m_resetBindingHash.contains(propertyName) && m_resetBindingHash.value(propertyName).data();
235}
236
237QQmlAbstractBinding *QQuickDesignerCustomObjectData::getResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const
238{
239 return m_resetBindingHash.value(propertyName).data();
240}
241
242bool QQuickDesignerCustomObjectData::hasBindingForProperty(QQmlContext *context,
243 const QQuickDesignerSupport::PropertyName &propertyName,
244 bool *hasChanged) const
245{
246 if (QQuickDesignerSupportProperties::isPropertyBlackListed(propertyName))
247 return false;
248
249 QQmlProperty property(object(), QString::fromUtf8(propertyName), context);
250
251 bool hasBinding = QQmlPropertyPrivate::binding(property);
252
253 if (hasChanged) {
254 *hasChanged = hasBinding != m_hasBindingHash.value(propertyName, false);
255 if (*hasChanged)
256 m_hasBindingHash.insert(propertyName, hasBinding);
257 }
258
259 return QQmlPropertyPrivate::binding(property);
260}
261
262void QQuickDesignerCustomObjectData::setPropertyBinding(QQmlContext *context,
263 const QQuickDesignerSupport::PropertyName &propertyName,
264 const QString &expression)
265{
266 QQmlProperty property(object(), QString::fromUtf8(propertyName), context);
267
268 if (!property.isValid())
269 return;
270
271 if (property.isProperty()) {
272 QQmlBinding *binding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core,
273 expression, object(), QQmlContextData::get(context));
274 binding->setTarget(property);
275 binding->setNotifyOnValueChanged(true);
276
277 QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding);
278 //Refcounting is taking take care of deletion
279 binding->update();
280 if (binding->hasError()) {
281 if (property.property().userType() == QVariant::String)
282 property.write(QVariant(QLatin1Char('#') + expression + QLatin1Char('#')));
283 }
284
285 } else {
286 qWarning() << Q_FUNC_INFO << ": Cannot set binding for property" << propertyName << ": property is unknown for type";
287 }
288}
289
290void QQuickDesignerCustomObjectData::keepBindingFromGettingDeleted(QQmlContext *context,
291 const QQuickDesignerSupport::PropertyName &propertyName)
292{
293 //Refcounting is taking care
294 Q_UNUSED(context)
295 Q_UNUSED(propertyName)
296}
297
298void QQuickDesignerCustomObjectData::handleDestroyed()
299{
300 s_designerObjectToDataHash()->remove(m_object);
301 delete this;
302}
303
304QT_END_NAMESPACE
305
306