Initial import from the monolithic Qt.
[profile/ivi/qtdeclarative.git] / src / declarative / util / qdeclarativeconnections.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/qdeclarativeconnections_p.h"
43
44 #include <qdeclarativeexpression.h>
45 #include <qdeclarativeproperty_p.h>
46 #include <qdeclarativeboundsignal_p.h>
47 #include <qdeclarativecontext.h>
48 #include <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     \ingroup qml-utility-elements
76     \since 4.7
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 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 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 = QString::fromUtf8(props.at(ii).name());
206         if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) {
207             error(props.at(ii), QDeclarativeConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName));
208             return QByteArray();
209         }
210
211         QList<QVariant> values = props.at(ii).assignedValues();
212
213         for (int i = 0; i < values.count(); ++i) {
214             const QVariant &value = values.at(i);
215
216             if (value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) {
217                 error(props.at(ii), QDeclarativeConnections::tr("Connections: nested objects not allowed"));
218                 return QByteArray();
219             } else if (value.userType() == qMetaTypeId<QDeclarativeCustomParserProperty>()) {
220                 error(props.at(ii), QDeclarativeConnections::tr("Connections: syntax error"));
221                 return QByteArray();
222             } else {
223                 QDeclarativeParser::Variant v = qvariant_cast<QDeclarativeParser::Variant>(value);
224                 if (v.isScript()) {
225                     ds << propName;
226                     ds << v.asScript();
227                 } else {
228                     error(props.at(ii), QDeclarativeConnections::tr("Connections: script expected"));
229                     return QByteArray();
230                 }
231             }
232         }
233     }
234
235     return rv;
236 }
237
238 void QDeclarativeConnectionsParser::setCustomData(QObject *object,
239                                             const QByteArray &data)
240 {
241     QDeclarativeConnectionsPrivate *p =
242         static_cast<QDeclarativeConnectionsPrivate *>(QObjectPrivate::get(object));
243     p->data = data;
244 }
245
246
247 void QDeclarativeConnections::connectSignals()
248 {
249     Q_D(QDeclarativeConnections);
250     if (!d->componentcomplete || (d->targetSet && !target()))
251         return;
252
253     QDataStream ds(d->data);
254     while (!ds.atEnd()) {
255         QString propName;
256         ds >> propName;
257         QString script;
258         ds >> script;
259         QDeclarativeProperty prop(target(), propName);
260         if (prop.isValid() && (prop.type() & QDeclarativeProperty::SignalProperty)) {
261             QDeclarativeBoundSignal *signal =
262                 new QDeclarativeBoundSignal(target(), prop.method(), this);
263             QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(this), 0, script);
264             QDeclarativeData *ddata = QDeclarativeData::get(this);
265             if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty())
266                 expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber);
267             signal->setExpression(expression);
268             d->boundsignals += signal;
269         } else {
270             if (!d->ignoreUnknownSignals)
271                 qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName);
272         }
273     }
274 }
275
276 void QDeclarativeConnections::classBegin()
277 {
278     Q_D(QDeclarativeConnections);
279     d->componentcomplete=false;
280 }
281
282 void QDeclarativeConnections::componentComplete()
283 {
284     Q_D(QDeclarativeConnections);
285     d->componentcomplete=true;
286     connectSignals();
287 }
288
289 QT_END_NAMESPACE