1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qquickconnections_p.h"
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>
51 #include <QtCore/qdebug.h>
52 #include <QtCore/qstringlist.h>
54 #include <private/qobject_p.h>
58 class QQuickConnectionsPrivate : public QObjectPrivate
61 QQuickConnectionsPrivate() : target(0), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {}
63 QList<QQmlBoundSignal*> boundsignals;
67 bool ignoreUnknownSignals;
68 bool componentcomplete;
74 \qmlclass Connections QQuickConnections
75 \inqmlmodule QtQuick 2
76 \ingroup qml-utility-elements
77 \brief A Connections element describes generalized connections to signals.
79 A Connections object creates a connection to a QML signal.
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:
86 onClicked: { foo(parameters) }
90 However, it is not possible to connect to a signal in this way in some
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
99 When any of these are needed, the Connections element can be used instead.
101 For example, the above code can be changed to use a Connections object,
107 onClicked: foo(parameters)
112 More generally, the Connections object can be a child of some object other than
113 the sender of the signal:
124 onClicked: foo(parameters)
130 QQuickConnections::QQuickConnections(QObject *parent) :
131 QObject(*(new QQuickConnectionsPrivate), parent)
135 QQuickConnections::~QQuickConnections()
140 \qmlproperty Object QtQuick2::Connections::target
141 This property holds the object that sends the signal.
143 If this property is not set, the \c target defaults to the parent of the Connection.
145 If set to null, no connection is made and any signal handlers are ignored
146 until the target is not null.
148 QObject *QQuickConnections::target() const
150 Q_D(const QQuickConnections);
151 return d->targetSet ? d->target : parent();
154 class QQmlBoundSignalDeleter : public QObject
157 QQmlBoundSignalDeleter(QQmlBoundSignal *signal) : m_signal(signal) { m_signal->removeFromObject(); }
158 ~QQmlBoundSignalDeleter() { delete m_signal; }
161 QQmlBoundSignal *m_signal;
164 void QQuickConnections::setTarget(QObject *obj)
166 Q_D(QQuickConnections);
167 d->targetSet = true; // even if setting to 0, it is *set*
168 if (d->target == obj)
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();
178 d->boundsignals.clear();
181 emit targetChanged();
185 \qmlproperty bool QtQuick2::Connections::ignoreUnknownSignals
187 Normally, a connection to a non-existent signal produces runtime errors.
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.
193 bool QQuickConnections::ignoreUnknownSignals() const
195 Q_D(const QQuickConnections);
196 return d->ignoreUnknownSignals;
199 void QQuickConnections::setIgnoreUnknownSignals(bool ignore)
201 Q_D(QQuickConnections);
202 d->ignoreUnknownSignals = ignore;
208 QQmlConnectionsParser::compile(const QList<QQmlCustomParserProperty> &props)
211 QDataStream ds(&rv, QIODevice::WriteOnly);
213 for(int ii = 0; ii < props.count(); ++ii)
215 QString propName = props.at(ii).name();
216 int propLine = props.at(ii).location().line;
217 int propColumn = props.at(ii).location().column;
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));
224 QList<QVariant> values = props.at(ii).assignedValues();
226 for (int i = 0; i < values.count(); ++i) {
227 const QVariant &value = values.at(i);
229 if (value.userType() == qMetaTypeId<QQmlCustomParserNode>()) {
230 error(props.at(ii), QQuickConnections::tr("Connections: nested objects not allowed"));
232 } else if (value.userType() == qMetaTypeId<QQmlCustomParserProperty>()) {
233 error(props.at(ii), QQuickConnections::tr("Connections: syntax error"));
236 QQmlScript::Variant v = qvariant_cast<QQmlScript::Variant>(value);
239 ds << rewriteSignalHandler(v, propName);
243 error(props.at(ii), QQuickConnections::tr("Connections: script expected"));
253 void QQmlConnectionsParser::setCustomData(QObject *object,
254 const QByteArray &data)
256 QQuickConnectionsPrivate *p =
257 static_cast<QQuickConnectionsPrivate *>(QObjectPrivate::get(object));
262 void QQuickConnections::connectSignals()
264 Q_D(QQuickConnections);
265 if (!d->componentcomplete || (d->targetSet && !target()))
268 QDataStream ds(d->data);
269 while (!ds.atEnd()) {
279 QQmlProperty prop(target(), propName);
280 if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) {
281 QQmlBoundSignal *signal =
282 new QQmlBoundSignal(target(), prop.method(), this);
285 QQmlContextData *ctxtdata = 0;
286 QQmlData *ddata = QQmlData::get(this);
288 ctxtdata = ddata->outerContext;
289 if (ctxtdata && !ctxtdata->url.isEmpty())
290 location = ddata->outerContext->urlString;
293 QQmlBoundSignalExpression *expression = ctxtdata ?
294 new QQmlBoundSignalExpression(ctxtdata, 0, script, true, location, line, column) : 0;
295 signal->takeExpression(expression);
296 d->boundsignals += signal;
298 if (!d->ignoreUnknownSignals)
299 qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName);
304 void QQuickConnections::classBegin()
306 Q_D(QQuickConnections);
307 d->componentcomplete=false;
310 void QQuickConnections::componentComplete()
312 Q_D(QQuickConnections);
313 d->componentcomplete=true;