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 | |
9 | QT_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 | |
38 | QTreeWidgetItemIterator::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 | |
56 | QTreeWidgetItemIterator::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 | |
78 | QTreeWidgetItemIterator::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 | |
111 | QTreeWidgetItemIterator::~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 | |
121 | QTreeWidgetItemIterator &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 | |
140 | QTreeWidgetItemIterator &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 | |
155 | QTreeWidgetItemIterator &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 | */ |
167 | bool 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 | */ |
230 | QTreeWidgetItem* 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 | |
245 | QTreeWidgetItem *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 | |
272 | QTreeWidgetItem *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 | |
296 | void 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 | |
417 | QT_END_NAMESPACE |
418 | |