1740224fad12e83a8c9ca71266ac1a3a5d64508f
[profile/ivi/qtdeclarative.git] / src / quick / util / qquickconnections.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 "qquickconnections_p.h"
43
44 #include <private/qqmlexpression_p.h>
45 #include <private/qqmlproperty_p.h>
46 #include <private/qqmlboundsignal_p.h>
47 #include <qqmlcontext.h>
48 #include <private/qqmlcontext_p.h>
49 #include <qqmlinfo.h>
50
51 #include <QtCore/qdebug.h>
52 #include <QtCore/qstringlist.h>
53
54 #include <private/qobject_p.h>
55
56 QT_BEGIN_NAMESPACE
57
58 class QQuickConnectionsPrivate : public QObjectPrivate
59 {
60 public:
61     QQuickConnectionsPrivate() : target(0), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {}
62
63     QList<QQmlBoundSignal*> boundsignals;
64     QObject *target;
65
66     bool targetSet;
67     bool ignoreUnknownSignals;
68     bool componentcomplete;
69
70     QByteArray data;
71 };
72
73 /*!
74     \qmlclass Connections QQuickConnections
75     \inqmlmodule QtQuick 2
76     \ingroup qml-utility-elements
77     \brief A Connections element describes generalized connections to signals.
78
79     A Connections object creates a connection to a QML signal.
80
81     When connecting to signals in QML, the usual way is to create an
82     "on<Signal>" handler that reacts when a signal is received, like this:
83
84     \qml
85     MouseArea {
86         onClicked: { foo(parameters) }
87     }
88     \endqml
89
90     However, it is not possible to connect to a signal in this way in some 
91     cases, such as when:
92
93     \list
94         \li Multiple connections to the same signal are required
95         \li Creating connections outside the scope of the signal sender
96         \li Connecting to targets not defined in QML
97     \endlist
98
99     When any of these are needed, the Connections element can be used instead.
100
101     For example, the above code can be changed to use a Connections object,
102     like this:
103
104     \qml
105     MouseArea {
106         Connections {
107             onClicked: foo(parameters)
108         }
109     }
110     \endqml
111
112     More generally, the Connections object can be a child of some object other than
113     the sender of the signal:
114
115     \qml
116     MouseArea {
117         id: area
118     }
119     // ...
120     \endqml
121     \qml
122     Connections {
123         target: area
124         onClicked: foo(parameters)
125     }
126     \endqml
127
128     \sa QtQml
129 */
130 QQuickConnections::QQuickConnections(QObject *parent) :
131     QObject(*(new QQuickConnectionsPrivate), parent)
132 {
133 }
134
135 QQuickConnections::~QQuickConnections()
136 {
137 }
138
139 /*!
140     \qmlproperty Object QtQuick2::Connections::target
141     This property holds the object that sends the signal.
142
143     If this property is not set, the \c target defaults to the parent of the Connection.
144
145     If set to null, no connection is made and any signal handlers are ignored
146     until the target is not null.
147 */
148 QObject *QQuickConnections::target() const
149 {
150     Q_D(const QQuickConnections);
151     return d->targetSet ? d->target : parent();
152 }
153
154 class QQmlBoundSignalDeleter : public QObject
155 {
156 public:
157     QQmlBoundSignalDeleter(QQmlBoundSignal *signal) : m_signal(signal) { m_signal->removeFromObject(); }
158     ~QQmlBoundSignalDeleter() { delete m_signal; }
159
160 private:
161     QQmlBoundSignal *m_signal;
162 };
163
164 void QQuickConnections::setTarget(QObject *obj)
165 {
166     Q_D(QQuickConnections);
167     d->targetSet = true; // even if setting to 0, it is *set*
168     if (d->target == obj)
169         return;
170     foreach (QQmlBoundSignal *s, d->boundsignals) {
171         // It is possible that target is being changed due to one of our signal
172         // handlers -> use deleteLater().
173         if (s->isEvaluating())
174             (new QQmlBoundSignalDeleter(s))->deleteLater();
175         else
176             delete s;
177     }
178     d->boundsignals.clear();
179     d->target = obj;
180     connectSignals();
181     emit targetChanged();
182 }
183
184 /*!
185     \qmlproperty bool QtQuick2::Connections::ignoreUnknownSignals
186
187     Normally, a connection to a non-existent signal produces runtime errors.
188
189     If this property is set to \c true, such errors are ignored.
190     This is useful if you intend to connect to different types of objects, handling
191     a different set of signals for each object.
192 */
193 bool QQuickConnections::ignoreUnknownSignals() const
194 {
195     Q_D(const QQuickConnections);
196     return d->ignoreUnknownSignals;
197 }
198
199 void QQuickConnections::setIgnoreUnknownSignals(bool ignore)
200 {
201     Q_D(QQuickConnections);
202     d->ignoreUnknownSignals = ignore;
203 }
204
205
206
207 QByteArray
208 QQmlConnectionsParser::compile(const QList<QQmlCustomParserProperty> &props)
209 {
210     QByteArray rv;
211     QDataStream ds(&rv, QIODevice::WriteOnly);
212
213     for(int ii = 0; ii < props.count(); ++ii)
214     {
215         QString propName = props.at(ii).name();
216         int propLine = props.at(ii).location().line;
217         int propColumn = props.at(ii).location().column;
218
219         if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) {
220             error(props.at(ii), QQuickConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName));
221             return QByteArray();
222         }
223
224         QList<QVariant> values = props.at(ii).assignedValues();
225
226         for (int i = 0; i < values.count(); ++i) {
227             const QVariant &value = values.at(i);
228
229             if (value.userType() == qMetaTypeId<QQmlCustomParserNode>()) {
230                 error(props.at(ii), QQuickConnections::tr("Connections: nested objects not allowed"));
231                 return QByteArray();
232             } else if (value.userType() == qMetaTypeId<QQmlCustomParserProperty>()) {
233                 error(props.at(ii), QQuickConnections::tr("Connections: syntax error"));
234                 return QByteArray();
235             } else {
236                 QQmlScript::Variant v = qvariant_cast<QQmlScript::Variant>(value);
237                 if (v.isScript()) {
238                     ds << propName;
239                     ds << rewriteSignalHandler(v, propName);
240                     ds << propLine;
241                     ds << propColumn;
242                 } else {
243                     error(props.at(ii), QQuickConnections::tr("Connections: script expected"));
244                     return QByteArray();
245                 }
246             }
247         }
248     }
249
250     return rv;
251 }
252
253 void QQmlConnectionsParser::setCustomData(QObject *object,
254                                             const QByteArray &data)
255 {
256     QQuickConnectionsPrivate *p =
257         static_cast<QQuickConnectionsPrivate *>(QObjectPrivate::get(object));
258     p->data = data;
259 }
260
261
262 void QQuickConnections::connectSignals()
263 {
264     Q_D(QQuickConnections);
265     if (!d->componentcomplete || (d->targetSet && !target()))
266         return;
267
268     QDataStream ds(d->data);
269     while (!ds.atEnd()) {
270         QString propName;
271         ds >> propName;
272         QString script;
273         ds >> script;
274         int line;
275         ds >> line;
276         int column;
277         ds >> column;
278
279         QQmlProperty prop(target(), propName);
280         if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) {
281             QQmlBoundSignal *signal =
282                 new QQmlBoundSignal(target(), prop.method(), this);
283
284             QString location;
285             QQmlContextData *ctxtdata = 0;
286             QQmlData *ddata = QQmlData::get(this);
287             if (ddata) {
288                 ctxtdata = ddata->outerContext;
289                 if (ctxtdata && !ctxtdata->url.isEmpty())
290                     location = ddata->outerContext->urlString;
291             }
292
293             QQmlBoundSignalExpression *expression = ctxtdata ?
294                 new QQmlBoundSignalExpression(ctxtdata, 0, script, true, location, line, column) : 0;
295             signal->takeExpression(expression);
296             d->boundsignals += signal;
297         } else {
298             if (!d->ignoreUnknownSignals)
299                 qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName);
300         }
301     }
302 }
303
304 void QQuickConnections::classBegin()
305 {
306     Q_D(QQuickConnections);
307     d->componentcomplete=false;
308 }
309
310 void QQuickConnections::componentComplete()
311 {
312     Q_D(QQuickConnections);
313     d->componentcomplete=true;
314     connectSignals();
315 }
316
317 QT_END_NAMESPACE