1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the Qt Solutions component.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
10 **
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
13 ** met:
14 ** * Redistributions of source code must retain the above copyright
15 ** notice, this list of conditions and the following disclaimer.
16 ** * Redistributions in binary form must reproduce the above copyright
17 ** notice, this list of conditions and the following disclaimer in
18 ** the documentation and/or other materials provided with the
19 ** distribution.
20 ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
21 ** of its contributors may be used to endorse or promote products derived
22 ** from this software without specific prior written permission.
23 **
24 **
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40
41 #include <QMetaObject>
42 #include <QMetaProperty>
43 #include <QVBoxLayout>
44 #include <QScrollArea>
45 #include "objectcontroller.h"
46 #include "qtvariantproperty.h"
47 #include "qtgroupboxpropertybrowser.h"
48 #include "qttreepropertybrowser.h"
49 #include "qtpropertybrowser.h"
50
51 class ObjectControllerPrivate
52 {
53 ObjectController *q_ptr;
54 Q_DECLARE_PUBLIC(ObjectController)
55 public:
56
57 void addClassProperties(const QMetaObject *metaObject);
58 void updateClassProperties(const QMetaObject *metaObject, bool recursive);
59 void saveExpandedState();
60 void restoreExpandedState();
61 void slotValueChanged(QtProperty *property, const QVariant &value);
62 int enumToInt(const QMetaEnum &metaEnum, int enumValue) const;
63 int intToEnum(const QMetaEnum &metaEnum, int intValue) const;
64 int flagToInt(const QMetaEnum &metaEnum, int flagValue) const;
65 int intToFlag(const QMetaEnum &metaEnum, int intValue) const;
66 bool isSubValue(int value, int subValue) const;
67 bool isPowerOf2(int value) const;
68
69 QObject *m_object;
70
71 QMap<const QMetaObject *, QtProperty *> m_classToProperty;
72 QMap<QtProperty *, const QMetaObject *> m_propertyToClass;
73 QMap<QtProperty *, int> m_propertyToIndex;
74 QMap<const QMetaObject *, QMap<int, QtVariantProperty *> > m_classToIndexToProperty;
75
76 QMap<QtProperty *, bool> m_propertyToExpanded;
77
78 QList<QtProperty *> m_topLevelProperties;
79
80 QtAbstractPropertyBrowser *m_browser;
81 QtVariantPropertyManager *m_manager;
82 QtVariantPropertyManager *m_readOnlyManager;
83 };
84
enumToInt(const QMetaEnum & metaEnum,int enumValue) const85 int ObjectControllerPrivate::enumToInt(const QMetaEnum &metaEnum, int enumValue) const
86 {
87 QMap<int, int> valueMap; // dont show multiple enum values which have the same values
88 int pos = 0;
89 for (int i = 0; i < metaEnum.keyCount(); i++) {
90 int value = metaEnum.value(i);
91 if (!valueMap.contains(value)) {
92 if (value == enumValue)
93 return pos;
94 valueMap[value] = pos++;
95 }
96 }
97 return -1;
98 }
99
intToEnum(const QMetaEnum & metaEnum,int intValue) const100 int ObjectControllerPrivate::intToEnum(const QMetaEnum &metaEnum, int intValue) const
101 {
102 QMap<int, bool> valueMap; // dont show multiple enum values which have the same values
103 QList<int> values;
104 for (int i = 0; i < metaEnum.keyCount(); i++) {
105 int value = metaEnum.value(i);
106 if (!valueMap.contains(value)) {
107 valueMap[value] = true;
108 values.append(value);
109 }
110 }
111 if (intValue >= values.count())
112 return -1;
113 return values.at(intValue);
114 }
115
isSubValue(int value,int subValue) const116 bool ObjectControllerPrivate::isSubValue(int value, int subValue) const
117 {
118 if (value == subValue)
119 return true;
120 int i = 0;
121 while (subValue) {
122 if (!(value & (1 << i))) {
123 if (subValue & 1)
124 return false;
125 }
126 i++;
127 subValue = subValue >> 1;
128 }
129 return true;
130 }
131
isPowerOf2(int value) const132 bool ObjectControllerPrivate::isPowerOf2(int value) const
133 {
134 while (value) {
135 if (value & 1) {
136 return value == 1;
137 }
138 value = value >> 1;
139 }
140 return false;
141 }
142
flagToInt(const QMetaEnum & metaEnum,int flagValue) const143 int ObjectControllerPrivate::flagToInt(const QMetaEnum &metaEnum, int flagValue) const
144 {
145 if (!flagValue)
146 return 0;
147 int intValue = 0;
148 QMap<int, int> valueMap; // dont show multiple enum values which have the same values
149 int pos = 0;
150 for (int i = 0; i < metaEnum.keyCount(); i++) {
151 int value = metaEnum.value(i);
152 if (!valueMap.contains(value) && isPowerOf2(value)) {
153 if (isSubValue(flagValue, value))
154 intValue |= (1 << pos);
155 valueMap[value] = pos++;
156 }
157 }
158 return intValue;
159 }
160
intToFlag(const QMetaEnum & metaEnum,int intValue) const161 int ObjectControllerPrivate::intToFlag(const QMetaEnum &metaEnum, int intValue) const
162 {
163 QMap<int, bool> valueMap; // dont show multiple enum values which have the same values
164 QList<int> values;
165 for (int i = 0; i < metaEnum.keyCount(); i++) {
166 int value = metaEnum.value(i);
167 if (!valueMap.contains(value) && isPowerOf2(value)) {
168 valueMap[value] = true;
169 values.append(value);
170 }
171 }
172 int flagValue = 0;
173 int temp = intValue;
174 int i = 0;
175 while (temp) {
176 if (i >= values.count())
177 return -1;
178 if (temp & 1)
179 flagValue |= values.at(i);
180 i++;
181 temp = temp >> 1;
182 }
183 return flagValue;
184 }
185
updateClassProperties(const QMetaObject * metaObject,bool recursive)186 void ObjectControllerPrivate::updateClassProperties(const QMetaObject *metaObject, bool recursive)
187 {
188 if (!metaObject)
189 return;
190
191 if (recursive)
192 updateClassProperties(metaObject->superClass(), recursive);
193
194 QtProperty *classProperty = m_classToProperty.value(metaObject);
195 if (!classProperty)
196 return;
197
198 for (int idx = metaObject->propertyOffset(); idx < metaObject->propertyCount(); idx++) {
199 QMetaProperty metaProperty = metaObject->property(idx);
200 if (metaProperty.isReadable()) {
201 if (m_classToIndexToProperty.contains(metaObject) && m_classToIndexToProperty[metaObject].contains(idx)) {
202 QtVariantProperty *subProperty = m_classToIndexToProperty[metaObject][idx];
203 if (metaProperty.isEnumType()) {
204 if (metaProperty.isFlagType())
205 subProperty->setValue(flagToInt(metaProperty.enumerator(), metaProperty.read(m_object).toInt()));
206 else
207 subProperty->setValue(enumToInt(metaProperty.enumerator(), metaProperty.read(m_object).toInt()));
208 } else {
209 subProperty->setValue(metaProperty.read(m_object));
210 }
211 }
212 }
213 }
214 }
215
addClassProperties(const QMetaObject * metaObject)216 void ObjectControllerPrivate::addClassProperties(const QMetaObject *metaObject)
217 {
218 if (!metaObject)
219 return;
220
221 addClassProperties(metaObject->superClass());
222
223 QtProperty *classProperty = m_classToProperty.value(metaObject);
224 if (!classProperty) {
225 QString className = QLatin1String(metaObject->className());
226 classProperty = m_manager->addProperty(QtVariantPropertyManager::groupTypeId(), className);
227 m_classToProperty[metaObject] = classProperty;
228 m_propertyToClass[classProperty] = metaObject;
229
230 for (int idx = metaObject->propertyOffset(); idx < metaObject->propertyCount(); idx++) {
231 QMetaProperty metaProperty = metaObject->property(idx);
232 int type = metaProperty.userType();
233 QtVariantProperty *subProperty = 0;
234 if (!metaProperty.isReadable()) {
235 subProperty = m_readOnlyManager->addProperty(QVariant::String, QLatin1String(metaProperty.name()));
236 subProperty->setValue(QLatin1String("< Non Readable >"));
237 } else if (metaProperty.isEnumType()) {
238 if (metaProperty.isFlagType()) {
239 subProperty = m_manager->addProperty(QtVariantPropertyManager::flagTypeId(), QLatin1String(metaProperty.name()));
240 QMetaEnum metaEnum = metaProperty.enumerator();
241 QMap<int, bool> valueMap;
242 QStringList flagNames;
243 for (int i = 0; i < metaEnum.keyCount(); i++) {
244 int value = metaEnum.value(i);
245 if (!valueMap.contains(value) && isPowerOf2(value)) {
246 valueMap[value] = true;
247 flagNames.append(QLatin1String(metaEnum.key(i)));
248 }
249 subProperty->setAttribute(QLatin1String("flagNames"), flagNames);
250 subProperty->setValue(flagToInt(metaEnum, metaProperty.read(m_object).toInt()));
251 }
252 } else {
253 subProperty = m_manager->addProperty(QtVariantPropertyManager::enumTypeId(), QLatin1String(metaProperty.name()));
254 QMetaEnum metaEnum = metaProperty.enumerator();
255 QMap<int, bool> valueMap; // dont show multiple enum values which have the same values
256 QStringList enumNames;
257 for (int i = 0; i < metaEnum.keyCount(); i++) {
258 int value = metaEnum.value(i);
259 if (!valueMap.contains(value)) {
260 valueMap[value] = true;
261 enumNames.append(QLatin1String(metaEnum.key(i)));
262 }
263 }
264 subProperty->setAttribute(QLatin1String("enumNames"), enumNames);
265 subProperty->setValue(enumToInt(metaEnum, metaProperty.read(m_object).toInt()));
266 }
267 } else if (m_manager->isPropertyTypeSupported(type)) {
268 if (!metaProperty.isWritable())
269 subProperty = m_readOnlyManager->addProperty(type, QLatin1String(metaProperty.name()) + QLatin1String(" (Non Writable)"));
270 if (!metaProperty.isDesignable())
271 subProperty = m_readOnlyManager->addProperty(type, QLatin1String(metaProperty.name()) + QLatin1String(" (Non Designable)"));
272 else
273 subProperty = m_manager->addProperty(type, QLatin1String(metaProperty.name()));
274 subProperty->setValue(metaProperty.read(m_object));
275 } else {
276 subProperty = m_readOnlyManager->addProperty(QVariant::String, QLatin1String(metaProperty.name()));
277 subProperty->setValue(QLatin1String("< Unknown Type >"));
278 subProperty->setEnabled(false);
279 }
280 classProperty->addSubProperty(subProperty);
281 m_propertyToIndex[subProperty] = idx;
282 m_classToIndexToProperty[metaObject][idx] = subProperty;
283 }
284 } else {
285 updateClassProperties(metaObject, false);
286 }
287
288 m_topLevelProperties.append(classProperty);
289 m_browser->addProperty(classProperty);
290 }
291
saveExpandedState()292 void ObjectControllerPrivate::saveExpandedState()
293 {
294
295 }
296
restoreExpandedState()297 void ObjectControllerPrivate::restoreExpandedState()
298 {
299
300 }
301
slotValueChanged(QtProperty * property,const QVariant & value)302 void ObjectControllerPrivate::slotValueChanged(QtProperty *property, const QVariant &value)
303 {
304 if (!m_propertyToIndex.contains(property))
305 return;
306
307 int idx = m_propertyToIndex.value(property);
308
309 const QMetaObject *metaObject = m_object->metaObject();
310 QMetaProperty metaProperty = metaObject->property(idx);
311 if (metaProperty.isEnumType()) {
312 if (metaProperty.isFlagType())
313 metaProperty.write(m_object, intToFlag(metaProperty.enumerator(), value.toInt()));
314 else
315 metaProperty.write(m_object, intToEnum(metaProperty.enumerator(), value.toInt()));
316 } else {
317 metaProperty.write(m_object, value);
318 }
319
320 updateClassProperties(metaObject, true);
321 }
322
323 ///////////////////
324
ObjectController(QWidget * parent)325 ObjectController::ObjectController(QWidget *parent)
326 : QWidget(parent)
327 {
328 d_ptr = new ObjectControllerPrivate;
329 d_ptr->q_ptr = this;
330
331 d_ptr->m_object = 0;
332 /*
333 QScrollArea *scroll = new QScrollArea(this);
334 scroll->setWidgetResizable(true);
335
336 d_ptr->m_browser = new QtGroupBoxPropertyBrowser(this);
337 QVBoxLayout *layout = new QVBoxLayout(this);
338 layout->setMargin(0);
339 layout->addWidget(scroll);
340 scroll->setWidget(d_ptr->m_browser);
341 */
342 QtTreePropertyBrowser *browser = new QtTreePropertyBrowser(this);
343 browser->setRootIsDecorated(false);
344 d_ptr->m_browser = browser;
345 QVBoxLayout *layout = new QVBoxLayout(this);
346 layout->setMargin(0);
347 layout->addWidget(d_ptr->m_browser);
348
349 d_ptr->m_readOnlyManager = new QtVariantPropertyManager(this);
350 d_ptr->m_manager = new QtVariantPropertyManager(this);
351 QtVariantEditorFactory *factory = new QtVariantEditorFactory(this);
352 d_ptr->m_browser->setFactoryForManager(d_ptr->m_manager, factory);
353
354 connect(d_ptr->m_manager, SIGNAL(valueChanged(QtProperty *, const QVariant &)),
355 this, SLOT(slotValueChanged(QtProperty *, const QVariant &)));
356 }
357
~ObjectController()358 ObjectController::~ObjectController()
359 {
360 delete d_ptr;
361 }
362
setObject(QObject * object)363 void ObjectController::setObject(QObject *object)
364 {
365 if (d_ptr->m_object == object)
366 return;
367
368 if (d_ptr->m_object) {
369 d_ptr->saveExpandedState();
370 QListIterator<QtProperty *> it(d_ptr->m_topLevelProperties);
371 while (it.hasNext()) {
372 d_ptr->m_browser->removeProperty(it.next());
373 }
374 d_ptr->m_topLevelProperties.clear();
375 }
376
377 d_ptr->m_object = object;
378
379 if (!d_ptr->m_object)
380 return;
381
382 d_ptr->addClassProperties(d_ptr->m_object->metaObject());
383
384 d_ptr->restoreExpandedState();
385 }
386
object() const387 QObject *ObjectController::object() const
388 {
389 return d_ptr->m_object;
390 }
391
392 #include "moc_objectcontroller.cpp"
393