Merge branch 'master' of git://gitorious.org/qt/qtdeclarative into api_changes
[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;
178         if (prof.enabled) {
179             prof.setSignalInfo(QString::fromLatin1(m_signal.signature()),
180                                m_expression->expression());
181             prof.setLocation(m_expression->sourceFile(), m_expression->lineNumber(),
182                              m_expression->columnNumber());
183         }
184
185         m_isEvaluating = true;
186         if (!m_paramsValid) {
187             if (!m_signal.parameterTypes().isEmpty())
188                 m_params = new QQmlBoundSignalParameters(m_signal, this);
189             m_paramsValid = true;
190         }
191
192         if (m_params) m_params->setValues(a);
193         if (m_expression && m_expression->engine()) {
194             QQmlExpressionPrivate::get(m_expression)->value(m_params);
195             if (m_expression && m_expression->hasError())
196                 QQmlEnginePrivate::warning(m_expression->engine(), m_expression->error());
197         }
198         if (m_params) m_params->clearValues();
199         m_isEvaluating = false;
200         return -1;
201     } else {
202         return QObject::qt_metacall(c, id, a);
203     }
204 }
205
206 QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method, 
207                                                                      QObject *parent)
208 : QObject(parent), types(0), values(0)
209 {
210     MetaObject *mo = new MetaObject(this);
211
212     // ### Optimize!
213     QMetaObjectBuilder mob;
214     mob.setSuperClass(&QQmlBoundSignalParameters::staticMetaObject);
215     mob.setClassName("QQmlBoundSignalParameters");
216
217     QList<QByteArray> paramTypes = method.parameterTypes();
218     QList<QByteArray> paramNames = method.parameterNames();
219     types = new int[paramTypes.count()];
220     for (int ii = 0; ii < paramTypes.count(); ++ii) {
221         const QByteArray &type = paramTypes.at(ii);
222         const QByteArray &name = paramNames.at(ii);
223
224         if (name.isEmpty() || type.isEmpty()) {
225             types[ii] = 0;
226             continue;
227         }
228
229         QVariant::Type t = (QVariant::Type)QMetaType::type(type.constData());
230         if (QQmlMetaType::isQObject(t)) {
231             types[ii] = QMetaType::QObjectStar;
232             QMetaPropertyBuilder prop = mob.addProperty(name, "QObject*");
233             prop.setWritable(false);
234         } else {
235             QByteArray propType = type;
236             if (t >= QVariant::UserType || t == QVariant::Invalid) {
237                 QByteArray scope;
238                 QByteArray name;
239                 int scopeIdx = propType.lastIndexOf("::");
240                 if (scopeIdx != -1) {
241                     scope = propType.left(scopeIdx);
242                     name = propType.mid(scopeIdx + 2);
243                 } else {
244                     name = propType;
245                 }
246                 const QMetaObject *meta;
247                 if (scope == "Qt")
248                     meta = &QObject::staticQtMetaObject;
249                 else
250                     meta = parent->parent()->metaObject();   //### assumes parent->parent()
251                 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
252                     QMetaEnum m = meta->enumerator(i);
253                     if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) {
254                         t = QVariant::Int;
255                         propType = "int";
256                         break;
257                     }
258                 }
259             }
260             types[ii] = t;
261             QMetaPropertyBuilder prop = mob.addProperty(name, propType);
262             prop.setWritable(false);
263         }
264     }
265     myMetaObject = mob.toMetaObject();
266     *static_cast<QMetaObject *>(mo) = *myMetaObject;
267
268     d_ptr->metaObject = mo;
269 }
270
271 QQmlBoundSignalParameters::~QQmlBoundSignalParameters()
272 {
273     delete [] types;
274     free(myMetaObject);
275 }
276
277 void QQmlBoundSignalParameters::setValues(void **v)
278 {
279     values = v;
280 }
281
282 void QQmlBoundSignalParameters::clearValues()
283 {
284     values = 0;
285 }
286
287 int QQmlBoundSignalParameters::metaCall(QMetaObject::Call c, int id, void **a)
288 {
289     if (!values)
290         return -1;
291
292     if (c == QMetaObject::ReadProperty && id >= 1) {
293         int t = types[id - 1];
294         void *p = a[0];
295         QMetaType::destruct(t, p);
296         QMetaType::construct(t, p, values[id]);
297         return -1;
298     } else {
299         return qt_metacall(c, id, a);
300     }
301 }
302
303 QT_END_NAMESPACE
304
305 #include <qqmlboundsignal.moc>