09c0daf01352cce415d9126592bb0cee7682a9eb
[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: http://www.qt-project.org/
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         int propColumn = props.at(ii).location().column;
208
209         if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) {
210             error(props.at(ii), QDeclarativeConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName));
211             return QByteArray();
212         }
213
214         QList<QVariant> values = props.at(ii).assignedValues();
215
216         for (int i = 0; i < values.count(); ++i) {
217             const QVariant &value = values.at(i);
218
219             if (value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) {
220                 error(props.at(ii), QDeclarativeConnections::tr("Connections: nested objects not allowed"));
221                 return QByteArray();
222             } else if (value.userType() == qMetaTypeId<QDeclarativeCustomParserProperty>()) {
223                 error(props.at(ii), QDeclarativeConnections::tr("Connections: syntax error"));
224                 return QByteArray();
225             } else {
226                 QDeclarativeScript::Variant v = qvariant_cast<QDeclarativeScript::Variant>(value);
227                 if (v.isScript()) {
228                     ds << propName;
229                     ds << rewriteSignalHandler(v.asScript(), propName);
230                     ds << propLine;
231                     ds << propColumn;
232                 } else {
233                     error(props.at(ii), QDeclarativeConnections::tr("Connections: script expected"));
234                     return QByteArray();
235                 }
236             }
237         }
238     }
239
240     return rv;
241 }
242
243 void QDeclarativeConnectionsParser::setCustomData(QObject *object,
244                                             const QByteArray &data)
245 {
246     QDeclarativeConnectionsPrivate *p =
247         static_cast<QDeclarativeConnectionsPrivate *>(QObjectPrivate::get(object));
248     p->data = data;
249 }
250
251
252 void QDeclarativeConnections::connectSignals()
253 {
254     Q_D(QDeclarativeConnections);
255     if (!d->componentcomplete || (d->targetSet && !target()))
256         return;
257
258     QDataStream ds(d->data);
259     while (!ds.atEnd()) {
260         QString propName;
261         ds >> propName;
262         QString script;
263         ds >> script;
264         int line;
265         ds >> line;
266         int column;
267         ds >> column;
268
269         QDeclarativeProperty prop(target(), propName);
270         if (prop.isValid() && (prop.type() & QDeclarativeProperty::SignalProperty)) {
271             QDeclarativeBoundSignal *signal =
272                 new QDeclarativeBoundSignal(target(), prop.method(), this);
273
274             QString location;
275             QDeclarativeContextData *ctxtdata = 0;
276             QDeclarativeData *ddata = QDeclarativeData::get(this);
277             if (ddata) {
278                 ctxtdata = ddata->outerContext;
279                 if (ctxtdata && !ctxtdata->url.isEmpty())
280                     location = ddata->outerContext->url.toString();
281             }
282
283             QDeclarativeExpression *expression = ctxtdata ?
284                 QDeclarativeExpressionPrivate::create(ctxtdata, 0, script, true, location, line, column) : 0;
285             signal->setExpression(expression);
286             d->boundsignals += signal;
287         } else {
288             if (!d->ignoreUnknownSignals)
289                 qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName);
290         }
291     }
292 }
293
294 void QDeclarativeConnections::classBegin()
295 {
296     Q_D(QDeclarativeConnections);
297     d->componentcomplete=false;
298 }
299
300 void QDeclarativeConnections::componentComplete()
301 {
302     Q_D(QDeclarativeConnections);
303     d->componentcomplete=true;
304     connectSignals();
305 }
306
307 QT_END_NAMESPACE