1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qqmlabstractbinding_p.h"
41 
42 #include <QtQml/qqmlinfo.h>
43 #include <private/qqmlbinding_p.h>
44 #include <private/qqmlvaluetypeproxybinding_p.h>
45 
46 QT_BEGIN_NAMESPACE
47 
QQmlAbstractBinding()48 QQmlAbstractBinding::QQmlAbstractBinding()
49     : m_targetIndex(-1)
50 {
51     Q_ASSERT(!isAddedToObject());
52 }
53 
~QQmlAbstractBinding()54 QQmlAbstractBinding::~QQmlAbstractBinding()
55 {
56     Q_ASSERT(!ref);
57     Q_ASSERT(!isAddedToObject());
58 
59     if (m_nextBinding.data() && !m_nextBinding->ref.deref())
60         delete m_nextBinding.data();
61 }
62 
63 /*!
64 Add this binding to \a object.
65 
66 This transfers ownership of the binding to the object, marks the object's property as
67 being bound.
68 
69 However, it does not enable the binding itself or call update() on it.
70 */
addToObject()71 void QQmlAbstractBinding::addToObject()
72 {
73     Q_ASSERT(!nextBinding());
74     Q_ASSERT(isAddedToObject() == false);
75 
76     QObject *obj = targetObject();
77     Q_ASSERT(obj);
78 
79     QQmlData *data = QQmlData::get(obj, true);
80 
81     int coreIndex = targetPropertyIndex().coreIndex();
82     if (targetPropertyIndex().hasValueTypeIndex()) {
83         // Value type
84 
85         // Find the value type proxy (if there is one)
86         QQmlValueTypeProxyBinding *proxy = nullptr;
87         if (data->hasBindingBit(coreIndex)) {
88             QQmlAbstractBinding *b = data->bindings;
89             while (b && (b->targetPropertyIndex().coreIndex() != coreIndex ||
90                          b->targetPropertyIndex().hasValueTypeIndex()))
91                 b = b->nextBinding();
92             Q_ASSERT(b && b->isValueTypeProxy());
93             proxy = static_cast<QQmlValueTypeProxyBinding *>(b);
94         }
95 
96         if (!proxy) {
97             proxy = new QQmlValueTypeProxyBinding(obj, QQmlPropertyIndex(coreIndex));
98 
99             Q_ASSERT(proxy->targetPropertyIndex().coreIndex() == coreIndex);
100             Q_ASSERT(!proxy->targetPropertyIndex().hasValueTypeIndex());
101             Q_ASSERT(proxy->targetObject() == obj);
102 
103             proxy->addToObject();
104         }
105 
106         setNextBinding(proxy->m_bindings.data());
107         proxy->m_bindings = this;
108 
109     } else {
110         setNextBinding(data->bindings);
111         if (data->bindings) {
112             data->bindings->ref.deref();
113             Q_ASSERT(data->bindings->ref.refCount > 0);
114         }
115         data->bindings = this;
116         ref.ref();
117 
118         data->setBindingBit(obj, coreIndex);
119     }
120 
121     setAddedToObject(true);
122 }
123 
124 /*!
125 Remove the binding from the object.
126 */
removeFromObject()127 void QQmlAbstractBinding::removeFromObject()
128 {
129     if (!isAddedToObject())
130         return;
131 
132     setAddedToObject(false);
133 
134     QObject *obj = targetObject();
135     QQmlData *data = QQmlData::get(obj, false);
136     Q_ASSERT(data);
137 
138     QQmlAbstractBinding::Ptr next;
139     next = nextBinding();
140     setNextBinding(nullptr);
141 
142     int coreIndex = targetPropertyIndex().coreIndex();
143     if (targetPropertyIndex().hasValueTypeIndex()) {
144 
145         // Find the value type binding
146         QQmlAbstractBinding *vtbinding = data->bindings;
147         while (vtbinding && (vtbinding->targetPropertyIndex().coreIndex() != coreIndex ||
148                              vtbinding->targetPropertyIndex().hasValueTypeIndex())) {
149             vtbinding = vtbinding->nextBinding();
150             Q_ASSERT(vtbinding);
151         }
152         Q_ASSERT(vtbinding->isValueTypeProxy());
153 
154         QQmlValueTypeProxyBinding *vtproxybinding =
155             static_cast<QQmlValueTypeProxyBinding *>(vtbinding);
156 
157         QQmlAbstractBinding *binding = vtproxybinding->m_bindings.data();
158         if (binding == this) {
159             vtproxybinding->m_bindings = next;
160         } else {
161            while (binding->nextBinding() != this) {
162               binding = binding->nextBinding();
163               Q_ASSERT(binding);
164            }
165            binding->setNextBinding(next.data());
166         }
167 
168         // Value type - we don't remove the proxy from the object.  It will sit their happily
169         // doing nothing until it is removed by a write, a binding change or it is reused
170         // to hold more sub-bindings.
171         return;
172     }
173 
174     if (data->bindings == this) {
175         if (next.data())
176             next->ref.ref();
177         data->bindings = next.data();
178         if (!ref.deref())
179             delete this;
180     } else {
181         QQmlAbstractBinding *binding = data->bindings;
182         while (binding->nextBinding() != this) {
183             binding = binding->nextBinding();
184             Q_ASSERT(binding);
185         }
186         binding->setNextBinding(next.data());
187     }
188 
189     data->clearBindingBit(coreIndex);
190 }
191 
printBindingLoopError(QQmlProperty & prop)192 void QQmlAbstractBinding::printBindingLoopError(QQmlProperty &prop)
193 {
194     qmlWarning(prop.object()) << QString(QLatin1String("Binding loop detected for property \"%1\"")).arg(prop.name());
195 }
196 
expression() const197 QString QQmlAbstractBinding::expression() const
198 {
199     return QLatin1String("<Unknown>");
200 }
201 
isValueTypeProxy() const202 bool QQmlAbstractBinding::isValueTypeProxy() const
203 {
204     return false;
205 }
206 
207 QT_END_NAMESPACE
208