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