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 "qqmlpropertymap.h"
41
42#include <private/qmetaobjectbuilder_p.h>
43#include <private/qqmlopenmetaobject_p.h>
44
45#include <QDebug>
46
47QT_BEGIN_NAMESPACE
48
49//QQmlPropertyMapMetaObject lets us listen for changes coming from QML
50//so we can emit the changed signal.
51class QQmlPropertyMapMetaObject : public QQmlOpenMetaObject
52{
53public:
54 QQmlPropertyMapMetaObject(QQmlPropertyMap *obj, QQmlPropertyMapPrivate *objPriv, const QMetaObject *staticMetaObject);
55
56protected:
57 QVariant propertyWriteValue(int, const QVariant &) override;
58 void propertyWritten(int index) override;
59 void propertyCreated(int, QMetaPropertyBuilder &) override;
60 int createProperty(const char *, const char *) override;
61
62 const QString &propertyName(int index);
63
64private:
65 QQmlPropertyMap *map;
66 QQmlPropertyMapPrivate *priv;
67};
68
69class QQmlPropertyMapPrivate : public QObjectPrivate
70{
71 Q_DECLARE_PUBLIC(QQmlPropertyMap)
72public:
73 QQmlPropertyMapMetaObject *mo;
74 QStringList keys;
75
76 QVariant updateValue(const QString &key, const QVariant &input);
77 void emitChanged(const QString &key, const QVariant &value);
78 bool validKeyName(const QString& name);
79
80 const QString &propertyName(int index) const;
81};
82
83bool QQmlPropertyMapPrivate::validKeyName(const QString& name)
84{
85 //The following strings shouldn't be used as property names
86 return name != QLatin1String("keys")
87 && name != QLatin1String("valueChanged")
88 && name != QLatin1String("QObject")
89 && name != QLatin1String("destroyed")
90 && name != QLatin1String("deleteLater");
91}
92
93QVariant QQmlPropertyMapPrivate::updateValue(const QString &key, const QVariant &input)
94{
95 Q_Q(QQmlPropertyMap);
96 return q->updateValue(key, input);
97}
98
99void QQmlPropertyMapPrivate::emitChanged(const QString &key, const QVariant &value)
100{
101 Q_Q(QQmlPropertyMap);
102 emit q->valueChanged(key, value);
103}
104
105const QString &QQmlPropertyMapPrivate::propertyName(int index) const
106{
107 Q_ASSERT(index < keys.size());
108 return keys[index];
109}
110
111QQmlPropertyMapMetaObject::QQmlPropertyMapMetaObject(QQmlPropertyMap *obj, QQmlPropertyMapPrivate *objPriv, const QMetaObject *staticMetaObject)
112 : QQmlOpenMetaObject(obj, staticMetaObject)
113{
114 map = obj;
115 priv = objPriv;
116}
117
118QVariant QQmlPropertyMapMetaObject::propertyWriteValue(int index, const QVariant &input)
119{
120 return priv->updateValue(key: priv->propertyName(index), input);
121}
122
123void QQmlPropertyMapMetaObject::propertyWritten(int index)
124{
125 priv->emitChanged(key: priv->propertyName(index), value: value(index));
126}
127
128void QQmlPropertyMapMetaObject::propertyCreated(int, QMetaPropertyBuilder &b)
129{
130 priv->keys.append(t: QString::fromUtf8(str: b.name()));
131}
132
133int QQmlPropertyMapMetaObject::createProperty(const char *name, const char *value)
134{
135 if (!priv->validKeyName(name: QString::fromUtf8(str: name)))
136 return -1;
137 return QQmlOpenMetaObject::createProperty(name, value);
138}
139
140/*!
141 \class QQmlPropertyMap
142 \brief The QQmlPropertyMap class allows you to set key-value pairs that can be used in QML bindings.
143 \inmodule QtQml
144
145 QQmlPropertyMap provides a convenient way to expose domain data to the UI layer.
146 The following example shows how you might declare data in C++ and then
147 access it in QML.
148
149 In the C++ file:
150 \code
151 // create our data
152 QQmlPropertyMap ownerData;
153 ownerData.insert("name", QVariant(QString("John Smith")));
154 ownerData.insert("phone", QVariant(QString("555-5555")));
155
156 // expose it to the UI layer
157 QQuickView view;
158 QQmlContext *ctxt = view.rootContext();
159 ctxt->setContextProperty("owner", &ownerData);
160
161 view.setSource(QUrl::fromLocalFile("main.qml"));
162 view.show();
163 \endcode
164
165 Then, in \c main.qml:
166 \code
167 Text { text: owner.name + " " + owner.phone }
168 \endcode
169
170 The binding is dynamic - whenever a key's value is updated, anything bound to that
171 key will be updated as well.
172
173 To detect value changes made in the UI layer you can connect to the valueChanged() signal.
174 However, note that valueChanged() is \b NOT emitted when changes are made by calling insert()
175 or clear() - it is only emitted when a value is updated from QML.
176
177 \note It is not possible to remove keys from the map; once a key has been added, you can only
178 modify or clear its associated value.
179
180 \note When deriving a class from QQmlPropertyMap, use the
181 \l {QQmlPropertyMap::QQmlPropertyMap(DerivedType *derived, QObject *parent)} {protected two-argument constructor}
182 which ensures that the class is correctly registered with the Qt \l {Meta-Object System}.
183
184 \note The QMetaObject of a QQmlPropertyMap is dynamically generated and modified.
185 Operations on that meta object are not thread safe, so applications need to take
186 care to explicitly synchronize access to the meta object.
187*/
188
189/*!
190 Constructs a bindable map with parent object \a parent.
191*/
192QQmlPropertyMap::QQmlPropertyMap(QObject *parent)
193: QQmlPropertyMap(&staticMetaObject, parent)
194{
195}
196
197/*!
198 Destroys the bindable map.
199*/
200QQmlPropertyMap::~QQmlPropertyMap()
201{
202}
203
204/*!
205 Clears the value (if any) associated with \a key.
206*/
207void QQmlPropertyMap::clear(const QString &key)
208{
209 Q_D(QQmlPropertyMap);
210 d->mo->setValue(key.toUtf8(), QVariant());
211}
212
213/*!
214 Returns the value associated with \a key.
215
216 If no value has been set for this key (or if the value has been cleared),
217 an invalid QVariant is returned.
218*/
219QVariant QQmlPropertyMap::value(const QString &key) const
220{
221 Q_D(const QQmlPropertyMap);
222 return d->mo->value(key.toUtf8());
223}
224
225/*!
226 Sets the value associated with \a key to \a value.
227
228 If the key doesn't exist, it is automatically created.
229*/
230void QQmlPropertyMap::insert(const QString &key, const QVariant &value)
231{
232 Q_D(QQmlPropertyMap);
233
234 if (d->validKeyName(name: key)) {
235 d->mo->setValue(key.toUtf8(), value);
236 } else {
237 qWarning() << "Creating property with name"
238 << key
239 << "is not permitted, conflicts with internal symbols.";
240 }
241}
242
243/*!
244 Returns the list of keys.
245
246 Keys that have been cleared will still appear in this list, even though their
247 associated values are invalid QVariants.
248*/
249QStringList QQmlPropertyMap::keys() const
250{
251 Q_D(const QQmlPropertyMap);
252 return d->keys;
253}
254
255/*!
256 \overload
257
258 Same as size().
259*/
260int QQmlPropertyMap::count() const
261{
262 Q_D(const QQmlPropertyMap);
263 return d->keys.count();
264}
265
266/*!
267 Returns the number of keys in the map.
268
269 \sa isEmpty(), count()
270*/
271int QQmlPropertyMap::size() const
272{
273 Q_D(const QQmlPropertyMap);
274 return d->keys.size();
275}
276
277/*!
278 Returns true if the map contains no keys; otherwise returns
279 false.
280
281 \sa size()
282*/
283bool QQmlPropertyMap::isEmpty() const
284{
285 Q_D(const QQmlPropertyMap);
286 return d->keys.isEmpty();
287}
288
289/*!
290 Returns true if the map contains \a key.
291
292 \sa size()
293*/
294bool QQmlPropertyMap::contains(const QString &key) const
295{
296 Q_D(const QQmlPropertyMap);
297 return d->keys.contains(str: key);
298}
299
300/*!
301 Returns the value associated with the key \a key as a modifiable
302 reference.
303
304 If the map contains no item with key \a key, the function inserts
305 an invalid QVariant into the map with key \a key, and
306 returns a reference to it.
307
308 \sa insert(), value()
309*/
310QVariant &QQmlPropertyMap::operator[](const QString &key)
311{
312 //### optimize
313 Q_D(QQmlPropertyMap);
314 QByteArray utf8key = key.toUtf8();
315 if (!d->keys.contains(str: key))
316 insert(key, value: QVariant());//force creation -- needed below
317
318 return d->mo->valueRef(utf8key);
319}
320
321/*!
322 \overload
323
324 Same as value().
325*/
326QVariant QQmlPropertyMap::operator[](const QString &key) const
327{
328 return value(key);
329}
330
331/*!
332 Returns the new value to be stored for the key \a key. This function is provided
333 to intercept updates to a property from QML, where the value provided from QML is \a input.
334
335 Override this function to manipulate the property value as it is updated. Note that
336 this function is only invoked when the value is updated from QML.
337*/
338QVariant QQmlPropertyMap::updateValue(const QString &key, const QVariant &input)
339{
340 Q_UNUSED(key)
341 return input;
342}
343
344/*! \internal */
345QQmlPropertyMap::QQmlPropertyMap(const QMetaObject *staticMetaObject, QObject *parent)
346 : QObject(*(new QQmlPropertyMapPrivate), parent)
347{
348 Q_D(QQmlPropertyMap);
349 d->mo = new QQmlPropertyMapMetaObject(this, d, staticMetaObject);
350}
351
352/*!
353 \fn void QQmlPropertyMap::valueChanged(const QString &key, const QVariant &value)
354 This signal is emitted whenever one of the values in the map is changed. \a key
355 is the key corresponding to the \a value that was changed.
356
357 \note valueChanged() is \b NOT emitted when changes are made by calling insert()
358 or clear() - it is only emitted when a value is updated from QML.
359*/
360
361/*!
362 \fn template<class DerivedType> QQmlPropertyMap::QQmlPropertyMap(DerivedType *derived, QObject *parent)
363
364 Constructs a bindable map with parent object \a parent. Use this constructor
365 in classes derived from QQmlPropertyMap.
366
367 The type of \a derived is used to register the property map with the \l {Meta-Object System},
368 which is necessary to ensure that properties of the derived class are accessible.
369 This type must be derived from QQmlPropertyMap.
370*/
371
372QT_END_NAMESPACE
373

source code of qtdeclarative/src/qml/util/qqmlpropertymap.cpp