Support (registered) non-local enums for signal/slot params in QML.
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlboundsignal.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qqmlboundsignal_p.h"
43
44 #include <private/qmetaobjectbuilder_p.h>
45 #include "qqmlengine_p.h"
46 #include "qqmlexpression_p.h"
47 #include "qqmlcontext_p.h"
48 #include "qqmlmetatype_p.h"
49 #include "qqml.h"
50 #include "qqmlcontext.h"
51 #include "qqmlglobal_p.h"
52 #include <private/qqmlprofilerservice_p.h>
53 #include <private/qv8debugservice_p.h>
54
55 #include <QtCore/qstringbuilder.h>
56 #include <QtCore/qdebug.h>
57
58 QT_BEGIN_NAMESPACE
59
60 class QQmlBoundSignalParameters : public QObject
61 {
62 Q_OBJECT
63 public:
64     QQmlBoundSignalParameters(const QMetaMethod &, QObject * = 0);
65     ~QQmlBoundSignalParameters();
66
67     void setValues(void **);
68     void clearValues();
69
70 private:
71     friend class MetaObject;
72     int metaCall(QMetaObject::Call, int _id, void **);
73     struct MetaObject : public QAbstractDynamicMetaObject {
74         MetaObject(QQmlBoundSignalParameters *b)
75             : parent(b) {}
76
77         int metaCall(QMetaObject::Call c, int id, void **a) { 
78             return parent->metaCall(c, id, a);
79         }
80         QQmlBoundSignalParameters *parent;
81     };
82
83     int *types;
84     void **values;
85     QMetaObject *myMetaObject;
86 };
87
88 static int evaluateIdx = -1;
89
90 QQmlAbstractBoundSignal::QQmlAbstractBoundSignal(QObject *parent)
91 : QObject(parent)
92 {
93 }
94
95 QQmlAbstractBoundSignal::~QQmlAbstractBoundSignal()
96 {
97 }
98
99 QQmlBoundSignal::QQmlBoundSignal(QObject *scope, const QMetaMethod &signal, 
100                                QObject *parent)
101 : m_expression(0), m_signal(signal), m_paramsValid(false), m_isEvaluating(false), m_params(0)
102 {
103     // This is thread safe.  Although it may be updated by two threads, they
104     // will both set it to the same value - so the worst thing that can happen
105     // is that they both do the work to figure it out.  Boo hoo.
106     if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount();
107
108     QQml_setParent_noEvent(this, parent);
109     QQmlPropertyPrivate::connect(scope, m_signal.methodIndex(), this, evaluateIdx);
110 }
111
112 QQmlBoundSignal::QQmlBoundSignal(QQmlContext *ctxt, const QString &val, 
113                                QObject *scope, const QMetaMethod &signal,
114                                QObject *parent)
115 : m_expression(0), m_signal(signal), m_paramsValid(false), m_isEvaluating(false), m_params(0)
116 {
117     // This is thread safe.  Although it may be updated by two threads, they
118     // will both set it to the same value - so the worst thing that can happen
119     // is that they both do the work to figure it out.  Boo hoo.
120     if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount();
121
122     QQml_setParent_noEvent(this, parent);
123     QQmlPropertyPrivate::connect(scope, m_signal.methodIndex(), this, evaluateIdx);
124
125     m_expression = new QQmlExpression(ctxt, scope, val);
126 }
127
128 QQmlBoundSignal::~QQmlBoundSignal()
129 {
130     delete m_expression;
131     m_expression = 0;
132 }
133
134 int QQmlBoundSignal::index() const 
135
136     return m_signal.methodIndex();
137 }
138
139 /*!
140     Returns the signal expression.
141 */
142 QQmlExpression *QQmlBoundSignal::expression() const
143 {
144     return m_expression;
145 }
146
147 /*!
148     Sets the signal expression to \a e.  Returns the current signal expression,
149     or null if there is no signal expression.
150
151     The QQmlBoundSignal instance takes ownership of \a e.  The caller is 
152     assumes ownership of the returned QQmlExpression.
153 */
154 QQmlExpression *QQmlBoundSignal::setExpression(QQmlExpression *e)
155 {
156     QQmlExpression *rv = m_expression;
157     m_expression = e;
158     if (m_expression) m_expression->setNotifyOnValueChanged(false);
159     return rv;
160 }
161
162 QQmlBoundSignal *QQmlBoundSignal::cast(QObject *o)
163 {
164     QQmlAbstractBoundSignal *s = qobject_cast<QQmlAbstractBoundSignal*>(o);
165     return static_cast<QQmlBoundSignal *>(s);
166 }
167
168 int QQmlBoundSignal::qt_metacall(QMetaObject::Call c, int id, void **a)
169 {
170     if (c == QMetaObject::InvokeMetaMethod && id == evaluateIdx) {
171         if (!m_expression)
172             return -1;
173
174         if (QQmlDebugService::isDebuggingEnabled())
175             QV8DebugService::instance()->signalEmitted(QString::fromAscii(m_signal.signature()));
176
177         QQmlHandlingSignalProfiler prof(m_signal, m_expression);
178
179         m_isEvaluating = true;
180         if (!m_paramsValid) {
181             if (!m_signal.parameterTypes().isEmpty())
182                 m_params = new QQmlBoundSignalParameters(m_signal, this);
183             m_paramsValid = true;
184         }
185
186         if (m_params) m_params->setValues(a);
187         if (m_expression && m_expression->engine()) {
188             QQmlExpressionPrivate::get(m_expression)->value(m_params);
189             if (m_expression && m_expression->hasError())
190                 QQmlEnginePrivate::warning(m_expression->engine(), m_expression->error());
191         }
192         if (m_params) m_params->clearValues();
193         m_isEvaluating = false;
194         return -1;
195     } else {
196         return QObject::qt_metacall(c, id, a);
197     }
198 }
199
200 QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method, 
201                                                                      QObject *parent)
202 : QObject(parent), types(0), values(0)
203 {
204     MetaObject *mo = new MetaObject(this);
205
206     // ### Optimize!
207     QMetaObjectBuilder mob;
208     mob.setSuperClass(&QQmlBoundSignalParameters::staticMetaObject);
209     mob.setClassName("QQmlBoundSignalParameters");
210
211     QList<QByteArray> paramTypes = method.parameterTypes();
212     QList<QByteArray> paramNames = method.parameterNames();
213     types = new int[paramTypes.count()];
214     for (int ii = 0; ii < paramTypes.count(); ++ii) {
215         const QByteArray &type = paramTypes.at(ii);
216         const QByteArray &name = paramNames.at(ii);
217
218         if (name.isEmpty() || type.isEmpty()) {
219             types[ii] = 0;
220             continue;
221         }
222
223         QVariant::Type t = (QVariant::Type)QMetaType::type(type.constData());
224         if (QQmlMetaType::isQObject(t)) {
225             types[ii] = QMetaType::QObjectStar;
226             QMetaPropertyBuilder prop = mob.addProperty(name, "QObject*");
227             prop.setWritable(false);
228         } else {
229             QByteArray propType = type;
230             if ((QMetaType::typeFlags(t) & QMetaType::IsEnumeration) == QMetaType::IsEnumeration) {
231                 t = QVariant::Int;
232                 propType = "int";
233             } else if (t == QVariant::Invalid) {
234                 QByteArray scope;
235                 QByteArray name;
236                 int scopeIdx = propType.lastIndexOf("::");
237                 if (scopeIdx != -1) {
238                     scope = propType.left(scopeIdx);
239                     name = propType.mid(scopeIdx + 2);
240                 } else {
241                     name = propType;
242                 }
243                 const QMetaObject *meta;
244                 if (scope == "Qt")
245                     meta = &QObject::staticQtMetaObject;
246                 else
247                     meta = parent->parent()->metaObject();   //### assumes parent->parent()
248                 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
249                     QMetaEnum m = meta->enumerator(i);
250                     if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) {
251                         t = QVariant::Int;
252                         propType = "int";
253                         break;
254                     }
255                 }
256             }
257             types[ii] = t;
258             QMetaPropertyBuilder prop = mob.addProperty(name, propType);
259             prop.setWritable(false);
260         }
261     }
262     myMetaObject = mob.toMetaObject();
263     *static_cast<QMetaObject *>(mo) = *myMetaObject;
264
265     d_ptr->metaObject = mo;
266 }
267
268 QQmlBoundSignalParameters::~QQmlBoundSignalParameters()
269 {
270     delete [] types;
271     free(myMetaObject);
272 }
273
274 void QQmlBoundSignalParameters::setValues(void **v)
275 {
276     values = v;
277 }
278
279 void QQmlBoundSignalParameters::clearValues()
280 {
281     values = 0;
282 }
283
284 int QQmlBoundSignalParameters::metaCall(QMetaObject::Call c, int id, void **a)
285 {
286     if (!values)
287         return -1;
288
289     if (c == QMetaObject::ReadProperty && id >= 1) {
290         int t = types[id - 1];
291         void *p = a[0];
292         QMetaType::destruct(t, p);
293         QMetaType::construct(t, p, values[id]);
294         return -1;
295     } else {
296         return qt_metacall(c, id, a);
297     }
298 }
299
300 QT_END_NAMESPACE
301
302 #include <qqmlboundsignal.moc>