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 test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtCore>
30#include <QAbstractItemModel>
31
32class TestModel: public QAbstractItemModel
33{
34 Q_OBJECT
35public:
36 TestModel(QObject *parent = 0): QAbstractItemModel(parent),
37 fetched(false), rows(10), cols(1), levels(INT_MAX), wrongIndex(false) { init(); }
38
39 TestModel(int _rows, int _cols, QObject *parent = 0): QAbstractItemModel(parent),
40 fetched(false), rows(_rows), cols(_cols), levels(INT_MAX), wrongIndex(false) { init(); }
41
42 enum {
43 NameRole = Qt::UserRole + 1
44 };
45
46 void init() {
47 decorationsEnabled = false;
48 alternateChildlessRows = true;
49 tree = new Node(rows);
50 }
51
52 inline qint32 level(const QModelIndex &index) const {
53 Node *n = (Node *)index.internalPointer();
54 if (!n)
55 return -1;
56 int l = -1;
57 while (n != tree) {
58 n = n->parent;
59 ++l;
60 }
61 return l;
62 }
63
64 void resetModel()
65 {
66 beginResetModel();
67 fetched = false;
68 delete tree;
69 tree = new Node(rows);
70 endResetModel();
71 }
72
73 QString displayData(const QModelIndex &idx) const
74 {
75 return QString("[%1,%2,%3,%4]").arg(a: idx.row()).arg(a: idx.column()).arg(a: idx.internalId()).arg(a: hasChildren(parent: idx));
76 }
77
78 bool canFetchMore(const QModelIndex &) const {
79 return !fetched;
80 }
81
82 void fetchMore(const QModelIndex &) {
83 fetched = true;
84 }
85
86 bool hasChildren(const QModelIndex &parent = QModelIndex()) const {
87 bool hasFetched = fetched;
88 fetched = true;
89 bool r = QAbstractItemModel::hasChildren(parent);
90 fetched = hasFetched;
91 return r;
92 }
93
94 int rowCount(const QModelIndex& parent = QModelIndex()) const {
95 if (!fetched)
96 qFatal(msg: "%s: rowCount should not be called before fetching", Q_FUNC_INFO);
97 if ((parent.column() > 0) || (level(index: parent) > levels)
98 || (alternateChildlessRows && parent.row() > 0 && (parent.row() & 1)))
99 return 0;
100 Node *n = (Node*)parent.internalPointer();
101 if (!n)
102 n = tree;
103 return n->children.count();
104 }
105
106 int columnCount(const QModelIndex& parent = QModelIndex()) const {
107 if ((parent.column() > 0) || (level(index: parent) > levels)
108 || (alternateChildlessRows && parent.row() > 0 && (parent.row() & 1)))
109 return 0;
110 return cols;
111 }
112
113 bool isEditable(const QModelIndex &index) const {
114 if (index.isValid())
115 return true;
116 return false;
117 }
118
119 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const
120 {
121 if (row < 0 || column < 0 || (level(index: parent) > levels) || column >= cols)
122 return QModelIndex();
123 Node *pn = (Node*)parent.internalPointer();
124 if (!pn)
125 pn = tree;
126 if (row >= pn->children.count())
127 return QModelIndex();
128
129 Node *n = pn->children.at(i: row);
130 if (!n) {
131 n = new Node(rows, pn);
132 pn->children[row] = n;
133 }
134 return createIndex(arow: row, acolumn: column, adata: n);
135 }
136
137 QModelIndex parent(const QModelIndex &index) const
138 {
139 Node *n = (Node *)index.internalPointer();
140 if (!n || n->parent == tree)
141 return QModelIndex();
142 Q_ASSERT(n->parent->parent);
143 int parentRow = n->parent->parent->children.indexOf(t: n->parent);
144 Q_ASSERT(parentRow != -1);
145 return createIndex(arow: parentRow, acolumn: 0, adata: n->parent);
146 }
147
148 QVariant data(const QModelIndex &idx, int role) const
149 {
150 if (!idx.isValid())
151 return QVariant();
152
153 Node *pn = (Node *)idx.internalPointer();
154 if (!pn)
155 pn = tree;
156 if (pn != tree)
157 pn = pn->parent;
158 if (idx.row() < 0 || idx.column() < 0 || idx.column() >= cols
159 || idx.row() >= pn->children.count()) {
160 wrongIndex = true;
161 qWarning(msg: "Invalid modelIndex [%d,%d,%p]", idx.row(), idx.column(),
162 idx.internalPointer());
163 return QVariant();
164 }
165
166 if (role == Qt::DisplayRole)
167 return displayData(idx);
168
169 if (role == NameRole)
170 return QString("Name-%1-%2-%3-%4").arg(a: idx.row()).arg(a: idx.column()).arg(a: idx.internalId()).arg(a: hasChildren(parent: idx));
171
172 return QVariant();
173 }
174
175 bool setData(const QModelIndex &index, const QVariant &value, int role)
176 {
177 Q_UNUSED(value);
178 QVector<int> changedRole(1, role);
179 emit dataChanged(topLeft: index, bottomRight: index, roles: changedRole);
180 return true;
181 }
182
183 void groupedSetData(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
184 {
185 emit dataChanged(topLeft, bottomRight, roles);
186 }
187
188 void changeLayout(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>())
189 {
190 emit layoutAboutToBeChanged(parents);
191 emit layoutChanged(parents);
192 }
193
194 Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex())
195 {
196 beginRemoveRows(parent, first: row, last: row + count - 1);
197 Node *n = (Node *)parent.internalPointer();
198 if (!n)
199 n = tree;
200 n->removeRows(row, count);
201 endRemoveRows();
202 return true;
203 }
204
205 void removeLastColumn()
206 {
207 beginRemoveColumns(parent: QModelIndex(), first: cols - 1, last: cols - 1);
208 --cols;
209 endRemoveColumns();
210 }
211
212 void removeAllColumns()
213 {
214 beginRemoveColumns(parent: QModelIndex(), first: 0, last: cols - 1);
215 cols = 0;
216 endRemoveColumns();
217 }
218
219 bool insertRows(int row, int count, const QModelIndex &parent)
220 {
221 beginInsertRows(parent, first: row, last: row + count - 1);
222 Node *n = (Node *)parent.internalPointer();
223 if (!n)
224 n = tree;
225 n->addRows(row, count);
226 endInsertRows();
227 return true;
228 }
229
230 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)
231 {
232 Q_ASSERT_X(sourceRow >= 0 && sourceRow < rowCount(sourceParent)
233 && count > 0 && sourceRow + count - 1 < rowCount(sourceParent)
234 && destinationChild >= 0 && destinationChild <= rowCount(destinationParent),
235 Q_FUNC_INFO, "Rows out of range.");
236 Q_ASSERT_X(!(sourceParent == destinationParent && destinationChild >= sourceRow && destinationChild < sourceRow + count),
237 Q_FUNC_INFO, "Moving rows onto themselves.");
238 if (!beginMoveRows(sourceParent, sourceFirst: sourceRow, sourceLast: sourceRow + count - 1, destinationParent, destinationRow: destinationChild))
239 return false;
240 Node *src = (Node *)sourceParent.internalPointer();
241 if (!src)
242 src = tree;
243 Node *dest = (Node *)destinationParent.internalPointer();
244 if (!dest)
245 dest = tree;
246 QVector<Node *> buffer = src->children.mid(pos: sourceRow, len: count);
247 if (src != dest) {
248 src->removeRows(row: sourceRow, count, keepAlive: true /* keep alive */);
249 dest->addRows(row: destinationChild, count);
250 } else {
251 QVector<Node *> &c = dest->children;
252 if (sourceRow < destinationChild) {
253 memmove(dest: &c[sourceRow], src: &c[sourceRow + count], n: sizeof(Node *) * (destinationChild - sourceRow - count));
254 destinationChild -= count;
255 } else {
256 memmove(dest: &c[destinationChild + count], src: &c[destinationChild], n: sizeof(Node *) * (sourceRow - destinationChild));
257 }
258 }
259 for (int i = 0; i < count; i++) {
260 Node *n = buffer[i];
261 n->parent = dest;
262 dest->children[i + destinationChild] = n;
263 }
264
265 endMoveRows();
266 return true;
267 }
268
269 void setDecorationsEnabled(bool enable)
270 {
271 decorationsEnabled = enable;
272 }
273
274 mutable bool fetched;
275 bool decorationsEnabled;
276 bool alternateChildlessRows;
277 int rows, cols;
278 int levels;
279 mutable bool wrongIndex;
280
281 struct Node {
282 Node *parent;
283 QVector<Node *> children;
284
285 Node(int rows, Node *p = 0) : parent(p)
286 {
287 addRows(row: 0, count: rows);
288 }
289
290 ~Node()
291 {
292 qDeleteAll(c: children);
293 }
294
295 void addRows(int row, int count)
296 {
297 if (count > 0) {
298 children.reserve(asize: children.count() + count);
299 children.insert(i: row, n: count, t: (Node *)0);
300 }
301 }
302
303 void removeRows(int row, int count, bool keepAlive = false)
304 {
305 int newCount = qMax(a: children.count() - count, b: 0);
306 int effectiveCountDiff = children.count() - newCount;
307 if (effectiveCountDiff > 0) {
308 if (!keepAlive)
309 for (int i = 0; i < effectiveCountDiff; i++)
310 delete children[i + row];
311 children.remove(i: row, n: effectiveCountDiff);
312 }
313 }
314 };
315
316 QHash<int, QByteArray> roleNames() const
317 {
318 QHash<int, QByteArray> rn = QAbstractItemModel::roleNames();
319 rn[NameRole] = "name";
320 return rn;
321 }
322
323 Node *tree;
324};
325

source code of qtquickcontrols/tests/auto/shared/testmodel.h