1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qqmllistaccessor_p.h" |
5 | |
6 | #include <private/qqmlmetatype_p.h> |
7 | |
8 | #include <QtCore/qstringlist.h> |
9 | #include <QtCore/qdebug.h> |
10 | #include <QtCore/qurl.h> |
11 | |
12 | // ### Remove me |
13 | #include <private/qqmlengine_p.h> |
14 | |
15 | QT_BEGIN_NAMESPACE |
16 | |
17 | QQmlListAccessor::QQmlListAccessor() |
18 | : m_type(Invalid) |
19 | { |
20 | } |
21 | |
22 | QQmlListAccessor::~QQmlListAccessor() |
23 | { |
24 | } |
25 | |
26 | QVariant QQmlListAccessor::list() const |
27 | { |
28 | return d; |
29 | } |
30 | |
31 | void QQmlListAccessor::setList(const QVariant &v) |
32 | { |
33 | d = v; |
34 | |
35 | // An incoming JS array as model is treated as a variant list, so we need to |
36 | // convert it first with toVariant(). |
37 | QMetaType variantsType = d.metaType(); |
38 | if (variantsType == QMetaType::fromType<QJSValue>()) { |
39 | d = d.value<QJSValue>().toVariant(); |
40 | variantsType = d.metaType(); |
41 | } |
42 | |
43 | if (!d.isValid()) { |
44 | m_type = Invalid; |
45 | } else if (variantsType == QMetaType::fromType<QStringList>()) { |
46 | m_type = StringList; |
47 | } else if (variantsType == QMetaType::fromType<QList<QUrl>>()) { |
48 | m_type = UrlList; |
49 | } else if (variantsType == QMetaType::fromType<QVariantList>()) { |
50 | m_type = VariantList; |
51 | } else if (variantsType == QMetaType::fromType<QList<QObject *>>()) { |
52 | m_type = ObjectList; |
53 | } else if (variantsType.flags() & QMetaType::IsQmlList) { |
54 | d = QVariant::fromValue(value: QQmlListReference(d)); |
55 | m_type = ListProperty; |
56 | } else if (variantsType == QMetaType::fromType<QQmlListReference>()) { |
57 | m_type = ListProperty; |
58 | } else if (variantsType.flags() & QMetaType::PointerToQObject) { |
59 | m_type = Instance; |
60 | } else if (int i = 0; [&](){bool ok = false; i = v.toInt(ok: &ok); return ok;}()) { |
61 | // Here we have to check for an upper limit, because down the line code might (well, will) |
62 | // allocate memory depending on the number of elements. The upper limit cannot be INT_MAX: |
63 | // QVector<QPointer<QQuickItem>> something; |
64 | // something.resize(count()); |
65 | // (See e.g. QQuickRepeater::regenerate()) |
66 | // This will allocate data along the lines of: |
67 | // sizeof(QPointer<QQuickItem>) * count() + QVector::headerSize |
68 | // So, doing an approximate round-down-to-nice-number, we get: |
69 | const int upperLimit = 100 * 1000 * 1000; |
70 | |
71 | if (i < 0) { |
72 | qWarning(msg: "Model size of %d is less than 0" , i); |
73 | m_type = Invalid; |
74 | } else if (i > upperLimit) { |
75 | qWarning(msg: "Model size of %d is bigger than the upper limit %d" , i, upperLimit); |
76 | m_type = Invalid; |
77 | } else { |
78 | m_type = Integer; |
79 | d = i; |
80 | } |
81 | } else { |
82 | const QQmlType type = QQmlMetaType::qmlListType(metaType: v.metaType()); |
83 | if (type.isSequentialContainer()) { |
84 | m_metaSequence = type.listMetaSequence(); |
85 | m_type = Sequence; |
86 | } else { |
87 | m_type = Instance; |
88 | } |
89 | } |
90 | } |
91 | |
92 | qsizetype QQmlListAccessor::count() const |
93 | { |
94 | switch(m_type) { |
95 | case StringList: |
96 | Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>()); |
97 | return reinterpret_cast<const QStringList *>(d.constData())->size(); |
98 | case UrlList: |
99 | Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>()); |
100 | return reinterpret_cast<const QList<QUrl> *>(d.constData())->size(); |
101 | case VariantList: |
102 | Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>()); |
103 | return reinterpret_cast<const QVariantList *>(d.constData())->size(); |
104 | case ObjectList: |
105 | Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>()); |
106 | return reinterpret_cast<const QList<QObject *> *>(d.constData())->size(); |
107 | case ListProperty: |
108 | Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>()); |
109 | return reinterpret_cast<const QQmlListReference *>(d.constData())->count(); |
110 | case Sequence: |
111 | Q_ASSERT(m_metaSequence != QMetaSequence()); |
112 | return m_metaSequence.size(container: d.constData()); |
113 | case Instance: |
114 | return 1; |
115 | case Integer: |
116 | return *reinterpret_cast<const int *>(d.constData()); |
117 | case Invalid: |
118 | return 0; |
119 | } |
120 | Q_UNREACHABLE_RETURN(0); |
121 | } |
122 | |
123 | QVariant QQmlListAccessor::at(qsizetype idx) const |
124 | { |
125 | Q_ASSERT(idx >= 0 && idx < count()); |
126 | switch(m_type) { |
127 | case StringList: |
128 | Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>()); |
129 | return QVariant::fromValue(value: reinterpret_cast<const QStringList *>(d.constData())->at(i: idx)); |
130 | case UrlList: |
131 | Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>()); |
132 | return QVariant::fromValue(value: reinterpret_cast<const QList<QUrl> *>(d.constData())->at(i: idx)); |
133 | case VariantList: |
134 | Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>()); |
135 | return reinterpret_cast<const QVariantList *>(d.constData())->at(i: idx); |
136 | case ObjectList: |
137 | Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>()); |
138 | return QVariant::fromValue(value: reinterpret_cast<const QList<QObject *> *>(d.constData())->at(i: idx)); |
139 | case ListProperty: |
140 | Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>()); |
141 | return QVariant::fromValue(value: reinterpret_cast<const QQmlListReference *>(d.constData())->at(idx)); |
142 | case Sequence: { |
143 | Q_ASSERT(m_metaSequence != QMetaSequence()); |
144 | QVariant result; |
145 | const QMetaType valueMetaType = m_metaSequence.valueMetaType(); |
146 | if (valueMetaType == QMetaType::fromType<QVariant>()) { |
147 | m_metaSequence.valueAtIndex(container: d.constData(), index: idx, result: &result); |
148 | } else { |
149 | result = QVariant(valueMetaType); |
150 | m_metaSequence.valueAtIndex(container: d.constData(), index: idx, result: result.data()); |
151 | } |
152 | return result; |
153 | } |
154 | case Instance: |
155 | return d; |
156 | case Integer: |
157 | return QVariant(idx); |
158 | case Invalid: |
159 | return QVariant(); |
160 | } |
161 | Q_UNREACHABLE_RETURN(QVariant()); |
162 | } |
163 | |
164 | void QQmlListAccessor::set(qsizetype idx, const QVariant &value) |
165 | { |
166 | Q_ASSERT(idx >= 0 && idx < count()); |
167 | switch (m_type) { |
168 | case StringList: |
169 | Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>()); |
170 | (*static_cast<QStringList *>(d.data()))[idx] = value.toString(); |
171 | break; |
172 | case UrlList: |
173 | Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>()); |
174 | (*static_cast<QList<QUrl> *>(d.data()))[idx] = value.value<QUrl>(); |
175 | break; |
176 | case VariantList: |
177 | Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>()); |
178 | (*static_cast<QVariantList *>(d.data()))[idx] = value; |
179 | break; |
180 | case ObjectList: |
181 | Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>()); |
182 | (*static_cast<QList<QObject *> *>(d.data()))[idx] = value.value<QObject *>(); |
183 | break; |
184 | case ListProperty: |
185 | Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>()); |
186 | static_cast<QQmlListReference *>(d.data())->replace(idx, value.value<QObject *>()); |
187 | break; |
188 | case Sequence: { |
189 | Q_ASSERT(m_metaSequence != QMetaSequence()); |
190 | const QMetaType valueMetaType = m_metaSequence.valueMetaType(); |
191 | if (valueMetaType == QMetaType::fromType<QVariant>()) { |
192 | m_metaSequence.setValueAtIndex(container: d.data(), index: idx, value: &value); |
193 | } else if (valueMetaType == value.metaType()) { |
194 | m_metaSequence.setValueAtIndex(container: d.data(), index: idx, value: value.constData()); |
195 | } else { |
196 | QVariant converted = value; |
197 | converted.convert(type: valueMetaType); |
198 | m_metaSequence.setValueAtIndex(container: d.data(), index: idx, value: converted.constData()); |
199 | } |
200 | break; |
201 | } |
202 | case Instance: |
203 | d = value; |
204 | break; |
205 | case Integer: |
206 | break;; |
207 | case Invalid: |
208 | break; |
209 | } |
210 | } |
211 | |
212 | bool QQmlListAccessor::isValid() const |
213 | { |
214 | return m_type != Invalid; |
215 | } |
216 | |
217 | QT_END_NAMESPACE |
218 | |