1 | /*************************************************************************** |
2 | * Copyright (C) 2005-2014 by the Quassel Project * |
3 | * devel@quassel-irc.org * |
4 | * * |
5 | * This program is free software; you can redistribute it and/or modify * |
6 | * it under the terms of the GNU General Public License as published by * |
7 | * the Free Software Foundation; either version 2 of the License, or * |
8 | * (at your option) version 3. * |
9 | * * |
10 | * This program is distributed in the hope that it will be useful, * |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
13 | * GNU General Public License for more details. * |
14 | * * |
15 | * You should have received a copy of the GNU General Public License * |
16 | * along with this program; if not, write to the * |
17 | * Free Software Foundation, Inc., * |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
19 | ***************************************************************************/ |
20 | |
21 | #include <QMetaProperty> |
22 | |
23 | #include <QDebug> |
24 | |
25 | #include "syncableobject.h" |
26 | |
27 | #include "signalproxy.h" |
28 | #include "util.h" |
29 | |
30 | INIT_SYNCABLE_OBJECT(SyncableObject) |
31 | SyncableObject::SyncableObject(QObject *parent) |
32 | : QObject(parent), |
33 | _initialized(false), |
34 | _allowClientUpdates(false) |
35 | { |
36 | } |
37 | |
38 | |
39 | SyncableObject::SyncableObject(const QString &objectName, QObject *parent) |
40 | : QObject(parent), |
41 | _initialized(false), |
42 | _allowClientUpdates(false) |
43 | { |
44 | setObjectName(objectName); |
45 | } |
46 | |
47 | |
48 | SyncableObject::SyncableObject(const SyncableObject &other, QObject *parent) |
49 | : QObject(parent), |
50 | _initialized(other._initialized), |
51 | _allowClientUpdates(other._allowClientUpdates) |
52 | { |
53 | } |
54 | |
55 | |
56 | SyncableObject::~SyncableObject() |
57 | { |
58 | QList<SignalProxy *>::iterator proxyIter = _signalProxies.begin(); |
59 | while (proxyIter != _signalProxies.end()) { |
60 | SignalProxy *proxy = (*proxyIter); |
61 | proxyIter = _signalProxies.erase(proxyIter); |
62 | proxy->stopSynchronize(this); |
63 | } |
64 | } |
65 | |
66 | |
67 | SyncableObject &SyncableObject::operator=(const SyncableObject &other) |
68 | { |
69 | if (this == &other) |
70 | return *this; |
71 | |
72 | _initialized = other._initialized; |
73 | _allowClientUpdates = other._allowClientUpdates; |
74 | return *this; |
75 | } |
76 | |
77 | |
78 | bool SyncableObject::isInitialized() const |
79 | { |
80 | return _initialized; |
81 | } |
82 | |
83 | |
84 | void SyncableObject::setInitialized() |
85 | { |
86 | _initialized = true; |
87 | emit initDone(); |
88 | } |
89 | |
90 | |
91 | QVariantMap SyncableObject::toVariantMap() |
92 | { |
93 | QVariantMap properties; |
94 | |
95 | const QMetaObject *meta = metaObject(); |
96 | |
97 | // we collect data from properties |
98 | QMetaProperty prop; |
99 | QString propName; |
100 | for (int i = 0; i < meta->propertyCount(); i++) { |
101 | prop = meta->property(i); |
102 | propName = QString(prop.name()); |
103 | if (propName == "objectName" ) |
104 | continue; |
105 | properties[propName] = prop.read(this); |
106 | } |
107 | |
108 | // ...as well as methods, which have names starting with "init" |
109 | for (int i = 0; i < meta->methodCount(); i++) { |
110 | QMetaMethod method = meta->method(i); |
111 | QString methodname(SignalProxy::ExtendedMetaObject::methodName(method)); |
112 | if (!methodname.startsWith("init" ) || methodname.startsWith("initSet" ) || methodname.startsWith("initDone" )) |
113 | continue; |
114 | |
115 | QVariant::Type variantType = QVariant::nameToType(method.typeName()); |
116 | if (variantType == QVariant::Invalid && !QByteArray(method.typeName()).isEmpty()) { |
117 | #if QT_VERSION >= 0x050000 |
118 | qWarning() << "SyncableObject::toVariantMap(): cannot fetch init data for:" << this << method.methodSignature() << "- Returntype is unknown to Qt's MetaSystem:" << QByteArray(method.typeName()); |
119 | #else |
120 | qWarning() << "SyncableObject::toVariantMap(): cannot fetch init data for:" << this << method.signature() << "- Returntype is unknown to Qt's MetaSystem:" << QByteArray(method.typeName()); |
121 | #endif |
122 | continue; |
123 | } |
124 | |
125 | QVariant value(variantType, (const void *)0); |
126 | QGenericReturnArgument genericvalue = QGenericReturnArgument(method.typeName(), value.data()); |
127 | QMetaObject::invokeMethod(this, methodname.toLatin1(), genericvalue); |
128 | |
129 | properties[SignalProxy::ExtendedMetaObject::methodBaseName(method)] = value; |
130 | } |
131 | return properties; |
132 | } |
133 | |
134 | |
135 | void SyncableObject::fromVariantMap(const QVariantMap &properties) |
136 | { |
137 | const QMetaObject *meta = metaObject(); |
138 | |
139 | QVariantMap::const_iterator iterator = properties.constBegin(); |
140 | QString propName; |
141 | while (iterator != properties.constEnd()) { |
142 | propName = iterator.key(); |
143 | if (propName == "objectName" ) { |
144 | iterator++; |
145 | continue; |
146 | } |
147 | |
148 | int propertyIndex = meta->indexOfProperty(propName.toLatin1()); |
149 | |
150 | if (propertyIndex == -1 || !meta->property(propertyIndex).isWritable()) |
151 | setInitValue(propName, iterator.value()); |
152 | else |
153 | setProperty(propName.toLatin1(), iterator.value()); |
154 | // qDebug() << "<<< SYNC:" << name << iterator.value(); |
155 | iterator++; |
156 | } |
157 | } |
158 | |
159 | |
160 | bool SyncableObject::setInitValue(const QString &property, const QVariant &value) |
161 | { |
162 | QString handlername = QString("initSet" ) + property; |
163 | handlername[7] = handlername[7].toUpper(); |
164 | |
165 | QString methodSignature = QString("%1(%2)" ).arg(handlername).arg(value.typeName()); |
166 | int methodIdx = metaObject()->indexOfMethod(methodSignature.toLatin1().constData()); |
167 | if (methodIdx < 0) { |
168 | QByteArray normedMethodName = QMetaObject::normalizedSignature(methodSignature.toLatin1().constData()); |
169 | methodIdx = metaObject()->indexOfMethod(normedMethodName.constData()); |
170 | } |
171 | if (methodIdx < 0) { |
172 | return false; |
173 | } |
174 | |
175 | QGenericArgument param(value.typeName(), value.constData()); |
176 | return QMetaObject::invokeMethod(this, handlername.toLatin1(), param); |
177 | } |
178 | |
179 | |
180 | void SyncableObject::renameObject(const QString &newName) |
181 | { |
182 | const QString oldName = objectName(); |
183 | if (oldName != newName) { |
184 | setObjectName(newName); |
185 | foreach(SignalProxy *proxy, _signalProxies) { |
186 | proxy->renameObject(this, newName, oldName); |
187 | } |
188 | } |
189 | } |
190 | |
191 | |
192 | void SyncableObject::update(const QVariantMap &properties) |
193 | { |
194 | fromVariantMap(properties); |
195 | SYNC(ARG(properties)) |
196 | emit updated(); |
197 | } |
198 | |
199 | |
200 | void SyncableObject::requestUpdate(const QVariantMap &properties) |
201 | { |
202 | if (allowClientUpdates()) { |
203 | update(properties); |
204 | } |
205 | REQUEST(ARG(properties)) |
206 | } |
207 | |
208 | |
209 | void SyncableObject::sync_call__(SignalProxy::ProxyMode modeType, const char *funcname, ...) const |
210 | { |
211 | //qDebug() << Q_FUNC_INFO << modeType << funcname; |
212 | foreach(SignalProxy *proxy, _signalProxies) { |
213 | va_list ap; |
214 | va_start(ap, funcname); |
215 | proxy->sync_call__(this, modeType, funcname, ap); |
216 | va_end(ap); |
217 | } |
218 | } |
219 | |
220 | |
221 | void SyncableObject::synchronize(SignalProxy *proxy) |
222 | { |
223 | if (_signalProxies.contains(proxy)) |
224 | return; |
225 | _signalProxies << proxy; |
226 | } |
227 | |
228 | |
229 | void SyncableObject::stopSynchronize(SignalProxy *proxy) |
230 | { |
231 | for (int i = 0; i < _signalProxies.count(); i++) { |
232 | if (_signalProxies[i] == proxy) { |
233 | _signalProxies.removeAt(i); |
234 | break; |
235 | } |
236 | } |
237 | } |
238 | |