1/****************************************************************************
2**
3** Copyright (C) 2009 Stephen Kelly <steveire@gmail.com>
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 "dynamictreemodel.h"
30
31#include <QtCore/QHash>
32#include <QtCore/QList>
33#include <QtCore/QTimer>
34#include <QtCore/QDebug>
35
36DynamicTreeModel::DynamicTreeModel(QObject *parent) :
37 QAbstractItemModel(parent),
38 nextId(1)
39{
40}
41
42QModelIndex DynamicTreeModel::index(int row, int column, const QModelIndex &parent) const
43{
44// if (column != 0)
45// return QModelIndex();
46
47 if (column < 0 || row < 0)
48 return QModelIndex();
49
50 QList<QList<qint64> > childIdColumns = m_childItems.value(akey: parent.internalId());
51
52 const qint64 grandParent = findParentId(searchId: parent.internalId());
53 if (grandParent >= 0) {
54 QList<QList<qint64> > parentTable = m_childItems.value(akey: grandParent);
55 if (parent.column() >= parentTable.size())
56 qFatal(msg: "%s: parent.column() must be less than parentTable.size()", Q_FUNC_INFO);
57 QList<qint64> parentSiblings = parentTable.at(i: parent.column());
58 if (parent.row() >= parentSiblings.size())
59 qFatal(msg: "%s: parent.row() must be less than parentSiblings.size()", Q_FUNC_INFO);
60 }
61
62 if (childIdColumns.size() == 0)
63 return QModelIndex();
64
65 if (column >= childIdColumns.size())
66 return QModelIndex();
67
68 QList<qint64> rowIds = childIdColumns.at(i: column);
69
70 if (row >= rowIds.size())
71 return QModelIndex();
72
73 qint64 id = rowIds.at(i: row);
74
75 return createIndex(arow: row, acolumn: column, adata: reinterpret_cast<void *>(id));
76}
77
78qint64 DynamicTreeModel::findParentId(qint64 searchId) const
79{
80 if (searchId <= 0)
81 return -1;
82
83 for (auto i = m_childItems.cbegin(), end = m_childItems.cend(); i != end; ++i) {
84 for (const auto &list : i.value()) {
85 if (list.contains(t: searchId))
86 return i.key();
87 }
88 }
89 return -1;
90}
91
92QModelIndex DynamicTreeModel::parent(const QModelIndex &index) const
93{
94 if (!index.isValid())
95 return QModelIndex();
96
97 qint64 searchId = index.internalId();
98 qint64 parentId = findParentId(searchId);
99 // Will never happen for valid index, but what the hey...
100 if (parentId <= 0)
101 return QModelIndex();
102
103 qint64 grandParentId = findParentId(searchId: parentId);
104 if (grandParentId < 0)
105 grandParentId = 0;
106
107 int column = 0;
108 QList<qint64> childList = m_childItems.value(akey: grandParentId).at(i: column);
109
110 int row = childList.indexOf(t: parentId);
111
112 return createIndex(arow: row, acolumn: column, adata: reinterpret_cast<void *>(parentId));
113}
114
115int DynamicTreeModel::rowCount(const QModelIndex &index) const
116{
117 QList<QList<qint64> > cols = m_childItems.value(akey: index.internalId());
118
119 if (cols.size() == 0)
120 return 0;
121
122 if (index.column() > 0)
123 return 0;
124
125 return cols.at(i: 0).size();
126}
127
128int DynamicTreeModel::columnCount(const QModelIndex &index) const
129{
130// Q_UNUSED(index);
131 return m_childItems.value(akey: index.internalId()).size();
132}
133
134QVariant DynamicTreeModel::data(const QModelIndex &index, int role) const
135{
136 if (!index.isValid())
137 return QVariant();
138
139 if (Qt::DisplayRole == role)
140 return m_items.value(akey: index.internalId());
141 return QVariant();
142}
143
144void DynamicTreeModel::clear()
145{
146 beginResetModel();
147 m_items.clear();
148 m_childItems.clear();
149 nextId = 1;
150 endResetModel();
151}
152
153ModelChangeCommand::ModelChangeCommand(DynamicTreeModel *model, QObject *parent) :
154 QObject(parent),
155 m_model(model),
156 m_numCols(1),
157 m_startRow(-1),
158 m_endRow(-1)
159{
160}
161
162QModelIndex ModelChangeCommand::findIndex(const QList<int> &rows) const
163{
164 const int col = 0;
165 QModelIndex parent = QModelIndex();
166 for (int row : rows) {
167 parent = m_model->index(row, column: col, parent);
168 if (!parent.isValid())
169 qFatal(msg: "%s: parent must be valid", Q_FUNC_INFO);
170 }
171 return parent;
172}
173
174ModelInsertCommand::ModelInsertCommand(DynamicTreeModel *model, QObject *parent) :
175 ModelChangeCommand(model, parent)
176{
177}
178
179void ModelInsertCommand::doCommand()
180{
181 QModelIndex parent = findIndex(rows: m_rowNumbers);
182 m_model->beginInsertRows(parent, first: m_startRow, last: m_endRow);
183 qint64 parentId = parent.internalId();
184 for (int row = m_startRow; row <= m_endRow; row++) {
185 for (int col = 0; col < m_numCols; col++) {
186 if (m_model->m_childItems[parentId].size() <= col)
187 m_model->m_childItems[parentId].append(t: QList<qint64>());
188// QString name = QUuid::createUuid().toString();
189 qint64 id = m_model->newId();
190 QString name = QString::number(id);
191
192 m_model->m_items.insert(akey: id, avalue: name);
193 m_model->m_childItems[parentId][col].insert(i: row, t: id);
194 }
195 }
196 m_model->endInsertRows();
197}
198
199ModelMoveCommand::ModelMoveCommand(DynamicTreeModel *model, QObject *parent) :
200 ModelChangeCommand(model, parent)
201{
202}
203
204bool ModelMoveCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd,
205 const QModelIndex &destParent, int destRow)
206{
207 return m_model->beginMoveRows(sourceParent: srcParent, sourceFirst: srcStart, sourceLast: srcEnd, destinationParent: destParent, destinationRow: destRow);
208}
209
210void ModelMoveCommand::doCommand()
211{
212 QModelIndex srcParent = findIndex(rows: m_rowNumbers);
213 QModelIndex destParent = findIndex(rows: m_destRowNumbers);
214
215 if (!emitPreSignal(srcParent, srcStart: m_startRow, srcEnd: m_endRow, destParent, destRow: m_destRow))
216 return;
217
218 for (int column = 0; column < m_numCols; ++column) {
219 QList<qint64> l = m_model->m_childItems.value(akey: srcParent.internalId())[column].mid(
220 pos: m_startRow, alength: m_endRow - m_startRow + 1);
221
222 for (int i = m_startRow; i <= m_endRow; i++)
223 m_model->m_childItems[srcParent.internalId()][column].removeAt(i: m_startRow);
224 int d;
225 if (m_destRow < m_startRow) {
226 d = m_destRow;
227 } else {
228 if (srcParent == destParent)
229 d = m_destRow - (m_endRow - m_startRow + 1);
230 else
231 d = m_destRow;
232 }
233
234 foreach (const qint64 id, l)
235 m_model->m_childItems[destParent.internalId()][column].insert(i: d++, t: id);
236 }
237
238 emitPostSignal();
239}
240
241void ModelMoveCommand::emitPostSignal()
242{
243 m_model->endMoveRows();
244}
245
246ModelResetCommand::ModelResetCommand(DynamicTreeModel *model, QObject *parent) :
247 ModelMoveCommand(model, parent)
248{
249}
250
251ModelResetCommand::~ModelResetCommand()
252{
253}
254
255bool ModelResetCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd,
256 const QModelIndex &destParent, int destRow)
257{
258 Q_UNUSED(srcParent);
259 Q_UNUSED(srcStart);
260 Q_UNUSED(srcEnd);
261 Q_UNUSED(destParent);
262 Q_UNUSED(destRow);
263
264 return true;
265}
266
267void ModelResetCommand::emitPostSignal()
268{
269 m_model->beginResetModel();
270 m_model->endResetModel();
271}
272
273ModelResetCommandFixed::ModelResetCommandFixed(DynamicTreeModel *model, QObject *parent) :
274 ModelMoveCommand(model, parent)
275{
276}
277
278ModelResetCommandFixed::~ModelResetCommandFixed()
279{
280}
281
282bool ModelResetCommandFixed::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd,
283 const QModelIndex &destParent, int destRow)
284{
285 Q_UNUSED(srcParent);
286 Q_UNUSED(srcStart);
287 Q_UNUSED(srcEnd);
288 Q_UNUSED(destParent);
289 Q_UNUSED(destRow);
290
291 m_model->beginResetModel();
292 return true;
293}
294
295void ModelResetCommandFixed::emitPostSignal()
296{
297 m_model->endResetModel();
298}
299
300ModelChangeChildrenLayoutsCommand::ModelChangeChildrenLayoutsCommand(DynamicTreeModel *model,
301 QObject *parent) :
302 ModelChangeCommand(model, parent)
303{
304}
305
306void ModelChangeChildrenLayoutsCommand::doCommand()
307{
308 const QPersistentModelIndex parent1 = findIndex(rows: m_rowNumbers);
309 const QPersistentModelIndex parent2 = findIndex(rows: m_secondRowNumbers);
310
311 QList<QPersistentModelIndex> parents;
312 parents << parent1;
313 parents << parent2;
314
315 emit m_model->layoutAboutToBeChanged(parents);
316
317 int rowSize1 = -1;
318 int rowSize2 = -1;
319
320 for (int column = 0; column < m_numCols; ++column) {
321 {
322 QList<qint64> &l = m_model->m_childItems[parent1.internalId()][column];
323 rowSize1 = l.size();
324 l.prepend(t: l.takeLast());
325 }
326 {
327 QList<qint64> &l = m_model->m_childItems[parent2.internalId()][column];
328 rowSize2 = l.size();
329 l.append(t: l.takeFirst());
330 }
331 }
332
333 // If we're changing one of the parent indexes, we need to ensure that we do that before
334 // changing any children of that parent. The reason is that we're keeping parent1 and parent2
335 // around as QPersistentModelIndex instances, and we query idx.parent() in the loop.
336 QModelIndexList persistent = m_model->persistentIndexList();
337 foreach (const QModelIndex &parent, parents) {
338 int idx = persistent.indexOf(t: parent);
339 if (idx != -1)
340 persistent.move(from: idx, to: 0);
341 }
342
343 foreach (const QModelIndex &idx, persistent) {
344 if (idx.parent() == parent1) {
345 if (idx.row() == rowSize1 - 1) {
346 m_model->changePersistentIndex(from: idx,
347 to: m_model->createIndex(arow: 0, acolumn: idx.column(),
348 adata: idx.internalPointer()));
349 } else {
350 m_model->changePersistentIndex(from: idx,
351 to: m_model->createIndex(arow: idx.row() + 1, acolumn: idx.column(),
352 adata: idx.internalPointer()));
353 }
354 } else if (idx.parent() == parent2) {
355 if (idx.row() == 0) {
356 m_model->changePersistentIndex(from: idx,
357 to: m_model->createIndex(arow: rowSize2 - 1, acolumn: idx.column(),
358 adata: idx.internalPointer()));
359 } else {
360 m_model->changePersistentIndex(from: idx,
361 to: m_model->createIndex(arow: idx.row() - 1, acolumn: idx.column(),
362 adata: idx.internalPointer()));
363 }
364 }
365 }
366
367 emit m_model->layoutChanged(parents);
368}
369

source code of qtbase/tests/auto/other/qabstractitemmodelutils/dynamictreemodel.cpp