Update copyright year in license headers.
[profile/ivi/qtdeclarative.git] / src / quick / util / qdeclarativeconnections.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 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 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarativeconnections_p.h"
43
44 #include <private/qdeclarativeexpression_p.h>
45 #include <private/qdeclarativeproperty_p.h>
46 #include <private/qdeclarativeboundsignal_p.h>
47 #include <qdeclarativecontext.h>
48 #include <private/qdeclarativecontext_p.h>
49 #include <qdeclarativeinfo.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 QDeclarativeConnectionsPrivate : public QObjectPrivate
59 {
60 public:
61     QDeclarativeConnectionsPrivate() : target(0), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {}
62
63     QList<QDeclarativeBoundSignal*> boundsignals;
64     QObject *target;
65
66     bool targetSet;
67     bool ignoreUnknownSignals;
68     bool componentcomplete;
69
70     QByteArray data;
71 };
72
73 /*!
74     \qmlclass Connections QDeclarativeConnections
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         \i Multiple connections to the same signal are required
95         \i Creating connections outside the scope of the signal sender
96         \i 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 QtDeclarative
129 */
130 QDeclarativeConnections::QDeclarativeConnections(QObject *parent) :
131     QObject(*(new QDeclarativeConnectionsPrivate), parent)
132 {
133 }
134
135 QDeclarativeConnections::~QDeclarativeConnections()
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 *QDeclarativeConnections::target() const
149 {
150     Q_D(const QDeclarativeConnections);
151     return d->targetSet ? d->target : parent();
152 }
153
154 void QDeclarativeConnections::setTarget(QObject *obj)
155 {
156     Q_D(QDeclarativeConnections);
157     d->targetSet = true; // even if setting to 0, it is *set*
158     if (d->target == obj)
159         return;
160     foreach (QDeclarativeBoundSignal *s, d->boundsignals) {
161         // It is possible that target is being changed due to one of our signal
162         // handlers -> use deleteLater().
163         if (s->isEvaluating())
164             s->deleteLater();
165         else
166             delete s;
167     }
168     d->boundsignals.clear();
169     d->target = obj;
170     connectSignals();
171     emit targetChanged();
172 }
173
174 /*!
175     \qmlproperty bool QtQuick2::Connections::ignoreUnknownSignals
176
177     Normally, a connection to a non-existent signal produces runtime errors.
178
179     If this property is set to \c true, such errors are ignored.
180     This is useful if you intend to connect to different types of objects, handling
181     a different set of signals for each object.
182 */
183 bool QDeclarativeConnections::ignoreUnknownSignals() const
184 {
185     Q_D(const QDeclarativeConnections);
186     return d->ignoreUnknownSignals;
187 }
188
189 void QDeclarativeConnections::setIgnoreUnknownSignals(bool ignore)
190 {
191     Q_D(QDeclarativeConnections);
192     d->ignoreUnknownSignals = ignore;
193 }
194
195
196
197 QByteArray
198 QDeclarativeConnectionsParser::compile(const QList<QDeclarativeCustomParserProperty> &props)
199 {
200     QByteArray rv;
201     QDataStream ds(&rv, QIODevice::WriteOnly);
202
203     for(int ii = 0; ii < props.count(); ++ii)
204     {
205         QString propName = props.at(ii).name();
206         int propLine = props.at(ii).location().line;
207
208         if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) {
209             error(props.at(ii), QDeclarativeConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName));
210             return QByteArray();
211         }
212
213         QList<QVariant> values = props.at(ii).assignedValues();
214
215         for (int i = 0; i < values.count(); ++i) {
216             const QVariant &value = values.at(i);
217
218             if (value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) {
219                 error(props.at(ii), QDeclarativeConnections::tr("Connections: nested objects not allowed"));
220                 return QByteArray();
221             } else if (value.userType() == qMetaTypeId<QDeclarativeCustomParserProperty>()) {
222                 error(props.at(ii), QDeclarativeConnections::tr("Connections: syntax error"));
223                 return QByteArray();
224             } else {
225                 QDeclarativeScript::Variant v = qvariant_cast<QDeclarativeScript::Variant>(value);
226                 if (v.isScript()) {
227                     ds << propName;
228                     ds << rewriteSignalHandler(v.asScript(), propName);
229                     ds << propLine;
230                 } else {
231                     error(props.at(ii), QDeclarativeConnections::tr("Connections: script expected"));
232                     return QByteArray();
233                 }
234             }
235         }
236     }
237
238     return rv;
239 }
240
241 void QDeclarativeConnectionsParser::setCustomData(QObject *object,
242                                             const QByteArray &data)
243 {
244     QDeclarativeConnectionsPrivate *p =
245         static_cast<QDeclarativeConnectionsPrivate *>(QObjectPrivate::get(object));
246     p->data = data;
247 }
248
249
250 void QDeclarativeConnections::connectSignals()
251 {
252     Q_D(QDeclarativeConnections);
253     if (!d->componentcomplete || (d->targetSet && !target()))
254         return;
255
256     QDataStream ds(d->data);
257     while (!ds.atEnd()) {
258         QString propName;
259         ds >> propName;
260         QString script;
261         ds >> script;
262         int line;
263         ds >> line;
264         QDeclarativeProperty prop(target(), propName);
265         if (prop.isValid() && (prop.type() & QDeclarativeProperty::SignalProperty)) {
266             QDeclarativeBoundSignal *signal =
267                 new QDeclarativeBoundSignal(target(), prop.method(), this);
268
269             QString location;
270             QDeclarativeContextData *ctxtdata = 0;
271             QDeclarativeData *ddata = QDeclarativeData::get(this);
272             if (ddata) {
273                 ctxtdata = ddata->outerContext;
274                 if (ctxtdata && !ctxtdata->url.isEmpty())
275                     location = ddata->outerContext->url.toString();
276             }
277
278             QDeclarativeExpression *expression = ctxtdata ?
279                 QDeclarativeExpressionPrivate::create(ctxtdata, 0, script, true, location, line) : 0;
280             signal->setExpression(expression);
281             d->boundsignals += signal;
282         } else {
283             if (!d->ignoreUnknownSignals)
284                 qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName);
285         }
286     }
287 }
288
289 void QDeclarativeConnections::classBegin()
290 {
291     Q_D(QDeclarativeConnections);
292     d->componentcomplete=false;
293 }
294
295 void QDeclarativeConnections::componentComplete()
296 {
297     Q_D(QDeclarativeConnections);
298     d->componentcomplete=true;
299     connectSignals();
300 }
301
302 QT_END_NAMESPACE