YES! We finally have dynamic signals between Core and Client, meaning that arbitrary
[quassel.git] / src / contrib / qxt / qxtmetaobject.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) Qxt Foundation. Some rights reserved.
4 **
5 ** This file is part of the QxtCore module of the Qt eXTension library
6 **
7 ** This library is free software; you can redistribute it and/or modify it
8 ** under the terms of th Common Public License, version 1.0, as published by
9 ** IBM.
10 **
11 ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
12 ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
13 ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
14 ** FITNESS FOR A PARTICULAR PURPOSE.
15 **
16 ** You should have received a copy of the CPL along with this file.
17 ** See the LICENSE file and the cpl1.0.txt file included with the source
18 ** distribution for more information. If you did not receive a copy of the
19 ** license, contact the Qxt Foundation.
20 **
21 ** <http://libqxt.sourceforge.net>  <foundation@libqxt.org>
22 **
23 ****************************************************************************/
24 /**
25 \class QxtMetaObject QxtMetaObject
26
27 \ingroup core
28
29 \brief provides extensions to QMetaObject
30
31 including QxtMetaObject::bind \n
32
33 */
34 #include "qxtmetaobject.h"
35 #include "qxtboundfunction.h"
36 #include "qxtboundcfunction.h"
37
38 #include <QByteArray>
39 #include <QMetaObject>
40 #include <QMetaMethod>
41 #include <QtDebug>
42
43 class QxtBoundArgument
44 {
45     // This class intentionally left blank
46 };
47 Q_DECLARE_METATYPE(QxtBoundArgument)
48
49 class QxtBoundFunctionBase;
50
51 QxtBoundFunction::QxtBoundFunction(QObject* parent) : QObject(parent)
52 {
53     // initializer only
54 }
55
56 #define QXT_ARG(i) ((argCount>i)?QGenericArgument(p ## i .typeName(), p ## i .constData()):QGenericArgument())
57 #define QXT_VAR_ARG(i) (p ## i .isValid())?QGenericArgument(p ## i .typeName(), p ## i .constData()):QGenericArgument()
58 bool QxtBoundFunction::invoke(Qt::ConnectionType type, QXT_IMPL_10ARGS(QVariant))
59 {
60     return invoke(type, QXT_VAR_ARG(1), QXT_VAR_ARG(2), QXT_VAR_ARG(3), QXT_VAR_ARG(4), QXT_VAR_ARG(5), QXT_VAR_ARG(6), QXT_VAR_ARG(7), QXT_VAR_ARG(8), QXT_VAR_ARG(9), QXT_VAR_ARG(10));
61 }
62
63 bool QxtBoundFunction::invoke(Qt::ConnectionType type, QGenericReturnArgument returnValue, QXT_IMPL_10ARGS(QVariant))
64 {
65     return invoke(type, returnValue, QXT_VAR_ARG(1), QXT_VAR_ARG(2), QXT_VAR_ARG(3), QXT_VAR_ARG(4), QXT_VAR_ARG(5), QXT_VAR_ARG(6), QXT_VAR_ARG(7), QXT_VAR_ARG(8), QXT_VAR_ARG(9), QXT_VAR_ARG(10));
66 }
67
68 QxtBoundFunctionBase::QxtBoundFunctionBase(QObject* parent, QGenericArgument* params[10], QByteArray types[10]) : QxtBoundFunction(parent)
69 {
70     for (int i=0; i<10; i++)
71     {
72         if (!params[i]) break;
73         if (QByteArray(params[i]->name()) == "QxtBoundArgument")
74         {
75             arg[i] = QGenericArgument("QxtBoundArgument", params[i]->data());
76         }
77         else
78         {
79             data[i] = QMetaType::construct(QMetaType::type(params[i]->name()), params[i]->data());
80             arg[i] = p[i] = QGenericArgument(params[i]->name(), data[i]);
81         }
82         bindTypes[i] = types[i];
83     }
84 }
85
86 QxtBoundFunctionBase::~QxtBoundFunctionBase()
87 {
88     for (int i=0; i<10; i++)
89     {
90         if (arg[i].name() == 0) return;
91         if (QByteArray(arg[i].name()) != "QxtBoundArgument") QMetaType::destroy(QMetaType::type(arg[i].name()), arg[i].data());
92     }
93 }
94
95 int QxtBoundFunctionBase::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
96 {
97     _id = QObject::qt_metacall(_c, _id, _a);
98     if (_id < 0)
99         return _id;
100     if (_c == QMetaObject::InvokeMetaMethod)
101     {
102         if (_id == 0)
103         {
104             for (int i = 0; i < 10; i++)
105             {
106                 if (QByteArray(arg[i].name()) == "QxtBoundArgument")
107                 {
108                     p[i] = QGenericArgument(bindTypes[i].constData(), _a[(quintptr)(arg[i].data())]);
109                 }
110             }
111             invokeImpl(Qt::DirectConnection, QGenericReturnArgument(), p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
112         }
113         _id = -1;
114     }
115     return _id;
116 }
117
118 bool QxtBoundFunctionBase::invokeBase(Qt::ConnectionType type, QGenericReturnArgument returnValue, QXT_IMPL_10ARGS(QGenericArgument))
119 {
120     QGenericArgument* args[10] = { &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10 };
121     for (int i = 0; i < 10; i++)
122     {
123         if (QByteArray(arg[i].name()) == "QxtBoundArgument")
124         {
125             p[i] = *args[(quintptr)(arg[i].data())-1];
126         }
127     }
128     return invokeImpl(type, returnValue, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
129 }
130
131 bool QxtBoundFunction::invoke(Qt::ConnectionType type, QGenericReturnArgument returnValue, QXT_IMPL_10ARGS(QGenericArgument))
132 {
133     return reinterpret_cast<QxtBoundFunctionBase*>(this)->invokeBase(type, returnValue, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
134 }
135
136 class QxtBoundSlot : public QxtBoundFunctionBase
137 {
138 public:
139     QByteArray sig;
140
141     QxtBoundSlot(QObject* receiver, const char* invokable, QGenericArgument* params[10], QByteArray types[10]) : QxtBoundFunctionBase(receiver, params, types), sig(invokable)
142     {
143         // initializers only
144     }
145
146     virtual bool invokeImpl(Qt::ConnectionType type, QGenericReturnArgument returnValue, QXT_IMPL_10ARGS(QGenericArgument))
147     {
148         if (!QMetaObject::invokeMethod(parent(), QxtMetaObject::methodName(sig.constData()), type, returnValue, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10))
149         {
150             qWarning() << "QxtBoundFunction: call to" << sig << "failed";
151             return false;
152         }
153         return true;
154     }
155 };
156
157 namespace QxtMetaObject
158 {
159
160 /*!
161 \relates QxtMetaObject
162
163     \fn methodName(const char* method)
164
165     Returns the name of the given method.
166
167     Example usage:
168     \code
169     QByteArray method = QxtMetaObject::methodName(" int foo ( int bar, double baz )");
170     // method is now "foo"
171     \endcode
172  */
173 QByteArray methodName(const char* method)
174 {
175     QByteArray name = methodSignature(method);
176     const int idx = name.indexOf("(");
177     if (idx != -1)
178         name.truncate(idx);
179     return name;
180 }
181
182 /*!
183 \relates QxtMetaObject
184
185 \fn methodSignature(const char* method)
186
187 Returns the signature of the given method.
188  */
189 QByteArray methodSignature(const char* method)
190 {
191     QByteArray name = QMetaObject::normalizedSignature(method);
192     if (name.startsWith("1") || name.startsWith("2"))
193         return name.mid(1);
194     return name;
195 }
196
197 /*!
198 \relates QxtMetaObject
199
200 \fn bool isSignalOrSlot(const char* method)
201
202 checks if \p method contains parantesis and begins with 1 or 2 */
203 bool isSignalOrSlot (const char* method)
204 {
205     QByteArray m(method);
206     return (m.count() && (m[0] == '1'||m[0] == '2') && m.contains('(') && m.contains(')'));
207 }
208
209 /**
210 \relates QxtMetaObject
211 \fn bind(QObject* recv, const char* invokable, QXT_IMPL_10ARGS(QVariant))
212
213 creates a QxtBoundFunction from a slot + arguments \n
214 can be used for QxtMetaObject::connect \
215
216 \code
217 QxtMetaObject::connect(\n
218         this, SIGNAL(init()), \\n
219         QxtMetaObject::bind(this, SLOT(say(QString)), Q_ARG(QString,"hello")));
220 \endcode
221 \n
222 \code
223 QxtMetaObject::connect( \n
224         this, SIGNAL(init(int i)), \n
225         QxtMetaObject::bind(this, SLOT(say(QString),int), Q_ARG(QString,"hello"),Q_BIND(1)));
226 \endcode
227
228  */
229 QxtBoundFunction* bind(QObject* recv, const char* invokable, QXT_IMPL_10ARGS(QVariant))
230 {
231     if (!recv)
232     {
233         qWarning() << "QxtMetaObject::bind: cannot connect to null QObject";
234         return 0;
235     }
236
237     QVariant* args[10] = { &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10 };
238     QByteArray connSlot("2"), recvSlot(QMetaObject::normalizedSignature(invokable));
239     const QMetaObject* meta = recv->metaObject();
240     int methodID = meta->indexOfMethod(QxtMetaObject::methodSignature(recvSlot.constData()));
241     if (methodID == -1)
242     {
243         qWarning() << "QxtMetaObject::bind: no such method " << recvSlot;
244         return 0;
245     }
246     QMetaMethod method = meta->method(methodID);
247     int argCount = method.parameterTypes().count();
248     const QList<QByteArray> paramTypes = method.parameterTypes();
249
250     for (int i=0; i<argCount; i++)
251     {
252         if (paramTypes[i] == "QxtBoundArgument") continue;
253         int type = QMetaType::type(paramTypes[i].constData());
254         if (!args[i]->canConvert((QVariant::Type)type))
255         {
256             qWarning() << "QxtMetaObject::bind: incompatible parameter list for " << recvSlot;
257             return 0;
258         }
259     }
260
261     return QxtMetaObject::bind(recv, invokable, QXT_ARG(1), QXT_ARG(2), QXT_ARG(3), QXT_ARG(4), QXT_ARG(5), QXT_ARG(6), QXT_ARG(7), QXT_ARG(8), QXT_ARG(9), QXT_ARG(10));
262 }
263
264 QxtBoundFunction* bind(QObject* recv, const char* invokable, QXT_IMPL_10ARGS(QGenericArgument))
265 {
266     if (!recv)
267     {
268         qWarning() << "QxtMetaObject::bind: cannot connect to null QObject";
269         return 0;
270     }
271
272     QGenericArgument* args[10] = { &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10 };
273     QByteArray connSlot("2"), recvSlot(QMetaObject::normalizedSignature(invokable)), bindTypes[10];
274     const QMetaObject* meta = recv->metaObject();
275     int methodID = meta->indexOfMethod(QxtMetaObject::methodSignature(recvSlot.constData()).constData());
276     if (methodID == -1)
277     {
278         qWarning() << "QxtMetaObject::bind: no such method " << recvSlot;
279         return 0;
280     }
281     QMetaMethod method = meta->method(methodID);
282     int argCount = method.parameterTypes().count();
283
284     connSlot += QxtMetaObject::methodName(invokable) + "(";
285     for (int i=0; i<10; i++)
286     {
287         if (args[i]->name() == 0) break;        // done
288         if (i >= argCount)
289         {
290             qWarning() << "QxtMetaObject::bind: too many arguments passed to " << invokable;
291             return 0;
292         }
293         if (i > 0) connSlot += ",";             // argument separator
294         if (QByteArray(args[i]->name()) == "QxtBoundArgument")
295         {
296             Q_ASSERT_X((quintptr)(args[i]->data()) > 0 && (quintptr)(args[i]->data()) <= 10, "QXT_BIND", "invalid argument number");
297             connSlot += method.parameterTypes()[i];
298             bindTypes[i] = method.parameterTypes()[i];
299         }
300         else
301         {
302             connSlot += args[i]->name();        // type name
303         }
304     }
305     connSlot = QMetaObject::normalizedSignature(connSlot += ")");
306
307     if (!QMetaObject::checkConnectArgs(recvSlot.constData(), connSlot.constData()))
308     {
309         qWarning() << "QxtMetaObject::bind: provided parameters " << connSlot.mid(connSlot.indexOf('(')) << " is incompatible with " << invokable;
310         return 0;
311     }
312
313     return new QxtBoundSlot(recv, invokable, args, bindTypes);
314 }
315
316 /**
317 \relates QxtMetaObject
318 \fn connect(QObject* sender, const char* signal, QxtBoundFunction* slot, Qt::ConnectionType type) {
319
320 connects a signal to a QxtBoundFunction \n
321  */
322 bool connect(QObject* sender, const char* signal, QxtBoundFunction* slot, Qt::ConnectionType type)
323 {
324     const QMetaObject* meta = sender->metaObject();
325     int methodID = meta->indexOfMethod(meta->normalizedSignature(signal).mid(1).constData());
326     if (methodID < 0)
327     {
328         qWarning() << "QxtMetaObject::connect: no such signal: " << QByteArray(signal).mid(1);
329         return false;
330     }
331
332     return QMetaObject::connect(sender, methodID, slot, QObject::staticMetaObject.methodCount(), (int)(type));
333 }
334
335 }