Quassel IRC  Pre-Release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
syncableobject.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005-2015 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 
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 
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 
68 {
69  if (this == &other)
70  return *this;
71 
72  _initialized = other._initialized;
74  return *this;
75 }
76 
77 
79 {
80  return _initialized;
81 }
82 
83 
85 {
86  _initialized = true;
87  emit initDone();
88 }
89 
90 
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 
222 {
223  if (_signalProxies.contains(proxy))
224  return;
225  _signalProxies << proxy;
226 }
227 
228 
230 {
231  for (int i = 0; i < _signalProxies.count(); i++) {
232  if (_signalProxies[i] == proxy) {
233  _signalProxies.removeAt(i);
234  break;
235  }
236  }
237 }