-void SignalProxy::detachSlots(QObject* receiver) {
- SlotHash::iterator slotIter = _attachedSlots.begin();
- while(slotIter != _attachedSlots.end()) {
- if(slotIter.value().first == receiver) {
- slotIter = _attachedSlots.erase(slotIter);
- } else
- slotIter++;
- }
-}
-
-void SignalProxy::stopSync(SyncableObject* obj) {
- if(_relayHash.contains(obj))
- _relayHash[obj]->setSynchronize(false);
-
- // we can't use a className here, since it might be effed up, if we receive the call as a result of a decon
- // gladly the objectName() is still valid. So we have only to iterate over the classes not each instance! *sigh*
- QHash<QByteArray, ObjectId>::iterator classIter = _syncSlave.begin();
- while(classIter != _syncSlave.end()) {
- if(classIter->contains(obj->objectName()) && classIter.value()[obj->objectName()] == obj) {
- classIter->remove(obj->objectName());
- break;
- }
- classIter++;
- }
-}
-
-void SignalProxy::dispatchSignal(QIODevice *receiver, const RequestType &requestType, const QVariantList ¶ms) {
- QVariantList packedFunc;
- packedFunc << (qint16)requestType;
- packedFunc << params;
- writeDataToDevice(receiver, QVariant(packedFunc));
-}
-
-void SignalProxy::dispatchSignal(const RequestType &requestType, const QVariantList ¶ms) {
- // yes I know we have a little code duplication here... it's for the sake of performance
- QVariantList packedFunc;
- packedFunc << (qint16)requestType;
- packedFunc << params;
- foreach(QIODevice* dev, _peerByteCount.keys())
- writeDataToDevice(dev, QVariant(packedFunc));
-}
-
-void SignalProxy::receivePeerSignal(QIODevice *sender, const QVariant &packedFunc) {
- QVariantList params(packedFunc.toList());
-
- int callType = params.takeFirst().value<int>();
-
- switch(callType) {
- case RpcCall:
- if(params.empty()) {
- qWarning() << "SignalProxy::receivePeerSignal(): received empty RPC-Call";
- return;
- } else {
- return handleSignal(params.takeFirst().toByteArray(), params);
- }
- case Sync:
- return handleSync(params);
- case InitRequest:
- return handleInitRequest(sender, params);
- case InitData:
- return handleInitData(sender, params);
- case HeartBeat:
- return;
- default:
- qWarning() << "SignalProxy::receivePeerSignal(): received undefined CallType" << callType << params;
- return;
- }
-}
-
-void SignalProxy::handleSync(QVariantList params) {
- if(params.count() < 3) {
- qWarning() << "received invalid Sync call" << params;
- return;
- }
-
- QByteArray className = params.takeFirst().toByteArray();
- QString objectName = params.takeFirst().toString();
- QByteArray signal = params.takeFirst().toByteArray();
-
- if(!_syncSlave.contains(className) || !_syncSlave[className].contains(objectName)) {
- qWarning() << QString("no registered receiver for sync call: %1::%2 (objectName=\"%3\"). Params are:").arg(QString(className)).arg(QString(signal)).arg(objectName)
- << params;
- return;
- }
-
- SyncableObject *receiver = _syncSlave[className][objectName];
- if(!syncMap(receiver).contains(signal)) {
- qWarning() << QString("no matching slot for sync call: %1::%2 (objectName=\"%3\"). Params are:").arg(QString(className)).arg(QString(signal)).arg(objectName)
- << params;
- return;
- }
-
- int slotId = syncMap(receiver)[signal];
- if(!invokeSlot(receiver, slotId, params)) {
- qWarning("SignalProxy::handleSync(): invokeMethod for \"%s\" failed ", methodName(receiver, slotId).constData());
- return;
- }
- invokeSlot(receiver, updatedRemotelyId(receiver));
-}
-
-void SignalProxy::handleInitRequest(QIODevice *sender, const QVariantList ¶ms) {
- if(params.count() != 2) {
- qWarning() << "SignalProxy::handleInitRequest() received initRequest with invalid param Count:"
- << params;
- return;
- }
-
- QByteArray className(params[0].toByteArray());
- QString objectName(params[1].toString());
-
- if(!_syncSlave.contains(className)) {
- qWarning() << "SignalProxy::handleInitRequest() received initRequest for unregistered Class:"
- << className;
- return;
- }
-
- if(!_syncSlave[className].contains(objectName)) {
- qWarning() << "SignalProxy::handleInitRequest() received initRequest for unregistered Object:"
- << className << objectName;
- return;
- }
-
- SyncableObject *obj = _syncSlave[className][objectName];
-
- QVariantList params_;
- params_ << obj->metaObject()->className()
- << obj->objectName()
- << initData(obj);
-
- dispatchSignal(sender, InitData, params_);
-}
-
-void SignalProxy::handleInitData(QIODevice *sender, const QVariantList ¶ms) {
- Q_UNUSED(sender)
- if(params.count() != 3) {
- qWarning() << "SignalProxy::handleInitData() received initData with invalid param Count:"
- << params;
- return;
- }
-
- QByteArray className(params[0].toByteArray());
- QString objectName(params[1].toString());
- QVariantMap propertyMap(params[2].toMap());
-
- if(!_syncSlave.contains(className)) {
- qWarning() << "SignalProxy::handleInitData() received initData for unregistered Class:"
- << className;
- return;
- }
-
- if(!_syncSlave[className].contains(objectName)) {
- qWarning() << "SignalProxy::handleInitData() received initData for unregistered Object:"
- << className << objectName;
- return;
- }
-
- SyncableObject *obj = _syncSlave[className][objectName];
- setInitData(obj, propertyMap);
-}
-
-void SignalProxy::handleSignal(const QByteArray &funcName, const QVariantList ¶ms) {
- QObject* receiver;
- int methodId;
- SlotHash::const_iterator slot = _attachedSlots.constFind(funcName);
- while(slot != _attachedSlots.constEnd() && slot.key() == funcName) {
- receiver = (*slot).first;
- methodId = (*slot).second;
- if(!invokeSlot(receiver, methodId, params))
- qWarning("SignalProxy::handleSignal(): invokeMethod for \"%s\" failed ", methodName(receiver, methodId).constData());
- slot++;
- }
-}
-
-bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList ¶ms) {
- const QList<int> args = argTypes(receiver, methodId);
- const int numArgs = params.count() < args.count()
- ? params.count()
- : args.count();
-
- if(minArgCount(receiver, methodId) > params.count()) {
- qWarning() << "SignalProxy::invokeSlot(): not enough params to invoke" << methodName(receiver, methodId);
- return false;
- }
-
- void *_a[] = {0, // return type
- 0, 0, 0, 0 , 0, // and 10 args - that's the max size qt can handle with signals and slots
- 0, 0, 0, 0 , 0};
- // check for argument compatibility and build params array
- for(int i = 0; i < numArgs; i++) {
- if(!params[i].isValid()) {
- qWarning() << "SignalProxy::invokeSlot(): received invalid data for argument number" << i << "of method" << QString("%1::%2()").arg(receiver->metaObject()->className()).arg(receiver->metaObject()->method(methodId).signature());
- qWarning() << " - make sure all your data types are known by the Qt MetaSystem";
- return false;
- }
- if(args[i] != QMetaType::type(params[i].typeName())) {
- qWarning() << "SignalProxy::invokeSlot(): incompatible param types to invoke" << methodName(receiver, methodId);
- return false;
- }
- _a[i+1] = const_cast<void *>(params[i].constData());
- }
-
-
- Qt::ConnectionType type = QThread::currentThread() == receiver->thread()
- ? Qt::DirectConnection
- : Qt::QueuedConnection;
-
- if (type == Qt::DirectConnection) {
- return receiver->qt_metacall(QMetaObject::InvokeMetaMethod, methodId, _a) < 0;
- } else {
- qWarning() << "Queued Connections are not implemented yet";
- // not to self: qmetaobject.cpp:990 ff
- return false;
- }
-
-}
-
-void SignalProxy::dataAvailable() {
- // yet again. it's a private slot. no need for checks.
- QIODevice* ioDev = qobject_cast<QIODevice* >(sender());
- QVariant var;
- while(readDataFromDevice(ioDev, _peerByteCount[ioDev], var))
- receivePeerSignal(ioDev, var);
-}
-
-void SignalProxy::writeDataToDevice(QIODevice *dev, const QVariant &item) {
- QAbstractSocket* sock = qobject_cast<QAbstractSocket*>(dev);
- if(!dev->isOpen() || (sock && sock->state()!=QAbstractSocket::ConnectedState)) {
- qWarning("SignalProxy: Can't call on a closed device");
- return;
- }
- QByteArray block;
- QDataStream out(&block, QIODevice::WriteOnly);
- out.setVersion(QDataStream::Qt_4_2);
- out << (quint32)0 << item;
- out.device()->seek(0);
- out << (quint32)(block.size() - sizeof(quint32));
- dev->write(block);
-}
-
-bool SignalProxy::readDataFromDevice(QIODevice *dev, quint32 &blockSize, QVariant &item) {
- QDataStream in(dev);
- in.setVersion(QDataStream::Qt_4_2);
-
- if(blockSize == 0) {
- if(dev->bytesAvailable() < (int)sizeof(quint32)) return false;
- in >> blockSize;
- }
-
- if(dev->bytesAvailable() < blockSize)
- return false;
- in >> item;
- blockSize = 0;
- return true;
-}
-
-bool SignalProxy::methodsMatch(const QMetaMethod &signal, const QMetaMethod &slot) const {
- // if we don't even have the same basename it's a sure NO
- if(methodBaseName(signal) != methodBaseName(slot))
- return false;
-
- // are the signatures compatible?
- if(!QObject::staticMetaObject.checkConnectArgs(signal.signature(), slot.signature()))
- return false;
-
- // we take an educated guess if the signals and slots match
- QString signalsuffix = ::methodName(signal).mid(QString(::methodName(signal)).lastIndexOf(QRegExp("[A-Z]"))).toLower();
- QString slotprefix = ::methodName(slot).left(QString(::methodName(slot)).indexOf(QRegExp("[A-Z]"))).toLower();
-
- uint sizediff = qAbs(slotprefix.size() - signalsuffix.size());
- int ratio = editingDistance(slotprefix, signalsuffix) - sizediff;
- return (ratio < 2);
-}
-
-QString SignalProxy::methodBaseName(const QMetaMethod &method) {
- QString methodname = QString(method.signature()).section("(", 0, 0);
-
- // determine where we have to chop:
- if(method.methodType() == QMetaMethod::Slot) {
- // we take evertyhing from the first uppercase char if it's slot
- methodname = methodname.mid(methodname.indexOf(QRegExp("[A-Z]")));
- } else {
- // and if it's a signal we discard everything from the last uppercase char
- methodname = methodname.left(methodname.lastIndexOf(QRegExp("[A-Z]")));
- }
-
- methodname[0] = methodname[0].toUpper();
-
- return methodname;
+
+// ==================================================
+// ExtendedMetaObject
+// ==================================================
+SignalProxy::ExtendedMetaObject::ExtendedMetaObject(const QMetaObject *meta, bool checkConflicts)
+ : _meta(meta),
+ _updatedRemotelyId(_meta->indexOfSignal("updatedRemotely()"))
+{
+ for (int i = 0; i < _meta->methodCount(); i++) {
+ if (_meta->method(i).methodType() != QMetaMethod::Slot)
+ continue;
+
+#if QT_VERSION >= 0x050000
+ if (_meta->method(i).methodSignature().contains('*'))
+#else
+ if (QByteArray(_meta->method(i).signature()).contains('*'))
+#endif
+ continue; // skip methods with ptr params
+
+ QByteArray method = methodName(_meta->method(i));
+ if (method.startsWith("init"))
+ continue; // skip initializers
+
+ if (_methodIds.contains(method)) {
+ /* funny... moc creates for methods containing default parameters multiple metaMethod with separate methodIds.
+ we don't care... we just need the full fledged version
+ */
+ const QMetaMethod ¤t = _meta->method(_methodIds[method]);
+ const QMetaMethod &candidate = _meta->method(i);
+ if (current.parameterTypes().count() > candidate.parameterTypes().count()) {
+ int minCount = candidate.parameterTypes().count();
+ QList<QByteArray> commonParams = current.parameterTypes().mid(0, minCount);
+ if (commonParams == candidate.parameterTypes())
+ continue; // we already got the full featured version
+ }
+ else {
+ int minCount = current.parameterTypes().count();
+ QList<QByteArray> commonParams = candidate.parameterTypes().mid(0, minCount);
+ if (commonParams == current.parameterTypes()) {
+ _methodIds[method] = i; // use the new one
+ continue;
+ }
+ }
+ if (checkConflicts) {
+ qWarning() << "class" << meta->className() << "contains overloaded methods which is currently not supported!";
+#if QT_VERSION >= 0x050000
+ qWarning() << " - " << _meta->method(i).methodSignature() << "conflicts with" << _meta->method(_methodIds[method]).methodSignature();
+#else
+ qWarning() << " - " << _meta->method(i).signature() << "conflicts with" << _meta->method(_methodIds[method]).signature();
+#endif
+ }
+ continue;
+ }
+ _methodIds[method] = i;
+ }