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 <private/qtreewidgetitemiterator_p.h>
5#include "qtreewidget.h"
6#include "qtreewidget_p.h"
7#include "qwidgetitemdata_p.h"
8
9QT_BEGIN_NAMESPACE
10
11/*!
12 \class QTreeWidgetItemIterator
13 \ingroup model-view
14 \inmodule QtWidgets
15
16 \brief The QTreeWidgetItemIterator class provides a way to iterate over the
17 items in a QTreeWidget instance.
18
19 The iterator will walk the items in a pre-order traversal order, thus visiting the
20 parent node \e before it continues to the child nodes.
21
22 For example, the following code examples each item in a tree, checking the
23 text in the first column against a user-specified search string:
24
25 \snippet qtreewidgetitemiterator-using/mainwindow.cpp 0
26
27 It is also possible to filter out certain types of node by passing certain
28 \l{IteratorFlag}{flags} to the constructor of QTreeWidgetItemIterator.
29
30 \sa QTreeWidget, {Model/View Programming}, QTreeWidgetItem
31*/
32
33/*!
34 Constructs an iterator for the same QTreeWidget as \a it. The
35 current iterator item is set to point on the current item of \a it.
36*/
37
38QTreeWidgetItemIterator::QTreeWidgetItemIterator(const QTreeWidgetItemIterator &it)
39 : d_ptr(new QTreeWidgetItemIteratorPrivate(*(it.d_ptr))),
40 current(it.current), flags(it.flags)
41{
42 Q_D(QTreeWidgetItemIterator);
43 Q_ASSERT(d->m_model);
44 d->m_model->iterators.append(t: this);
45}
46
47/*!
48 Constructs an iterator for the given \a widget that uses the specified \a flags
49 to determine which items are found during iteration.
50 The iterator is set to point to the first top-level item contained in the widget,
51 or the next matching item if the top-level item doesn't match the flags.
52
53 \sa QTreeWidgetItemIterator::IteratorFlag
54*/
55
56QTreeWidgetItemIterator::QTreeWidgetItemIterator(QTreeWidget *widget, IteratorFlags flags)
57: current(nullptr), flags(flags)
58{
59 Q_ASSERT(widget);
60 QTreeModel *model = qobject_cast<QTreeModel*>(object: widget->model());
61 Q_ASSERT(model);
62 d_ptr.reset(other: new QTreeWidgetItemIteratorPrivate(this, model));
63 model->iterators.append(t: this);
64 if (!model->rootItem->children.isEmpty()) current = model->rootItem->child(index: 0);
65 if (current && !matchesFlags(item: current))
66 ++(*this);
67}
68
69/*!
70 Constructs an iterator for the given \a item that uses the specified \a flags
71 to determine which items are found during iteration.
72 The iterator is set to point to \a item, or the next matching item if \a item
73 doesn't match the flags.
74
75 \sa QTreeWidgetItemIterator::IteratorFlag
76*/
77
78QTreeWidgetItemIterator::QTreeWidgetItemIterator(QTreeWidgetItem *item, IteratorFlags flags)
79 : d_ptr(new QTreeWidgetItemIteratorPrivate(
80 this, qobject_cast<QTreeModel*>(object: item->view->model()))),
81 current(item), flags(flags)
82{
83 Q_D(QTreeWidgetItemIterator);
84 Q_ASSERT(item);
85 QTreeModel *model = qobject_cast<QTreeModel*>(object: item->view->model());
86 Q_ASSERT(model);
87 model->iterators.append(t: this);
88
89 // Initialize m_currentIndex and m_parentIndex as it would be if we had traversed from
90 // the beginning.
91 QTreeWidgetItem *parent = item;
92 parent = parent->parent();
93 QTreeWidgetItem *root = d->m_model->rootItem;
94 d->m_currentIndex = (parent ? parent : root)->indexOfChild(achild: item);
95
96 while (parent) {
97 QTreeWidgetItem *itm = parent;
98 parent = parent->parent();
99 const int index = (parent ? parent : root)->indexOfChild(achild: itm);
100 d->m_parentIndex.prepend(t: index);
101 }
102
103 if (current && !matchesFlags(item: current))
104 ++(*this);
105}
106
107/*!
108 Destroys the iterator.
109*/
110
111QTreeWidgetItemIterator::~QTreeWidgetItemIterator()
112{
113 d_func()->m_model->iterators.removeAll(t: this);
114}
115
116/*!
117 Assignment. Makes a copy of \a it and returns a reference to its
118 iterator.
119*/
120
121QTreeWidgetItemIterator &QTreeWidgetItemIterator::operator=(const QTreeWidgetItemIterator &it)
122{
123 Q_D(QTreeWidgetItemIterator);
124 if (d_func()->m_model != it.d_func()->m_model) {
125 d_func()->m_model->iterators.removeAll(t: this);
126 it.d_func()->m_model->iterators.append(t: this);
127 }
128 current = it.current;
129 flags = it.flags;
130 d->operator=(other: *it.d_func());
131 return *this;
132}
133
134/*!
135 The prefix \c{++} operator (\c{++it}) advances the iterator to the next matching item
136 and returns a reference to the resulting iterator.
137 Sets the current pointer to \nullptr if the current item is the last matching item.
138*/
139
140QTreeWidgetItemIterator &QTreeWidgetItemIterator::operator++()
141{
142 if (current)
143 do {
144 current = d_func()->next(current);
145 } while (current && !matchesFlags(item: current));
146 return *this;
147}
148
149/*!
150 The prefix \c{--} operator (\c{--it}) advances the iterator to the previous matching item
151 and returns a reference to the resulting iterator.
152 Sets the current pointer to \nullptr if the current item is the first matching item.
153*/
154
155QTreeWidgetItemIterator &QTreeWidgetItemIterator::operator--()
156{
157 if (current)
158 do {
159 current = d_func()->previous(current);
160 } while (current && !matchesFlags(item: current));
161 return *this;
162}
163
164/*!
165 \internal
166*/
167bool QTreeWidgetItemIterator::matchesFlags(const QTreeWidgetItem *item) const
168{
169 if (!item)
170 return false;
171
172 if (flags == All)
173 return true;
174
175 {
176 Qt::ItemFlags itemFlags = item->flags();
177 if ((flags & Selectable) && !(itemFlags & Qt::ItemIsSelectable))
178 return false;
179 if ((flags & NotSelectable) && (itemFlags & Qt::ItemIsSelectable))
180 return false;
181 if ((flags & DragEnabled) && !(itemFlags & Qt::ItemIsDragEnabled))
182 return false;
183 if ((flags & DragDisabled) && (itemFlags & Qt::ItemIsDragEnabled))
184 return false;
185 if ((flags & DropEnabled) && !(itemFlags & Qt::ItemIsDropEnabled))
186 return false;
187 if ((flags & DropDisabled) && (itemFlags & Qt::ItemIsDropEnabled))
188 return false;
189 if ((flags & Enabled) && !(itemFlags & Qt::ItemIsEnabled))
190 return false;
191 if ((flags & Disabled) && (itemFlags & Qt::ItemIsEnabled))
192 return false;
193 if ((flags & Editable) && !(itemFlags & Qt::ItemIsEditable))
194 return false;
195 if ((flags & NotEditable) && (itemFlags & Qt::ItemIsEditable))
196 return false;
197 }
198
199 if (flags & (Checked|NotChecked)) {
200 // ### We only test the check state for column 0
201 Qt::CheckState check = item->checkState(column: 0);
202 // PartiallyChecked matches as Checked.
203 if ((flags & Checked) && (check == Qt::Unchecked))
204 return false;
205 if ((flags & NotChecked) && (check != Qt::Unchecked))
206 return false;
207 }
208
209 if ((flags & HasChildren) && !item->childCount())
210 return false;
211 if ((flags & NoChildren) && item->childCount())
212 return false;
213
214 if ((flags & Hidden) && !item->isHidden())
215 return false;
216 if ((flags & NotHidden) && item->isHidden())
217 return false;
218
219 if ((flags & Selected) && !item->isSelected())
220 return false;
221 if ((flags & Unselected) && item->isSelected())
222 return false;
223
224 return true;
225}
226
227/*
228 * Implementation of QTreeWidgetItemIteratorPrivate
229 */
230QTreeWidgetItem* QTreeWidgetItemIteratorPrivate::nextSibling(const QTreeWidgetItem* item) const
231{
232 Q_ASSERT(item);
233 QTreeWidgetItem *next = nullptr;
234 if (QTreeWidgetItem *par = item->parent()) {
235 int i = par->indexOfChild(achild: const_cast<QTreeWidgetItem*>(item));
236 next = par->child(index: i + 1);
237 } else {
238 QTreeWidget *tw = item->treeWidget();
239 int i = tw->indexOfTopLevelItem(item: const_cast<QTreeWidgetItem*>(item));
240 next = tw->topLevelItem(index: i + 1);
241 }
242 return next;
243}
244
245QTreeWidgetItem *QTreeWidgetItemIteratorPrivate::next(const QTreeWidgetItem *current)
246{
247 if (!current) return nullptr;
248
249 QTreeWidgetItem *next = nullptr;
250 if (current->childCount()) {
251 // walk the child
252 m_parentIndex.push(t: m_currentIndex);
253 m_currentIndex = 0;
254 next = current->child(index: 0);
255 } else {
256 // walk the sibling
257 QTreeWidgetItem *parent = current->parent();
258 next = parent ? parent->child(index: m_currentIndex + 1)
259 : m_model->rootItem->child(index: m_currentIndex + 1);
260 while (!next && parent) {
261 // if we had no sibling walk up the parent and try the sibling of that
262 parent = parent->parent();
263 m_currentIndex = m_parentIndex.pop();
264 next = parent ? parent->child(index: m_currentIndex + 1)
265 : m_model->rootItem->child(index: m_currentIndex + 1);
266 }
267 if (next) ++(m_currentIndex);
268 }
269 return next;
270}
271
272QTreeWidgetItem *QTreeWidgetItemIteratorPrivate::previous(const QTreeWidgetItem *current)
273{
274 if (!current) return nullptr;
275
276 QTreeWidgetItem *prev = nullptr;
277 // walk the previous sibling
278 QTreeWidgetItem *parent = current->parent();
279 prev = parent ? parent->child(index: m_currentIndex - 1)
280 : m_model->rootItem->child(index: m_currentIndex - 1);
281 if (prev) {
282 // Yes, we had a previous sibling but we need go down to the last leafnode.
283 --m_currentIndex;
284 while (prev && prev->childCount()) {
285 m_parentIndex.push(t: m_currentIndex);
286 m_currentIndex = prev->childCount() - 1;
287 prev = prev->child(index: m_currentIndex);
288 }
289 } else if (parent) {
290 m_currentIndex = m_parentIndex.pop();
291 prev = parent;
292 }
293 return prev;
294}
295
296void QTreeWidgetItemIteratorPrivate::ensureValidIterator(const QTreeWidgetItem *itemToBeRemoved)
297{
298 Q_Q(QTreeWidgetItemIterator);
299 Q_ASSERT(itemToBeRemoved);
300
301 if (!q->current) return;
302 QTreeWidgetItem *nextItem = q->current;
303
304 // Do not walk to the ancestor to find the other item if they have the same parent.
305 if (nextItem->parent() != itemToBeRemoved->parent()) {
306 while (nextItem->parent() && nextItem != itemToBeRemoved) {
307 nextItem = nextItem->parent();
308 }
309 }
310 // If the item to be removed is an ancestor of the current iterator item,
311 // we need to adjust the iterator.
312 if (nextItem == itemToBeRemoved) {
313 QTreeWidgetItem *parent = nextItem;
314 nextItem = nullptr;
315 while (parent && !nextItem) {
316 nextItem = nextSibling(item: parent);
317 parent = parent->parent();
318 }
319 if (nextItem) {
320 // Ooooh... Set the iterator to the next valid item
321 *q = QTreeWidgetItemIterator(nextItem, q->flags);
322 if (!(q->matchesFlags(item: nextItem))) ++(*q);
323 } else {
324 // set it to null.
325 q->current = nullptr;
326 m_parentIndex.clear();
327 return;
328 }
329 }
330 if (nextItem->parent() == itemToBeRemoved->parent()) {
331 // They have the same parent, i.e. we have to adjust the m_currentIndex member of the iterator
332 // if the deleted item is to the left of the nextItem.
333
334 QTreeWidgetItem *par = itemToBeRemoved->parent(); // We know they both have the same parent.
335 QTreeWidget *tw = itemToBeRemoved->treeWidget(); // ..and widget
336 int indexOfItemToBeRemoved = par ? par->indexOfChild(achild: const_cast<QTreeWidgetItem *>(itemToBeRemoved))
337 : tw->indexOfTopLevelItem(item: const_cast<QTreeWidgetItem *>(itemToBeRemoved));
338 int indexOfNextItem = par ? par->indexOfChild(achild: nextItem) : tw->indexOfTopLevelItem(item: nextItem);
339
340 if (indexOfItemToBeRemoved <= indexOfNextItem) {
341 // A sibling to the left of us was deleted, adjust the m_currentIndex member of the iterator.
342 // Note that the m_currentIndex will be wrong until the item is actually removed!
343 m_currentIndex--;
344 }
345 }
346}
347
348/*!
349 \fn const QTreeWidgetItemIterator QTreeWidgetItemIterator::operator++(int)
350
351 The postfix ++ operator (it++) advances the iterator to the next matching item
352 and returns an iterator to the previously current item.
353*/
354
355/*!
356 \fn QTreeWidgetItemIterator &QTreeWidgetItemIterator::operator+=(int n)
357
358 Makes the iterator go forward by \a n matching items. (If n is negative, the
359 iterator goes backward.)
360
361 If the current item is beyond the last item, the current item pointer is
362 set to \nullptr. Returns the resulting iterator.
363*/
364
365/*!
366 \fn const QTreeWidgetItemIterator QTreeWidgetItemIterator::operator--(int)
367
368 The postfix -- operator (it--) makes the preceding matching item current and returns an iterator to the previously current item.
369*/
370
371/*!
372 \fn QTreeWidgetItemIterator &QTreeWidgetItemIterator::operator-=(int n)
373
374 Makes the iterator go backward by \a n matching items. (If n is negative, the
375 iterator goes forward.)
376
377 If the current item is ahead of the last item, the current item pointer is
378 set to \nullptr. Returns the resulting iterator.
379*/
380
381/*!
382 \fn QTreeWidgetItem *QTreeWidgetItemIterator::operator*() const
383
384 Dereference operator. Returns a pointer to the current item.
385*/
386
387
388/*!
389 \enum QTreeWidgetItemIterator::IteratorFlag
390
391 These flags can be passed to a QTreeWidgetItemIterator constructor
392 (OR-ed together if more than one is used), so that the iterator
393 will only iterate over items that match the given flags.
394
395 \value All
396 \value Hidden
397 \value NotHidden
398 \value Selected
399 \value Unselected
400 \value Selectable
401 \value NotSelectable
402 \value DragEnabled
403 \value DragDisabled
404 \value DropEnabled
405 \value DropDisabled
406 \value HasChildren
407 \value NoChildren
408 \value Checked
409 \value NotChecked
410 \value Enabled
411 \value Disabled
412 \value Editable
413 \value NotEditable
414 \value UserFlag
415*/
416
417QT_END_NAMESPACE
418

source code of qtbase/src/widgets/itemviews/qtreewidgetitemiterator.cpp