We now have a current svn snapshot of libqxt in our contrib dir, and
[quassel.git] / src / contrib / libqxt-2007-10-24 / src / core / 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 QxtCore
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 #include "qxtmetatype.h"
38
39 #include <QByteArray>
40 #include <QMetaObject>
41 #include <QMetaMethod>
42 #include <QtDebug>
43
44 #ifndef QXT_DOXYGEN_RUN
45 class QxtBoundArgument
46 {
47     // This class intentionally left blank
48 };
49 Q_DECLARE_METATYPE(QxtBoundArgument)
50
51 class QxtBoundFunctionBase;
52
53 QxtBoundFunction::QxtBoundFunction(QObject* parent) : QObject(parent)
54 {
55     // initializer only
56 }
57 #endif
58
59 bool QxtBoundFunction::invoke(Qt::ConnectionType type, QXT_IMPL_10ARGS(QVariant))
60 {
61     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));
62 }
63
64 bool QxtBoundFunction::invoke(Qt::ConnectionType type, QGenericReturnArgument returnValue, QXT_IMPL_10ARGS(QVariant))
65 {
66     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));
67 }
68
69 QxtBoundFunctionBase::QxtBoundFunctionBase(QObject* parent, QGenericArgument* params[10], QByteArray types[10]) : QxtBoundFunction(parent)
70 {
71     for (int i=0; i<10; i++)
72     {
73         if (!params[i]) break;
74         if (QByteArray(params[i]->name()) == "QxtBoundArgument")
75         {
76             arg[i] = QGenericArgument("QxtBoundArgument", params[i]->data());
77         }
78         else
79         {
80             data[i] = qxtConstructFromGenericArgument(*params[i]);
81             arg[i] = p[i] = QGenericArgument(params[i]->name(), data[i]);
82         }
83         bindTypes[i] = types[i];
84     }
85 }
86
87 QxtBoundFunctionBase::~QxtBoundFunctionBase()
88 {
89     for (int i=0; i<10; i++)
90     {
91         if (arg[i].name() == 0) return;
92         if (QByteArray(arg[i].name()) != "QxtBoundArgument") qxtDestroyFromGenericArgument(arg[i]);
93     }
94 }
95
96 int QxtBoundFunctionBase::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
97 {
98     _id = QObject::qt_metacall(_c, _id, _a);
99     if (_id < 0)
100         return _id;
101     if (_c == QMetaObject::InvokeMetaMethod)
102     {
103         if (_id == 0)
104         {
105             for (int i = 0; i < 10; i++)
106             {
107                 if (QByteArray(arg[i].name()) == "QxtBoundArgument")
108                 {
109                     p[i] = QGenericArgument(bindTypes[i].constData(), _a[(quintptr)(arg[i].data())]);
110                 }
111             }
112             invokeImpl(Qt::DirectConnection, QGenericReturnArgument(), p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
113         }
114         _id = -1;
115     }
116     return _id;
117 }
118
119 bool QxtBoundFunctionBase::invokeBase(Qt::ConnectionType type, QGenericReturnArgument returnValue, QXT_IMPL_10ARGS(QGenericArgument))
120 {
121     QGenericArgument* args[10] = { &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10 };
122     for (int i = 0; i < 10; i++)
123     {
124         if (QByteArray(arg[i].name()) == "QxtBoundArgument")
125         {
126             p[i] = *args[(quintptr)(arg[i].data())-1];
127         }
128     }
129     return invokeImpl(type, returnValue, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
130 }
131
132 bool QxtBoundFunction::invoke(Qt::ConnectionType type, QGenericReturnArgument returnValue, QXT_IMPL_10ARGS(QGenericArgument))
133 {
134     return reinterpret_cast<QxtBoundFunctionBase*>(this)->invokeBase(type, returnValue, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
135 }
136
137 #ifndef QXT_DOXYGEN_RUN
138 class QxtBoundSlot : public QxtBoundFunctionBase
139 {
140 public:
141     QByteArray sig;
142
143     QxtBoundSlot(QObject* receiver, const char* invokable, QGenericArgument* params[10], QByteArray types[10]) : QxtBoundFunctionBase(receiver, params, types), sig(invokable)
144     {
145         // initializers only
146     }
147
148     virtual bool invokeImpl(Qt::ConnectionType type, QGenericReturnArgument returnValue, QXT_IMPL_10ARGS(QGenericArgument))
149     {
150         if (!QMetaObject::invokeMethod(parent(), QxtMetaObject::methodName(sig.constData()), type, returnValue, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10))
151         {
152             qWarning() << "QxtBoundFunction: call to" << sig << "failed";
153             return false;
154         }
155         return true;
156     }
157 };
158 #endif
159
160 namespace QxtMetaObject
161 {
162
163 /*!
164 \relates QxtMetaObject
165
166     \fn methodName(const char* method)
167
168     Returns the name of the given method.
169
170     Example usage:
171     \code
172     QByteArray method = QxtMetaObject::methodName(" int foo ( int bar, double baz )");
173     // method is now "foo"
174     \endcode
175  */
176 QByteArray methodName(const char* method)
177 {
178     QByteArray name = methodSignature(method);
179     const int idx = name.indexOf("(");
180     if (idx != -1)
181         name.truncate(idx);
182     return name;
183 }
184
185 /*!
186 \relates QxtMetaObject
187
188 \fn methodSignature(const char* method)
189
190 Returns the signature of the given method.
191  */
192 QByteArray methodSignature(const char* method)
193 {
194     QByteArray name = QMetaObject::normalizedSignature(method);
195     if (name.startsWith("1") || name.startsWith("2"))
196         return name.mid(1);
197     return name;
198 }
199
200 /*!
201 \relates QxtMetaObject
202
203 \fn bool isSignalOrSlot(const char* method)
204
205 checks if \p method contains parantesis and begins with 1 or 2 */
206 bool isSignalOrSlot (const char* method)
207 {
208     QByteArray m(method);
209     return (m.count() && (m[0] == '1'||m[0] == '2') && m.contains('(') && m.contains(')'));
210 }
211
212 /**
213  * \relates QxtMetaObject
214  * \sa QxtMetaObject::connect
215  * \sa QxtBoundFunction
216  *
217  * Creates a binding to the provided signal, slot, or Q_INVOKABLE method using the
218  * provided parameter list. The type of each argument is deduced from the type of
219  * the QVariant. This function cannot bind positional arguments; see the
220  * overload using QGenericArgument.
221  *
222  * If the provided QObject does not implement the requested method, or if the
223  * argument list is incompatible with the method's function signature, this
224  * function returns NULL.
225  *
226  * The returned QxtBoundFunction is created as a child of the receiver.
227  * Changing the parent will result in undefined behavior.
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 /**
265  * \relates QxtMetaObject
266  * \sa QxtMetaObject::connect
267  * \sa QxtBoundFunction
268  * \sa QXT_BIND
269  *
270  * Creates a binding to the provided signal, slot, or Q_INVOKABLE method using the
271  * provided parameter list. Use the Q_ARG macro to specify constant parameters, or
272  * use the QXT_BIND macro to relay a parameter from a connected signal or passed
273  * via the QxtBoundFunction::invoke() method.
274  * 
275  * If the provided QObject does not implement the requested method, or if the
276  * argument list is incompatible with the method's function signature, this
277  * function returns NULL.
278  *
279  * The returned QxtBoundFunction is created as a child of the receiver.
280  * Changing the parent will result in undefined behavior.
281  */
282 QxtBoundFunction* bind(QObject* recv, const char* invokable, QXT_IMPL_10ARGS(QGenericArgument))
283 {
284     if (!recv)
285     {
286         qWarning() << "QxtMetaObject::bind: cannot connect to null QObject";
287         return 0;
288     }
289
290     QGenericArgument* args[10] = { &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10 };
291     QByteArray connSlot("2"), recvSlot(QMetaObject::normalizedSignature(invokable)), bindTypes[10];
292     const QMetaObject* meta = recv->metaObject();
293     int methodID = meta->indexOfMethod(QxtMetaObject::methodSignature(recvSlot.constData()).constData());
294     if (methodID == -1)
295     {
296         qWarning() << "QxtMetaObject::bind: no such method " << recvSlot;
297         return 0;
298     }
299     QMetaMethod method = meta->method(methodID);
300     int argCount = method.parameterTypes().count();
301
302     connSlot += QxtMetaObject::methodName(invokable) + "(";
303     for (int i=0; i<10; i++)
304     {
305         if (args[i]->name() == 0) break;        // done
306         if (i >= argCount)
307         {
308             qWarning() << "QxtMetaObject::bind: too many arguments passed to " << invokable;
309             return 0;
310         }
311         if (i > 0) connSlot += ",";             // argument separator
312         if (QByteArray(args[i]->name()) == "QxtBoundArgument")
313         {
314             Q_ASSERT_X((quintptr)(args[i]->data()) > 0 && (quintptr)(args[i]->data()) <= 10, "QXT_BIND", "invalid argument number");
315             connSlot += method.parameterTypes()[i];
316             bindTypes[i] = method.parameterTypes()[i];
317         }
318         else
319         {
320             connSlot += args[i]->name();        // type name
321         }
322     }
323     connSlot = QMetaObject::normalizedSignature(connSlot += ")");
324
325     if (!QMetaObject::checkConnectArgs(recvSlot.constData(), connSlot.constData()))
326     {
327         qWarning() << "QxtMetaObject::bind: provided parameters " << connSlot.mid(connSlot.indexOf('(')) << " is incompatible with " << invokable;
328         return 0;
329     }
330
331     return new QxtBoundSlot(recv, invokable, args, bindTypes);
332 }
333
334 /**
335 \relates QxtMetaObject
336 \fn connect(QObject* sender, const char* signal, QxtBoundFunction* slot, Qt::ConnectionType type) {
337
338 connects a signal to a QxtBoundFunction \n
339  */
340 bool connect(QObject* sender, const char* signal, QxtBoundFunction* slot, Qt::ConnectionType type)
341 {
342     const QMetaObject* meta = sender->metaObject();
343     int methodID = meta->indexOfMethod(meta->normalizedSignature(signal).mid(1).constData());
344     if (methodID < 0)
345     {
346         qWarning() << "QxtMetaObject::connect: no such signal: " << QByteArray(signal).mid(1);
347         return false;
348     }
349
350     return QMetaObject::connect(sender, methodID, slot, QObject::staticMetaObject.methodCount(), (int)(type));
351 }
352
353 }