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 QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39#include "qtreeview.h"
40
41#include <qheaderview.h>
42#include <qitemdelegate.h>
43#include <qapplication.h>
44#include <qscrollbar.h>
45#include <qpainter.h>
46#include <qstack.h>
47#include <qstyle.h>
48#include <qstyleoption.h>
49#include <qevent.h>
50#include <qpen.h>
51#include <qdebug.h>
52#include <QMetaMethod>
53#include <private/qscrollbar_p.h>
54#ifndef QT_NO_ACCESSIBILITY
55#include <qaccessible.h>
56#endif
57
58#include <private/qtreeview_p.h>
59#include <private/qheaderview_p.h>
60
61#include <algorithm>
62
63QT_BEGIN_NAMESPACE
64
65/*!
66 \class QTreeView
67 \brief The QTreeView class provides a default model/view implementation of a tree view.
68
69 \ingroup model-view
70 \ingroup advanced
71 \inmodule QtWidgets
72
73 \image windows-treeview.png
74
75 A QTreeView implements a tree representation of items from a
76 model. This class is used to provide standard hierarchical lists that
77 were previously provided by the \c QListView class, but using the more
78 flexible approach provided by Qt's model/view architecture.
79
80 The QTreeView class is one of the \l{Model/View Classes} and is part of
81 Qt's \l{Model/View Programming}{model/view framework}.
82
83 QTreeView implements the interfaces defined by the
84 QAbstractItemView class to allow it to display data provided by
85 models derived from the QAbstractItemModel class.
86
87 It is simple to construct a tree view displaying data from a
88 model. In the following example, the contents of a directory are
89 supplied by a QFileSystemModel and displayed as a tree:
90
91 \snippet shareddirmodel/main.cpp 3
92 \snippet shareddirmodel/main.cpp 6
93
94 The model/view architecture ensures that the contents of the tree view
95 are updated as the model changes.
96
97 Items that have children can be in an expanded (children are
98 visible) or collapsed (children are hidden) state. When this state
99 changes a collapsed() or expanded() signal is emitted with the
100 model index of the relevant item.
101
102 The amount of indentation used to indicate levels of hierarchy is
103 controlled by the \l indentation property.
104
105 Headers in tree views are constructed using the QHeaderView class and can
106 be hidden using \c{header()->hide()}. Note that each header is configured
107 with its \l{QHeaderView::}{stretchLastSection} property set to true,
108 ensuring that the view does not waste any of the space assigned to it for
109 its header. If this value is set to true, this property will override the
110 resize mode set on the last section in the header.
111
112 By default, all columns in a tree view are movable except the first. To
113 disable movement of these columns, use QHeaderView's
114 \l {QHeaderView::}{setSectionsMovable()} function. For more information
115 about rearranging sections, see \l {Moving Header Sections}.
116
117 \section1 Key Bindings
118
119 QTreeView supports a set of key bindings that enable the user to
120 navigate in the view and interact with the contents of items:
121
122 \table
123 \header \li Key \li Action
124 \row \li Up \li Moves the cursor to the item in the same column on
125 the previous row. If the parent of the current item has no more rows to
126 navigate to, the cursor moves to the relevant item in the last row
127 of the sibling that precedes the parent.
128 \row \li Down \li Moves the cursor to the item in the same column on
129 the next row. If the parent of the current item has no more rows to
130 navigate to, the cursor moves to the relevant item in the first row
131 of the sibling that follows the parent.
132 \row \li Left \li Hides the children of the current item (if present)
133 by collapsing a branch.
134 \row \li Minus \li Same as LeftArrow.
135 \row \li Right \li Reveals the children of the current item (if present)
136 by expanding a branch.
137 \row \li Plus \li Same as RightArrow.
138 \row \li Asterisk \li Expands all children of the current item (if present).
139 \row \li PageUp \li Moves the cursor up one page.
140 \row \li PageDown \li Moves the cursor down one page.
141 \row \li Home \li Moves the cursor to an item in the same column of the first
142 row of the first top-level item in the model.
143 \row \li End \li Moves the cursor to an item in the same column of the last
144 row of the last top-level item in the model.
145 \row \li F2 \li In editable models, this opens the current item for editing.
146 The Escape key can be used to cancel the editing process and revert
147 any changes to the data displayed.
148 \endtable
149
150 \omit
151 Describe the expanding/collapsing concept if not covered elsewhere.
152 \endomit
153
154 \section1 Improving Performance
155
156 It is possible to give the view hints about the data it is handling in order
157 to improve its performance when displaying large numbers of items. One approach
158 that can be taken for views that are intended to display items with equal heights
159 is to set the \l uniformRowHeights property to true.
160
161 \sa QListView, QTreeWidget, {View Classes}, QAbstractItemModel, QAbstractItemView,
162 {Dir View Example}
163*/
164
165
166/*!
167 \fn void QTreeView::expanded(const QModelIndex &index)
168
169 This signal is emitted when the item specified by \a index is expanded.
170*/
171
172
173/*!
174 \fn void QTreeView::collapsed(const QModelIndex &index)
175
176 This signal is emitted when the item specified by \a index is collapsed.
177*/
178
179/*!
180 Constructs a tree view with a \a parent to represent a model's
181 data. Use setModel() to set the model.
182
183 \sa QAbstractItemModel
184*/
185QTreeView::QTreeView(QWidget *parent)
186 : QAbstractItemView(*new QTreeViewPrivate, parent)
187{
188 Q_D(QTreeView);
189 d->initialize();
190}
191
192/*!
193 \internal
194*/
195QTreeView::QTreeView(QTreeViewPrivate &dd, QWidget *parent)
196 : QAbstractItemView(dd, parent)
197{
198 Q_D(QTreeView);
199 d->initialize();
200}
201
202/*!
203 Destroys the tree view.
204*/
205QTreeView::~QTreeView()
206{
207}
208
209/*!
210 \reimp
211*/
212void QTreeView::setModel(QAbstractItemModel *model)
213{
214 Q_D(QTreeView);
215 if (model == d->model)
216 return;
217 if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
218 disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
219 this, SLOT(rowsRemoved(QModelIndex,int,int)));
220
221 disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_modelAboutToBeReset()));
222 }
223
224 if (d->selectionModel) { // support row editing
225 disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
226 d->model, SLOT(submit()));
227 disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
228 this, SLOT(rowsRemoved(QModelIndex,int,int)));
229 disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_modelAboutToBeReset()));
230 }
231 d->viewItems.clear();
232 d->expandedIndexes.clear();
233 d->hiddenIndexes.clear();
234 d->header->setModel(model);
235 QAbstractItemView::setModel(model);
236
237 // QAbstractItemView connects to a private slot
238 disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
239 this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
240 // do header layout after the tree
241 disconnect(d->model, SIGNAL(layoutChanged()),
242 d->header, SLOT(_q_layoutChanged()));
243 // QTreeView has a public slot for this
244 connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
245 this, SLOT(rowsRemoved(QModelIndex,int,int)));
246
247 connect(d->model, SIGNAL(modelAboutToBeReset()), SLOT(_q_modelAboutToBeReset()));
248
249 if (d->sortingEnabled)
250 d->_q_sortIndicatorChanged(header()->sortIndicatorSection(), header()->sortIndicatorOrder());
251}
252
253/*!
254 \reimp
255*/
256void QTreeView::setRootIndex(const QModelIndex &index)
257{
258 Q_D(QTreeView);
259 d->header->setRootIndex(index);
260 QAbstractItemView::setRootIndex(index);
261}
262
263/*!
264 \reimp
265*/
266void QTreeView::setSelectionModel(QItemSelectionModel *selectionModel)
267{
268 Q_D(QTreeView);
269 Q_ASSERT(selectionModel);
270 if (d->selectionModel) {
271 // support row editing
272 disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
273 d->model, SLOT(submit()));
274 }
275
276 d->header->setSelectionModel(selectionModel);
277 QAbstractItemView::setSelectionModel(selectionModel);
278
279 if (d->selectionModel) {
280 // support row editing
281 connect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
282 d->model, SLOT(submit()));
283 }
284}
285
286/*!
287 Returns the header for the tree view.
288
289 \sa QAbstractItemModel::headerData()
290*/
291QHeaderView *QTreeView::header() const
292{
293 Q_D(const QTreeView);
294 return d->header;
295}
296
297/*!
298 Sets the header for the tree view, to the given \a header.
299
300 The view takes ownership over the given \a header and deletes it
301 when a new header is set.
302
303 \sa QAbstractItemModel::headerData()
304*/
305void QTreeView::setHeader(QHeaderView *header)
306{
307 Q_D(QTreeView);
308 if (header == d->header || !header)
309 return;
310 if (d->header && d->header->parent() == this)
311 delete d->header;
312 d->header = header;
313 d->header->setParent(this);
314 d->header->setFirstSectionMovable(false);
315
316 if (!d->header->model()) {
317 d->header->setModel(d->model);
318 if (d->selectionModel)
319 d->header->setSelectionModel(d->selectionModel);
320 }
321
322 connect(d->header, SIGNAL(sectionResized(int,int,int)),
323 this, SLOT(columnResized(int,int,int)));
324 connect(d->header, SIGNAL(sectionMoved(int,int,int)),
325 this, SLOT(columnMoved()));
326 connect(d->header, SIGNAL(sectionCountChanged(int,int)),
327 this, SLOT(columnCountChanged(int,int)));
328 connect(d->header, SIGNAL(sectionHandleDoubleClicked(int)),
329 this, SLOT(resizeColumnToContents(int)));
330 connect(d->header, SIGNAL(geometriesChanged()),
331 this, SLOT(updateGeometries()));
332
333 setSortingEnabled(d->sortingEnabled);
334 d->updateGeometry();
335}
336
337/*!
338 \property QTreeView::autoExpandDelay
339 \brief The delay time before items in a tree are opened during a drag and drop operation.
340 \since 4.3
341
342 This property holds the amount of time in milliseconds that the user must wait over
343 a node before that node will automatically open or close. If the time is
344 set to less then 0 then it will not be activated.
345
346 By default, this property has a value of -1, meaning that auto-expansion is disabled.
347*/
348int QTreeView::autoExpandDelay() const
349{
350 Q_D(const QTreeView);
351 return d->autoExpandDelay;
352}
353
354void QTreeView::setAutoExpandDelay(int delay)
355{
356 Q_D(QTreeView);
357 d->autoExpandDelay = delay;
358}
359
360/*!
361 \property QTreeView::indentation
362 \brief indentation of the items in the tree view.
363
364 This property holds the indentation measured in pixels of the items for each
365 level in the tree view. For top-level items, the indentation specifies the
366 horizontal distance from the viewport edge to the items in the first column;
367 for child items, it specifies their indentation from their parent items.
368
369 By default, the value of this property is style dependent. Thus, when the style
370 changes, this property updates from it. Calling setIndentation() stops the updates,
371 calling resetIndentation() will restore default behavior.
372*/
373int QTreeView::indentation() const
374{
375 Q_D(const QTreeView);
376 return d->indent;
377}
378
379void QTreeView::setIndentation(int i)
380{
381 Q_D(QTreeView);
382 if (!d->customIndent || (i != d->indent)) {
383 d->indent = i;
384 d->customIndent = true;
385 d->viewport->update();
386 }
387}
388
389void QTreeView::resetIndentation()
390{
391 Q_D(QTreeView);
392 if (d->customIndent) {
393 d->updateIndentationFromStyle();
394 d->customIndent = false;
395 }
396}
397
398/*!
399 \property QTreeView::rootIsDecorated
400 \brief whether to show controls for expanding and collapsing top-level items
401
402 Items with children are typically shown with controls to expand and collapse
403 them, allowing their children to be shown or hidden. If this property is
404 false, these controls are not shown for top-level items. This can be used to
405 make a single level tree structure appear like a simple list of items.
406
407 By default, this property is \c true.
408*/
409bool QTreeView::rootIsDecorated() const
410{
411 Q_D(const QTreeView);
412 return d->rootDecoration;
413}
414
415void QTreeView::setRootIsDecorated(bool show)
416{
417 Q_D(QTreeView);
418 if (show != d->rootDecoration) {
419 d->rootDecoration = show;
420 d->viewport->update();
421 }
422}
423
424/*!
425 \property QTreeView::uniformRowHeights
426 \brief whether all items in the treeview have the same height
427
428 This property should only be set to true if it is guaranteed that all items
429 in the view has the same height. This enables the view to do some
430 optimizations.
431
432 The height is obtained from the first item in the view. It is updated
433 when the data changes on that item.
434
435 \note If the editor size hint is bigger than the cell size hint, then the
436 size hint of the editor will be used.
437
438 By default, this property is \c false.
439*/
440bool QTreeView::uniformRowHeights() const
441{
442 Q_D(const QTreeView);
443 return d->uniformRowHeights;
444}
445
446void QTreeView::setUniformRowHeights(bool uniform)
447{
448 Q_D(QTreeView);
449 d->uniformRowHeights = uniform;
450}
451
452/*!
453 \property QTreeView::itemsExpandable
454 \brief whether the items are expandable by the user.
455
456 This property holds whether the user can expand and collapse items
457 interactively.
458
459 By default, this property is \c true.
460
461*/
462bool QTreeView::itemsExpandable() const
463{
464 Q_D(const QTreeView);
465 return d->itemsExpandable;
466}
467
468void QTreeView::setItemsExpandable(bool enable)
469{
470 Q_D(QTreeView);
471 d->itemsExpandable = enable;
472}
473
474/*!
475 \property QTreeView::expandsOnDoubleClick
476 \since 4.4
477 \brief whether the items can be expanded by double-clicking.
478
479 This property holds whether the user can expand and collapse items
480 by double-clicking. The default value is true.
481
482 \sa itemsExpandable
483*/
484bool QTreeView::expandsOnDoubleClick() const
485{
486 Q_D(const QTreeView);
487 return d->expandsOnDoubleClick;
488}
489
490void QTreeView::setExpandsOnDoubleClick(bool enable)
491{
492 Q_D(QTreeView);
493 d->expandsOnDoubleClick = enable;
494}
495
496/*!
497 Returns the horizontal position of the \a column in the viewport.
498*/
499int QTreeView::columnViewportPosition(int column) const
500{
501 Q_D(const QTreeView);
502 return d->header->sectionViewportPosition(column);
503}
504
505/*!
506 Returns the width of the \a column.
507
508 \sa resizeColumnToContents(), setColumnWidth()
509*/
510int QTreeView::columnWidth(int column) const
511{
512 Q_D(const QTreeView);
513 return d->header->sectionSize(column);
514}
515
516/*!
517 \since 4.2
518
519 Sets the width of the given \a column to the \a width specified.
520
521 \sa columnWidth(), resizeColumnToContents()
522*/
523void QTreeView::setColumnWidth(int column, int width)
524{
525 Q_D(QTreeView);
526 d->header->resizeSection(column, width);
527}
528
529/*!
530 Returns the column in the tree view whose header covers the \a x
531 coordinate given.
532*/
533int QTreeView::columnAt(int x) const
534{
535 Q_D(const QTreeView);
536 return d->header->logicalIndexAt(x);
537}
538
539/*!
540 Returns \c true if the \a column is hidden; otherwise returns \c false.
541
542 \sa hideColumn(), isRowHidden()
543*/
544bool QTreeView::isColumnHidden(int column) const
545{
546 Q_D(const QTreeView);
547 return d->header->isSectionHidden(column);
548}
549
550/*!
551 If \a hide is true the \a column is hidden, otherwise the \a column is shown.
552
553 \sa hideColumn(), setRowHidden()
554*/
555void QTreeView::setColumnHidden(int column, bool hide)
556{
557 Q_D(QTreeView);
558 if (column < 0 || column >= d->header->count())
559 return;
560 d->header->setSectionHidden(column, hide);
561}
562
563/*!
564 \property QTreeView::headerHidden
565 \brief whether the header is shown or not.
566 \since 4.4
567
568 If this property is \c true, the header is not shown otherwise it is.
569 The default value is false.
570
571 \sa header()
572*/
573bool QTreeView::isHeaderHidden() const
574{
575 Q_D(const QTreeView);
576 return d->header->isHidden();
577}
578
579void QTreeView::setHeaderHidden(bool hide)
580{
581 Q_D(QTreeView);
582 d->header->setHidden(hide);
583}
584
585/*!
586 Returns \c true if the item in the given \a row of the \a parent is hidden;
587 otherwise returns \c false.
588
589 \sa setRowHidden(), isColumnHidden()
590*/
591bool QTreeView::isRowHidden(int row, const QModelIndex &parent) const
592{
593 Q_D(const QTreeView);
594 if (!d->model)
595 return false;
596 return d->isRowHidden(d->model->index(row, 0, parent));
597}
598
599/*!
600 If \a hide is true the \a row with the given \a parent is hidden, otherwise the \a row is shown.
601
602 \sa isRowHidden(), setColumnHidden()
603*/
604void QTreeView::setRowHidden(int row, const QModelIndex &parent, bool hide)
605{
606 Q_D(QTreeView);
607 if (!d->model)
608 return;
609 QModelIndex index = d->model->index(row, 0, parent);
610 if (!index.isValid())
611 return;
612
613 if (hide) {
614 d->hiddenIndexes.insert(index);
615 } else if(d->isPersistent(index)) { //if the index is not persistent, it cannot be in the set
616 d->hiddenIndexes.remove(index);
617 }
618
619 d->doDelayedItemsLayout();
620}
621
622/*!
623 \since 4.3
624
625 Returns \c true if the item in first column in the given \a row
626 of the \a parent is spanning all the columns; otherwise returns \c false.
627
628 \sa setFirstColumnSpanned()
629*/
630bool QTreeView::isFirstColumnSpanned(int row, const QModelIndex &parent) const
631{
632 Q_D(const QTreeView);
633 if (d->spanningIndexes.isEmpty() || !d->model)
634 return false;
635 const QModelIndex index = d->model->index(row, 0, parent);
636 return d->spanningIndexes.contains(index);
637}
638
639/*!
640 \since 4.3
641
642 If \a span is true the item in the first column in the \a row
643 with the given \a parent is set to span all columns, otherwise all items
644 on the \a row are shown.
645
646 \sa isFirstColumnSpanned()
647*/
648void QTreeView::setFirstColumnSpanned(int row, const QModelIndex &parent, bool span)
649{
650 Q_D(QTreeView);
651 if (!d->model)
652 return;
653 const QModelIndex index = d->model->index(row, 0, parent);
654 if (!index.isValid())
655 return;
656
657 if (span)
658 d->spanningIndexes.insert(index);
659 else
660 d->spanningIndexes.remove(index);
661
662 d->executePostedLayout();
663 int i = d->viewIndex(index);
664 if (i >= 0)
665 d->viewItems[i].spanning = span;
666
667 d->viewport->update();
668}
669
670/*!
671 \reimp
672*/
673void QTreeView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
674{
675 Q_D(QTreeView);
676
677 // if we are going to do a complete relayout anyway, there is no need to update
678 if (d->delayedPendingLayout)
679 return;
680
681 // refresh the height cache here; we don't really lose anything by getting the size hint,
682 // since QAbstractItemView::dataChanged() will get the visualRect for the items anyway
683
684 bool sizeChanged = false;
685 int topViewIndex = d->viewIndex(topLeft);
686 if (topViewIndex == 0) {
687 int newDefaultItemHeight = indexRowSizeHint(topLeft);
688 sizeChanged = d->defaultItemHeight != newDefaultItemHeight;
689 d->defaultItemHeight = newDefaultItemHeight;
690 }
691
692 if (topViewIndex != -1) {
693 if (topLeft.row() == bottomRight.row()) {
694 int oldHeight = d->itemHeight(topViewIndex);
695 d->invalidateHeightCache(topViewIndex);
696 sizeChanged |= (oldHeight != d->itemHeight(topViewIndex));
697 if (topLeft.column() == 0)
698 d->viewItems[topViewIndex].hasChildren = d->hasVisibleChildren(topLeft);
699 } else {
700 int bottomViewIndex = d->viewIndex(bottomRight);
701 for (int i = topViewIndex; i <= bottomViewIndex; ++i) {
702 int oldHeight = d->itemHeight(i);
703 d->invalidateHeightCache(i);
704 sizeChanged |= (oldHeight != d->itemHeight(i));
705 if (topLeft.column() == 0)
706 d->viewItems[i].hasChildren = d->hasVisibleChildren(d->viewItems.at(i).index);
707 }
708 }
709 }
710
711 if (sizeChanged) {
712 d->updateScrollBars();
713 d->viewport->update();
714 }
715 QAbstractItemView::dataChanged(topLeft, bottomRight, roles);
716}
717
718/*!
719 Hides the \a column given.
720
721 \note This function should only be called after the model has been
722 initialized, as the view needs to know the number of columns in order to
723 hide \a column.
724
725 \sa showColumn(), setColumnHidden()
726*/
727void QTreeView::hideColumn(int column)
728{
729 Q_D(QTreeView);
730 if (d->header->isSectionHidden(column))
731 return;
732 d->header->hideSection(column);
733 doItemsLayout();
734}
735
736/*!
737 Shows the given \a column in the tree view.
738
739 \sa hideColumn(), setColumnHidden()
740*/
741void QTreeView::showColumn(int column)
742{
743 Q_D(QTreeView);
744 if (!d->header->isSectionHidden(column))
745 return;
746 d->header->showSection(column);
747 doItemsLayout();
748}
749
750/*!
751 \fn void QTreeView::expand(const QModelIndex &index)
752
753 Expands the model item specified by the \a index.
754
755 \sa expanded()
756*/
757void QTreeView::expand(const QModelIndex &index)
758{
759 Q_D(QTreeView);
760 if (!d->isIndexValid(index))
761 return;
762 if (index.flags() & Qt::ItemNeverHasChildren)
763 return;
764 if (d->isIndexExpanded(index))
765 return;
766 if (d->delayedPendingLayout) {
767 //A complete relayout is going to be performed, just store the expanded index, no need to layout.
768 if (d->storeExpanded(index))
769 emit expanded(index);
770 return;
771 }
772
773 int i = d->viewIndex(index);
774 if (i != -1) { // is visible
775 d->expand(i, true);
776 if (!d->isAnimating()) {
777 updateGeometries();
778 d->viewport->update();
779 }
780 } else if (d->storeExpanded(index)) {
781 emit expanded(index);
782 }
783}
784
785/*!
786 \fn void QTreeView::collapse(const QModelIndex &index)
787
788 Collapses the model item specified by the \a index.
789
790 \sa collapsed()
791*/
792void QTreeView::collapse(const QModelIndex &index)
793{
794 Q_D(QTreeView);
795 if (!d->isIndexValid(index))
796 return;
797 if (!d->isIndexExpanded(index))
798 return;
799 //if the current item is now invisible, the autoscroll will expand the tree to see it, so disable the autoscroll
800 d->delayedAutoScroll.stop();
801
802 if (d->delayedPendingLayout) {
803 //A complete relayout is going to be performed, just un-store the expanded index, no need to layout.
804 if (d->isPersistent(index) && d->expandedIndexes.remove(index))
805 emit collapsed(index);
806 return;
807 }
808 int i = d->viewIndex(index);
809 if (i != -1) { // is visible
810 d->collapse(i, true);
811 if (!d->isAnimating()) {
812 updateGeometries();
813 viewport()->update();
814 }
815 } else {
816 if (d->isPersistent(index) && d->expandedIndexes.remove(index))
817 emit collapsed(index);
818 }
819}
820
821/*!
822 \fn bool QTreeView::isExpanded(const QModelIndex &index) const
823
824 Returns \c true if the model item \a index is expanded; otherwise returns
825 false.
826
827 \sa expand(), expanded(), setExpanded()
828*/
829bool QTreeView::isExpanded(const QModelIndex &index) const
830{
831 Q_D(const QTreeView);
832 return d->isIndexExpanded(index);
833}
834
835/*!
836 Sets the item referred to by \a index to either collapse or expanded,
837 depending on the value of \a expanded.
838
839 \sa expanded(), expand(), isExpanded()
840*/
841void QTreeView::setExpanded(const QModelIndex &index, bool expanded)
842{
843 if (expanded)
844 this->expand(index);
845 else
846 this->collapse(index);
847}
848
849/*!
850 \since 4.2
851 \property QTreeView::sortingEnabled
852 \brief whether sorting is enabled
853
854 If this property is \c true, sorting is enabled for the tree; if the property
855 is false, sorting is not enabled. The default value is false.
856
857 \note In order to avoid performance issues, it is recommended that
858 sorting is enabled \e after inserting the items into the tree.
859 Alternatively, you could also insert the items into a list before inserting
860 the items into the tree.
861
862 \sa sortByColumn()
863*/
864
865void QTreeView::setSortingEnabled(bool enable)
866{
867 Q_D(QTreeView);
868 header()->setSortIndicatorShown(enable);
869 header()->setSectionsClickable(enable);
870 if (enable) {
871 //sortByColumn has to be called before we connect or set the sortingEnabled flag
872 // because otherwise it will not call sort on the model.
873 sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder());
874 connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
875 this, SLOT(_q_sortIndicatorChanged(int,Qt::SortOrder)), Qt::UniqueConnection);
876 } else {
877 disconnect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
878 this, SLOT(_q_sortIndicatorChanged(int,Qt::SortOrder)));
879 }
880 d->sortingEnabled = enable;
881}
882
883bool QTreeView::isSortingEnabled() const
884{
885 Q_D(const QTreeView);
886 return d->sortingEnabled;
887}
888
889/*!
890 \since 4.2
891 \property QTreeView::animated
892 \brief whether animations are enabled
893
894 If this property is \c true the treeview will animate expansion
895 and collapsing of branches. If this property is \c false, the treeview
896 will expand or collapse branches immediately without showing
897 the animation.
898
899 By default, this property is \c false.
900*/
901
902void QTreeView::setAnimated(bool animate)
903{
904 Q_D(QTreeView);
905 d->animationsEnabled = animate;
906}
907
908bool QTreeView::isAnimated() const
909{
910 Q_D(const QTreeView);
911 return d->animationsEnabled;
912}
913
914/*!
915 \since 4.2
916 \property QTreeView::allColumnsShowFocus
917 \brief whether items should show keyboard focus using all columns
918
919 If this property is \c true all columns will show focus, otherwise only
920 one column will show focus.
921
922 The default is false.
923*/
924
925void QTreeView::setAllColumnsShowFocus(bool enable)
926{
927 Q_D(QTreeView);
928 if (d->allColumnsShowFocus == enable)
929 return;
930 d->allColumnsShowFocus = enable;
931 d->viewport->update();
932}
933
934bool QTreeView::allColumnsShowFocus() const
935{
936 Q_D(const QTreeView);
937 return d->allColumnsShowFocus;
938}
939
940/*!
941 \property QTreeView::wordWrap
942 \brief the item text word-wrapping policy
943 \since 4.3
944
945 If this property is \c true then the item text is wrapped where
946 necessary at word-breaks; otherwise it is not wrapped at all.
947 This property is \c false by default.
948
949 Note that even if wrapping is enabled, the cell will not be
950 expanded to fit all text. Ellipsis will be inserted according to
951 the current \l{QAbstractItemView::}{textElideMode}.
952*/
953void QTreeView::setWordWrap(bool on)
954{
955 Q_D(QTreeView);
956 if (d->wrapItemText == on)
957 return;
958 d->wrapItemText = on;
959 d->doDelayedItemsLayout();
960}
961
962bool QTreeView::wordWrap() const
963{
964 Q_D(const QTreeView);
965 return d->wrapItemText;
966}
967
968/*!
969 \since 5.2
970
971 This specifies that the tree structure should be placed at logical index \a index.
972 If \index is set to -1 then the tree will always follow visual index 0.
973
974 \sa treePosition(), QHeaderView::swapSections(), QHeaderView::moveSection()
975*/
976
977void QTreeView::setTreePosition(int index)
978{
979 Q_D(QTreeView);
980 d->treePosition = index;
981 d->viewport->update();
982}
983
984/*!
985 \since 5.2
986
987 Return the logical index the tree is set on. If the return value is -1 then the
988 tree is placed on the visual index 0.
989
990 \sa setTreePosition()
991*/
992
993int QTreeView::treePosition() const
994{
995 Q_D(const QTreeView);
996 return d->treePosition;
997}
998
999/*!
1000 \reimp
1001 */
1002void QTreeView::keyboardSearch(const QString &search)
1003{
1004 Q_D(QTreeView);
1005 if (!d->model->rowCount(d->root) || !d->model->columnCount(d->root))
1006 return;
1007
1008 // Do a relayout nows, so that we can utilize viewItems
1009 d->executePostedLayout();
1010 if (d->viewItems.isEmpty())
1011 return;
1012
1013 QModelIndex start;
1014 if (currentIndex().isValid())
1015 start = currentIndex();
1016 else
1017 start = d->viewItems.at(0).index;
1018
1019 bool skipRow = false;
1020 bool keyboardTimeWasValid = d->keyboardInputTime.isValid();
1021 qint64 keyboardInputTimeElapsed;
1022 if (keyboardTimeWasValid)
1023 keyboardInputTimeElapsed = d->keyboardInputTime.restart();
1024 else
1025 d->keyboardInputTime.start();
1026 if (search.isEmpty() || !keyboardTimeWasValid
1027 || keyboardInputTimeElapsed > QApplication::keyboardInputInterval()) {
1028 d->keyboardInput = search;
1029 skipRow = currentIndex().isValid(); //if it is not valid we should really start at QModelIndex(0,0)
1030 } else {
1031 d->keyboardInput += search;
1032 }
1033
1034 // special case for searches with same key like 'aaaaa'
1035 bool sameKey = false;
1036 if (d->keyboardInput.length() > 1) {
1037 int c = d->keyboardInput.count(d->keyboardInput.at(d->keyboardInput.length() - 1));
1038 sameKey = (c == d->keyboardInput.length());
1039 if (sameKey)
1040 skipRow = true;
1041 }
1042
1043 // skip if we are searching for the same key or a new search started
1044 if (skipRow) {
1045 if (indexBelow(start).isValid()) {
1046 start = indexBelow(start);
1047 } else {
1048 const int origCol = start.column();
1049 start = d->viewItems.at(0).index;
1050 if (origCol != start.column())
1051 start = start.sibling(start.row(), origCol);
1052 }
1053 }
1054
1055 int startIndex = d->viewIndex(start);
1056 if (startIndex <= -1)
1057 return;
1058
1059 int previousLevel = -1;
1060 int bestAbove = -1;
1061 int bestBelow = -1;
1062 QString searchString = sameKey ? QString(d->keyboardInput.at(0)) : d->keyboardInput;
1063 for (int i = 0; i < d->viewItems.count(); ++i) {
1064 if ((int)d->viewItems.at(i).level > previousLevel) {
1065 QModelIndex searchFrom = d->viewItems.at(i).index;
1066 if (start.column() > 0)
1067 searchFrom = searchFrom.sibling(searchFrom.row(), start.column());
1068 if (searchFrom.parent() == start.parent())
1069 searchFrom = start;
1070 QModelIndexList match = d->model->match(searchFrom, Qt::DisplayRole, searchString);
1071 if (match.count()) {
1072 int hitIndex = d->viewIndex(match.at(0));
1073 if (hitIndex >= 0 && hitIndex < startIndex)
1074 bestAbove = bestAbove == -1 ? hitIndex : qMin(hitIndex, bestAbove);
1075 else if (hitIndex >= startIndex)
1076 bestBelow = bestBelow == -1 ? hitIndex : qMin(hitIndex, bestBelow);
1077 }
1078 }
1079 previousLevel = d->viewItems.at(i).level;
1080 }
1081
1082 QModelIndex index;
1083 if (bestBelow > -1)
1084 index = d->viewItems.at(bestBelow).index;
1085 else if (bestAbove > -1)
1086 index = d->viewItems.at(bestAbove).index;
1087
1088 if (start.column() > 0)
1089 index = index.sibling(index.row(), start.column());
1090
1091 if (index.isValid())
1092 setCurrentIndex(index);
1093}
1094
1095/*!
1096 Returns the rectangle on the viewport occupied by the item at \a index.
1097 If the index is not visible or explicitly hidden, the returned rectangle is invalid.
1098*/
1099QRect QTreeView::visualRect(const QModelIndex &index) const
1100{
1101 Q_D(const QTreeView);
1102
1103 if (!d->isIndexValid(index) || isIndexHidden(index))
1104 return QRect();
1105
1106 d->executePostedLayout();
1107
1108 int vi = d->viewIndex(index);
1109 if (vi < 0)
1110 return QRect();
1111
1112 bool spanning = d->viewItems.at(vi).spanning;
1113
1114 // if we have a spanning item, make the selection stretch from left to right
1115 int x = (spanning ? 0 : columnViewportPosition(index.column()));
1116 int w = (spanning ? d->header->length() : columnWidth(index.column()));
1117 // handle indentation
1118 if (d->isTreePosition(index.column())) {
1119 int i = d->indentationForItem(vi);
1120 w -= i;
1121 if (!isRightToLeft())
1122 x += i;
1123 }
1124
1125 int y = d->coordinateForItem(vi);
1126 int h = d->itemHeight(vi);
1127
1128 return QRect(x, y, w, h);
1129}
1130
1131/*!
1132 Scroll the contents of the tree view until the given model item
1133 \a index is visible. The \a hint parameter specifies more
1134 precisely where the item should be located after the
1135 operation.
1136 If any of the parents of the model item are collapsed, they will
1137 be expanded to ensure that the model item is visible.
1138*/
1139void QTreeView::scrollTo(const QModelIndex &index, ScrollHint hint)
1140{
1141 Q_D(QTreeView);
1142
1143 if (!d->isIndexValid(index))
1144 return;
1145
1146 d->executePostedLayout();
1147 d->updateScrollBars();
1148
1149 // Expand all parents if the parent(s) of the node are not expanded.
1150 QModelIndex parent = index.parent();
1151 while (parent != d->root && parent.isValid() && state() == NoState && d->itemsExpandable) {
1152 if (!isExpanded(parent))
1153 expand(parent);
1154 parent = d->model->parent(parent);
1155 }
1156
1157 int item = d->viewIndex(index);
1158 if (item < 0)
1159 return;
1160
1161 QRect area = d->viewport->rect();
1162
1163 // vertical
1164 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
1165 int top = verticalScrollBar()->value();
1166 int bottom = top + verticalScrollBar()->pageStep();
1167 if (hint == EnsureVisible && item >= top && item < bottom) {
1168 // nothing to do
1169 } else if (hint == PositionAtTop || (hint == EnsureVisible && item < top)) {
1170 verticalScrollBar()->setValue(item);
1171 } else { // PositionAtBottom or PositionAtCenter
1172 const int currentItemHeight = d->itemHeight(item);
1173 int y = (hint == PositionAtCenter
1174 //we center on the current item with a preference to the top item (ie. -1)
1175 ? area.height() / 2 + currentItemHeight - 1
1176 //otherwise we simply take the whole space
1177 : area.height());
1178 if (y > currentItemHeight) {
1179 while (item >= 0) {
1180 y -= d->itemHeight(item);
1181 if (y < 0) { //there is no more space left
1182 item++;
1183 break;
1184 }
1185 item--;
1186 }
1187 }
1188 verticalScrollBar()->setValue(item);
1189 }
1190 } else { // ScrollPerPixel
1191 QRect rect(columnViewportPosition(index.column()),
1192 d->coordinateForItem(item), // ### slow for items outside the view
1193 columnWidth(index.column()),
1194 d->itemHeight(item));
1195
1196 if (rect.isEmpty()) {
1197 // nothing to do
1198 } else if (hint == EnsureVisible && area.contains(rect)) {
1199 d->viewport->update(rect);
1200 // nothing to do
1201 } else {
1202 bool above = (hint == EnsureVisible
1203 && (rect.top() < area.top()
1204 || area.height() < rect.height()));
1205 bool below = (hint == EnsureVisible
1206 && rect.bottom() > area.bottom()
1207 && rect.height() < area.height());
1208
1209 int verticalValue = verticalScrollBar()->value();
1210 if (hint == PositionAtTop || above)
1211 verticalValue += rect.top();
1212 else if (hint == PositionAtBottom || below)
1213 verticalValue += rect.bottom() - area.height();
1214 else if (hint == PositionAtCenter)
1215 verticalValue += rect.top() - ((area.height() - rect.height()) / 2);
1216 verticalScrollBar()->setValue(verticalValue);
1217 }
1218 }
1219 // horizontal
1220 int viewportWidth = d->viewport->width();
1221 int horizontalOffset = d->header->offset();
1222 int horizontalPosition = d->header->sectionPosition(index.column());
1223 int cellWidth = d->header->sectionSize(index.column());
1224
1225 if (hint == PositionAtCenter) {
1226 horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2));
1227 } else {
1228 if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth)
1229 horizontalScrollBar()->setValue(horizontalPosition);
1230 else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth)
1231 horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth);
1232 }
1233}
1234
1235/*!
1236 \reimp
1237*/
1238void QTreeView::timerEvent(QTimerEvent *event)
1239{
1240 Q_D(QTreeView);
1241 if (event->timerId() == d->columnResizeTimerID) {
1242 updateGeometries();
1243 killTimer(d->columnResizeTimerID);
1244 d->columnResizeTimerID = 0;
1245 QRect rect;
1246 int viewportHeight = d->viewport->height();
1247 int viewportWidth = d->viewport->width();
1248 for (int i = d->columnsToUpdate.size() - 1; i >= 0; --i) {
1249 int column = d->columnsToUpdate.at(i);
1250 int x = columnViewportPosition(column);
1251 if (isRightToLeft())
1252 rect |= QRect(0, 0, x + columnWidth(column), viewportHeight);
1253 else
1254 rect |= QRect(x, 0, viewportWidth - x, viewportHeight);
1255 }
1256 d->viewport->update(rect.normalized());
1257 d->columnsToUpdate.clear();
1258 } else if (event->timerId() == d->openTimer.timerId()) {
1259 QPoint pos = d->viewport->mapFromGlobal(QCursor::pos());
1260 if (state() == QAbstractItemView::DraggingState
1261 && d->viewport->rect().contains(pos)) {
1262 QModelIndex index = indexAt(pos);
1263 setExpanded(index, !isExpanded(index));
1264 }
1265 d->openTimer.stop();
1266 }
1267
1268 QAbstractItemView::timerEvent(event);
1269}
1270
1271/*!
1272 \reimp
1273*/
1274#if QT_CONFIG(draganddrop)
1275void QTreeView::dragMoveEvent(QDragMoveEvent *event)
1276{
1277 Q_D(QTreeView);
1278 if (d->autoExpandDelay >= 0)
1279 d->openTimer.start(d->autoExpandDelay, this);
1280 QAbstractItemView::dragMoveEvent(event);
1281}
1282#endif
1283
1284/*!
1285 \reimp
1286*/
1287bool QTreeView::viewportEvent(QEvent *event)
1288{
1289 Q_D(QTreeView);
1290 switch (event->type()) {
1291 case QEvent::HoverEnter:
1292 case QEvent::HoverLeave:
1293 case QEvent::HoverMove: {
1294 QHoverEvent *he = static_cast<QHoverEvent*>(event);
1295 int oldBranch = d->hoverBranch;
1296 d->hoverBranch = d->itemDecorationAt(he->pos());
1297 QModelIndex newIndex = indexAt(he->pos());
1298 if (d->hover != newIndex || d->hoverBranch != oldBranch) {
1299 // Update the whole hovered over row. No need to update the old hovered
1300 // row, that is taken care in superclass hover handling.
1301 QRect rect = visualRect(newIndex);
1302 rect.setX(0);
1303 rect.setWidth(viewport()->width());
1304 viewport()->update(rect);
1305 }
1306 break; }
1307 default:
1308 break;
1309 }
1310 return QAbstractItemView::viewportEvent(event);
1311}
1312
1313/*!
1314 \reimp
1315*/
1316void QTreeView::paintEvent(QPaintEvent *event)
1317{
1318 Q_D(QTreeView);
1319 d->executePostedLayout();
1320 QPainter painter(viewport());
1321#ifndef QT_NO_ANIMATION
1322 if (d->isAnimating()) {
1323 drawTree(&painter, event->region() - d->animatedOperation.rect());
1324 d->drawAnimatedOperation(&painter);
1325 } else
1326#endif //QT_NO_ANIMATION
1327 {
1328 drawTree(&painter, event->region());
1329#if QT_CONFIG(draganddrop)
1330 d->paintDropIndicator(&painter);
1331#endif
1332 }
1333}
1334
1335int QTreeViewPrivate::logicalIndexForTree() const
1336{
1337 int index = treePosition;
1338 if (index < 0)
1339 index = header->logicalIndex(0);
1340 return index;
1341}
1342
1343void QTreeViewPrivate::paintAlternatingRowColors(QPainter *painter, QStyleOptionViewItem *option, int y, int bottom) const
1344{
1345 Q_Q(const QTreeView);
1346 if (!alternatingColors || !q->style()->styleHint(QStyle::SH_ItemView_PaintAlternatingRowColorsForEmptyArea, option, q))
1347 return;
1348 int rowHeight = defaultItemHeight;
1349 if (rowHeight <= 0) {
1350 rowHeight = itemDelegate->sizeHint(*option, QModelIndex()).height();
1351 if (rowHeight <= 0)
1352 return;
1353 }
1354 while (y <= bottom) {
1355 option->rect.setRect(0, y, viewport->width(), rowHeight);
1356 option->features.setFlag(QStyleOptionViewItem::Alternate, current & 1);
1357 ++current;
1358 q->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, option, painter, q);
1359 y += rowHeight;
1360 }
1361}
1362
1363bool QTreeViewPrivate::expandOrCollapseItemAtPos(const QPoint &pos)
1364{
1365 Q_Q(QTreeView);
1366 // we want to handle mousePress in EditingState (persistent editors)
1367 if ((state != QAbstractItemView::NoState
1368 && state != QAbstractItemView::EditingState)
1369 || !viewport->rect().contains(pos))
1370 return true;
1371
1372 int i = itemDecorationAt(pos);
1373 if ((i != -1) && itemsExpandable && hasVisibleChildren(viewItems.at(i).index)) {
1374 if (viewItems.at(i).expanded)
1375 collapse(i, true);
1376 else
1377 expand(i, true);
1378 if (!isAnimating()) {
1379 q->updateGeometries();
1380 viewport->update();
1381 }
1382 return true;
1383 }
1384 return false;
1385}
1386
1387void QTreeViewPrivate::_q_modelDestroyed()
1388{
1389 //we need to clear the viewItems because it contains QModelIndexes to
1390 //the model currently being destroyed
1391 viewItems.clear();
1392 QAbstractItemViewPrivate::_q_modelDestroyed();
1393}
1394
1395/*!
1396 \reimp
1397
1398 We have a QTreeView way of knowing what elements are on the viewport
1399*/
1400QItemViewPaintPairs QTreeViewPrivate::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const
1401{
1402 Q_ASSERT(r);
1403 Q_Q(const QTreeView);
1404 if (spanningIndexes.isEmpty())
1405 return QAbstractItemViewPrivate::draggablePaintPairs(indexes, r);
1406 QModelIndexList list;
1407 for (const QModelIndex &idx : indexes) {
1408 if (idx.column() > 0 && q->isFirstColumnSpanned(idx.row(), idx.parent()))
1409 continue;
1410 list << idx;
1411 }
1412 return QAbstractItemViewPrivate::draggablePaintPairs(list, r);
1413}
1414
1415void QTreeViewPrivate::adjustViewOptionsForIndex(QStyleOptionViewItem *option, const QModelIndex &current) const
1416{
1417 const int row = viewIndex(current); // get the index in viewItems[]
1418 option->state = option->state | (viewItems.at(row).expanded ? QStyle::State_Open : QStyle::State_None)
1419 | (viewItems.at(row).hasChildren ? QStyle::State_Children : QStyle::State_None)
1420 | (viewItems.at(row).hasMoreSiblings ? QStyle::State_Sibling : QStyle::State_None);
1421
1422 option->showDecorationSelected = (selectionBehavior & QTreeView::SelectRows)
1423 || option->showDecorationSelected;
1424
1425 QVector<int> logicalIndices; // index = visual index of visible columns only. data = logical index.
1426 QVector<QStyleOptionViewItem::ViewItemPosition> viewItemPosList; // vector of left/middle/end for each logicalIndex, visible columns only.
1427 const bool spanning = viewItems.at(row).spanning;
1428 const int left = (spanning ? header->visualIndex(0) : 0);
1429 const int right = (spanning ? header->visualIndex(0) : header->count() - 1 );
1430 calcLogicalIndices(&logicalIndices, &viewItemPosList, left, right);
1431
1432 const int visualIndex = logicalIndices.indexOf(current.column());
1433 option->viewItemPosition = viewItemPosList.at(visualIndex);
1434}
1435
1436
1437/*!
1438 \since 4.2
1439 Draws the part of the tree intersecting the given \a region using the specified
1440 \a painter.
1441
1442 \sa paintEvent()
1443*/
1444void QTreeView::drawTree(QPainter *painter, const QRegion &region) const
1445{
1446 Q_D(const QTreeView);
1447 const QVector<QTreeViewItem> viewItems = d->viewItems;
1448
1449 QStyleOptionViewItem option = d->viewOptionsV1();
1450 const QStyle::State state = option.state;
1451 d->current = 0;
1452
1453 if (viewItems.count() == 0 || d->header->count() == 0 || !d->itemDelegate) {
1454 d->paintAlternatingRowColors(painter, &option, 0, region.boundingRect().bottom()+1);
1455 return;
1456 }
1457
1458 int firstVisibleItemOffset = 0;
1459 const int firstVisibleItem = d->firstVisibleItem(&firstVisibleItemOffset);
1460 if (firstVisibleItem < 0) {
1461 d->paintAlternatingRowColors(painter, &option, 0, region.boundingRect().bottom()+1);
1462 return;
1463 }
1464
1465 const int viewportWidth = d->viewport->width();
1466
1467 QPoint hoverPos = d->viewport->mapFromGlobal(QCursor::pos());
1468 d->hoverBranch = d->itemDecorationAt(hoverPos);
1469
1470 QVector<int> drawn;
1471 bool multipleRects = (region.rectCount() > 1);
1472 for (const QRect &a : region) {
1473 const QRect area = (multipleRects
1474 ? QRect(0, a.y(), viewportWidth, a.height())
1475 : a);
1476 d->leftAndRight = d->startAndEndColumns(area);
1477
1478 int i = firstVisibleItem; // the first item at the top of the viewport
1479 int y = firstVisibleItemOffset; // we may only see part of the first item
1480
1481 // start at the top of the viewport and iterate down to the update area
1482 for (; i < viewItems.count(); ++i) {
1483 const int itemHeight = d->itemHeight(i);
1484 if (y + itemHeight > area.top())
1485 break;
1486 y += itemHeight;
1487 }
1488
1489 // paint the visible rows
1490 for (; i < viewItems.count() && y <= area.bottom(); ++i) {
1491 const int itemHeight = d->itemHeight(i);
1492 option.rect.setRect(0, y, viewportWidth, itemHeight);
1493 option.state = state | (viewItems.at(i).expanded ? QStyle::State_Open : QStyle::State_None)
1494 | (viewItems.at(i).hasChildren ? QStyle::State_Children : QStyle::State_None)
1495 | (viewItems.at(i).hasMoreSiblings ? QStyle::State_Sibling : QStyle::State_None);
1496 d->current = i;
1497 d->spanning = viewItems.at(i).spanning;
1498 if (!multipleRects || !drawn.contains(i)) {
1499 drawRow(painter, option, viewItems.at(i).index);
1500 if (multipleRects) // even if the rect only intersects the item,
1501 drawn.append(i); // the entire item will be painted
1502 }
1503 y += itemHeight;
1504 }
1505
1506 if (y <= area.bottom()) {
1507 d->current = i;
1508 d->paintAlternatingRowColors(painter, &option, y, area.bottom());
1509 }
1510 }
1511}
1512
1513/// ### move to QObject :)
1514static inline bool ancestorOf(QObject *widget, QObject *other)
1515{
1516 for (QObject *parent = other; parent != 0; parent = parent->parent()) {
1517 if (parent == widget)
1518 return true;
1519 }
1520 return false;
1521}
1522
1523void QTreeViewPrivate::calcLogicalIndices(QVector<int> *logicalIndices, QVector<QStyleOptionViewItem::ViewItemPosition> *itemPositions, int left, int right) const
1524{
1525 const int columnCount = header->count();
1526 /* 'left' and 'right' are the left-most and right-most visible visual indices.
1527 Compute the first visible logical indices before and after the left and right.
1528 We will use these values to determine the QStyleOptionViewItem::viewItemPosition. */
1529 int logicalIndexBeforeLeft = -1, logicalIndexAfterRight = -1;
1530 for (int visualIndex = left - 1; visualIndex >= 0; --visualIndex) {
1531 int logicalIndex = header->logicalIndex(visualIndex);
1532 if (!header->isSectionHidden(logicalIndex)) {
1533 logicalIndexBeforeLeft = logicalIndex;
1534 break;
1535 }
1536 }
1537
1538 for (int visualIndex = left; visualIndex < columnCount; ++visualIndex) {
1539 int logicalIndex = header->logicalIndex(visualIndex);
1540 if (!header->isSectionHidden(logicalIndex)) {
1541 if (visualIndex > right) {
1542 logicalIndexAfterRight = logicalIndex;
1543 break;
1544 }
1545 logicalIndices->append(logicalIndex);
1546 }
1547 }
1548
1549 itemPositions->resize(logicalIndices->count());
1550 for (int currentLogicalSection = 0; currentLogicalSection < logicalIndices->count(); ++currentLogicalSection) {
1551 const int headerSection = logicalIndices->at(currentLogicalSection);
1552 // determine the viewItemPosition depending on the position of column 0
1553 int nextLogicalSection = currentLogicalSection + 1 >= logicalIndices->count()
1554 ? logicalIndexAfterRight
1555 : logicalIndices->at(currentLogicalSection + 1);
1556 int prevLogicalSection = currentLogicalSection - 1 < 0
1557 ? logicalIndexBeforeLeft
1558 : logicalIndices->at(currentLogicalSection - 1);
1559 QStyleOptionViewItem::ViewItemPosition pos;
1560 if (columnCount == 1 || (nextLogicalSection == 0 && prevLogicalSection == -1)
1561 || (headerSection == 0 && nextLogicalSection == -1) || spanning)
1562 pos = QStyleOptionViewItem::OnlyOne;
1563 else if (isTreePosition(headerSection) || (nextLogicalSection != 0 && prevLogicalSection == -1))
1564 pos = QStyleOptionViewItem::Beginning;
1565 else if (nextLogicalSection == 0 || nextLogicalSection == -1)
1566 pos = QStyleOptionViewItem::End;
1567 else
1568 pos = QStyleOptionViewItem::Middle;
1569 (*itemPositions)[currentLogicalSection] = pos;
1570 }
1571}
1572
1573/*!
1574 \internal
1575 Get sizeHint width for single index (providing existing hint and style option) and index in viewIndex i.
1576*/
1577int QTreeViewPrivate::widthHintForIndex(const QModelIndex &index, int hint, const QStyleOptionViewItem &option, int i) const
1578{
1579 QWidget *editor = editorForIndex(index).widget.data();
1580 if (editor && persistent.contains(editor)) {
1581 hint = qMax(hint, editor->sizeHint().width());
1582 int min = editor->minimumSize().width();
1583 int max = editor->maximumSize().width();
1584 hint = qBound(min, hint, max);
1585 }
1586 int xhint = delegateForIndex(index)->sizeHint(option, index).width();
1587 hint = qMax(hint, xhint + (isTreePosition(index.column()) ? indentationForItem(i) : 0));
1588 return hint;
1589}
1590
1591/*!
1592 Draws the row in the tree view that contains the model item \a index,
1593 using the \a painter given. The \a option controls how the item is
1594 displayed.
1595
1596 \sa setAlternatingRowColors()
1597*/
1598void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option,
1599 const QModelIndex &index) const
1600{
1601 Q_D(const QTreeView);
1602 QStyleOptionViewItem opt = option;
1603 const QPoint offset = d->scrollDelayOffset;
1604 const int y = option.rect.y() + offset.y();
1605 const QModelIndex parent = index.parent();
1606 const QHeaderView *header = d->header;
1607 const QModelIndex current = currentIndex();
1608 const QModelIndex hover = d->hover;
1609 const bool reverse = isRightToLeft();
1610 const QStyle::State state = opt.state;
1611 const bool spanning = d->spanning;
1612 const int left = (spanning ? header->visualIndex(0) : d->leftAndRight.first);
1613 const int right = (spanning ? header->visualIndex(0) : d->leftAndRight.second);
1614 const bool alternate = d->alternatingColors;
1615 const bool enabled = (state & QStyle::State_Enabled) != 0;
1616 const bool allColumnsShowFocus = d->allColumnsShowFocus;
1617
1618
1619 // when the row contains an index widget which has focus,
1620 // we want to paint the entire row as active
1621 bool indexWidgetHasFocus = false;
1622 if ((current.row() == index.row()) && !d->editorIndexHash.isEmpty()) {
1623 const int r = index.row();
1624 QWidget *fw = QApplication::focusWidget();
1625 for (int c = 0; c < header->count(); ++c) {
1626 QModelIndex idx = d->model->index(r, c, parent);
1627 if (QWidget *editor = indexWidget(idx)) {
1628 if (ancestorOf(editor, fw)) {
1629 indexWidgetHasFocus = true;
1630 break;
1631 }
1632 }
1633 }
1634 }
1635
1636 const bool widgetHasFocus = hasFocus();
1637 bool currentRowHasFocus = false;
1638 if (allColumnsShowFocus && widgetHasFocus && current.isValid()) {
1639 // check if the focus index is before or after the visible columns
1640 const int r = index.row();
1641 for (int c = 0; c < left && !currentRowHasFocus; ++c) {
1642 QModelIndex idx = d->model->index(r, c, parent);
1643 currentRowHasFocus = (idx == current);
1644 }
1645 QModelIndex parent = d->model->parent(index);
1646 for (int c = right; c < header->count() && !currentRowHasFocus; ++c) {
1647 currentRowHasFocus = (d->model->index(r, c, parent) == current);
1648 }
1649 }
1650
1651 // ### special case: treeviews with multiple columns draw
1652 // the selections differently than with only one column
1653 opt.showDecorationSelected = (d->selectionBehavior & SelectRows)
1654 || option.showDecorationSelected;
1655
1656 int width, height = option.rect.height();
1657 int position;
1658 QModelIndex modelIndex;
1659 const bool hoverRow = selectionBehavior() == QAbstractItemView::SelectRows
1660 && index.parent() == hover.parent()
1661 && index.row() == hover.row();
1662
1663 QVector<int> logicalIndices;
1664 QVector<QStyleOptionViewItem::ViewItemPosition> viewItemPosList; // vector of left/middle/end for each logicalIndex
1665 d->calcLogicalIndices(&logicalIndices, &viewItemPosList, left, right);
1666
1667 for (int currentLogicalSection = 0; currentLogicalSection < logicalIndices.count(); ++currentLogicalSection) {
1668 int headerSection = logicalIndices.at(currentLogicalSection);
1669 position = columnViewportPosition(headerSection) + offset.x();
1670 width = header->sectionSize(headerSection);
1671
1672 if (spanning) {
1673 int lastSection = header->logicalIndex(header->count() - 1);
1674 if (!reverse) {
1675 width = columnViewportPosition(lastSection) + header->sectionSize(lastSection) - position;
1676 } else {
1677 width += position - columnViewportPosition(lastSection);
1678 position = columnViewportPosition(lastSection);
1679 }
1680 }
1681
1682 modelIndex = d->model->index(index.row(), headerSection, parent);
1683 if (!modelIndex.isValid())
1684 continue;
1685 opt.state = state;
1686
1687 opt.viewItemPosition = viewItemPosList.at(currentLogicalSection);
1688
1689 // fake activeness when row editor has focus
1690 if (indexWidgetHasFocus)
1691 opt.state |= QStyle::State_Active;
1692
1693 if (d->selectionModel->isSelected(modelIndex))
1694 opt.state |= QStyle::State_Selected;
1695 if (widgetHasFocus && (current == modelIndex)) {
1696 if (allColumnsShowFocus)
1697 currentRowHasFocus = true;
1698 else
1699 opt.state |= QStyle::State_HasFocus;
1700 }
1701 opt.state.setFlag(QStyle::State_MouseOver,
1702 (hoverRow || modelIndex == hover)
1703 && (option.showDecorationSelected || d->hoverBranch == -1));
1704
1705 if (enabled) {
1706 QPalette::ColorGroup cg;
1707 if ((d->model->flags(modelIndex) & Qt::ItemIsEnabled) == 0) {
1708 opt.state &= ~QStyle::State_Enabled;
1709 cg = QPalette::Disabled;
1710 } else if (opt.state & QStyle::State_Active) {
1711 cg = QPalette::Active;
1712 } else {
1713 cg = QPalette::Inactive;
1714 }
1715 opt.palette.setCurrentColorGroup(cg);
1716 }
1717
1718 if (alternate) {
1719 opt.features.setFlag(QStyleOptionViewItem::Alternate, d->current & 1);
1720 }
1721
1722 /* Prior to Qt 4.3, the background of the branch (in selected state and
1723 alternate row color was provided by the view. For backward compatibility,
1724 this is now delegated to the style using PE_PanelViewItemRow which
1725 does the appropriate fill */
1726 if (d->isTreePosition(headerSection)) {
1727 const int i = d->indentationForItem(d->current);
1728 QRect branches(reverse ? position + width - i : position, y, i, height);
1729 const bool setClipRect = branches.width() > width;
1730 if (setClipRect) {
1731 painter->save();
1732 painter->setClipRect(QRect(position, y, width, height));
1733 }
1734 // draw background for the branch (selection + alternate row)
1735 opt.rect = branches;
1736 style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);
1737
1738 // draw background of the item (only alternate row). rest of the background
1739 // is provided by the delegate
1740 QStyle::State oldState = opt.state;
1741 opt.state &= ~QStyle::State_Selected;
1742 opt.rect.setRect(reverse ? position : i + position, y, width - i, height);
1743 style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);
1744 opt.state = oldState;
1745
1746 if (d->indent != 0)
1747 drawBranches(painter, branches, index);
1748 if (setClipRect)
1749 painter->restore();
1750 } else {
1751 QStyle::State oldState = opt.state;
1752 opt.state &= ~QStyle::State_Selected;
1753 opt.rect.setRect(position, y, width, height);
1754 style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);
1755 opt.state = oldState;
1756 }
1757
1758 d->delegateForIndex(modelIndex)->paint(painter, opt, modelIndex);
1759 }
1760
1761 if (currentRowHasFocus) {
1762 QStyleOptionFocusRect o;
1763 o.QStyleOption::operator=(option);
1764 o.state |= QStyle::State_KeyboardFocusChange;
1765 QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
1766 ? QPalette::Normal : QPalette::Disabled;
1767 o.backgroundColor = option.palette.color(cg, d->selectionModel->isSelected(index)
1768 ? QPalette::Highlight : QPalette::Background);
1769 int x = 0;
1770 if (!option.showDecorationSelected)
1771 x = header->sectionPosition(0) + d->indentationForItem(d->current);
1772 QRect focusRect(x - header->offset(), y, header->length() - x, height);
1773 o.rect = style()->visualRect(layoutDirection(), d->viewport->rect(), focusRect);
1774 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter);
1775 // if we show focus on all columns and the first section is moved,
1776 // we have to split the focus rect into two rects
1777 if (allColumnsShowFocus && !option.showDecorationSelected
1778 && header->sectionsMoved() && (header->visualIndex(0) != 0)) {
1779 QRect sectionRect(0, y, header->sectionPosition(0), height);
1780 o.rect = style()->visualRect(layoutDirection(), d->viewport->rect(), sectionRect);
1781 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter);
1782 }
1783 }
1784}
1785
1786/*!
1787 Draws the branches in the tree view on the same row as the model item
1788 \a index, using the \a painter given. The branches are drawn in the
1789 rectangle specified by \a rect.
1790*/
1791void QTreeView::drawBranches(QPainter *painter, const QRect &rect,
1792 const QModelIndex &index) const
1793{
1794 Q_D(const QTreeView);
1795 const bool reverse = isRightToLeft();
1796 const int indent = d->indent;
1797 const int outer = d->rootDecoration ? 0 : 1;
1798 const int item = d->current;
1799 const QTreeViewItem &viewItem = d->viewItems.at(item);
1800 int level = viewItem.level;
1801 QRect primitive(reverse ? rect.left() : rect.right() + 1, rect.top(), indent, rect.height());
1802
1803 QModelIndex parent = index.parent();
1804 QModelIndex current = parent;
1805 QModelIndex ancestor = current.parent();
1806
1807 QStyleOptionViewItem opt = viewOptions();
1808 QStyle::State extraFlags = QStyle::State_None;
1809 if (isEnabled())
1810 extraFlags |= QStyle::State_Enabled;
1811 if (hasFocus())
1812 extraFlags |= QStyle::State_Active;
1813 QPoint oldBO = painter->brushOrigin();
1814 if (verticalScrollMode() == QAbstractItemView::ScrollPerPixel)
1815 painter->setBrushOrigin(QPoint(0, verticalOffset()));
1816
1817 if (d->alternatingColors) {
1818 opt.features.setFlag(QStyleOptionViewItem::Alternate, d->current & 1);
1819 }
1820
1821 // When hovering over a row, pass State_Hover for painting the branch
1822 // indicators if it has the decoration (aka branch) selected.
1823 bool hoverRow = selectionBehavior() == QAbstractItemView::SelectRows
1824 && opt.showDecorationSelected
1825 && index.parent() == d->hover.parent()
1826 && index.row() == d->hover.row();
1827
1828 if (d->selectionModel->isSelected(index))
1829 extraFlags |= QStyle::State_Selected;
1830
1831 if (level >= outer) {
1832 // start with the innermost branch
1833 primitive.moveLeft(reverse ? primitive.left() : primitive.left() - indent);
1834 opt.rect = primitive;
1835
1836 const bool expanded = viewItem.expanded;
1837 const bool children = viewItem.hasChildren;
1838 bool moreSiblings = viewItem.hasMoreSiblings;
1839
1840 opt.state = QStyle::State_Item | extraFlags
1841 | (moreSiblings ? QStyle::State_Sibling : QStyle::State_None)
1842 | (children ? QStyle::State_Children : QStyle::State_None)
1843 | (expanded ? QStyle::State_Open : QStyle::State_None);
1844 opt.state.setFlag(QStyle::State_MouseOver, hoverRow || item == d->hoverBranch);
1845
1846 style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);
1847 }
1848 // then go out level by level
1849 for (--level; level >= outer; --level) { // we have already drawn the innermost branch
1850 primitive.moveLeft(reverse ? primitive.left() + indent : primitive.left() - indent);
1851 opt.rect = primitive;
1852 opt.state = extraFlags;
1853 bool moreSiblings = false;
1854 if (d->hiddenIndexes.isEmpty()) {
1855 moreSiblings = (d->model->rowCount(ancestor) - 1 > current.row());
1856 } else {
1857 int successor = item + viewItem.total + 1;
1858 while (successor < d->viewItems.size()
1859 && d->viewItems.at(successor).level >= uint(level)) {
1860 const QTreeViewItem &successorItem = d->viewItems.at(successor);
1861 if (successorItem.level == uint(level)) {
1862 moreSiblings = true;
1863 break;
1864 }
1865 successor += successorItem.total + 1;
1866 }
1867 }
1868 if (moreSiblings)
1869 opt.state |= QStyle::State_Sibling;
1870 opt.state.setFlag(QStyle::State_MouseOver, hoverRow || item == d->hoverBranch);
1871
1872 style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);
1873 current = ancestor;
1874 ancestor = current.parent();
1875 }
1876 painter->setBrushOrigin(oldBO);
1877}
1878
1879/*!
1880 \reimp
1881*/
1882void QTreeView::mousePressEvent(QMouseEvent *event)
1883{
1884 Q_D(QTreeView);
1885 bool handled = false;
1886 if (style()->styleHint(QStyle::SH_ListViewExpand_SelectMouseType, 0, this) == QEvent::MouseButtonPress)
1887 handled = d->expandOrCollapseItemAtPos(event->pos());
1888 if (!handled && d->itemDecorationAt(event->pos()) == -1)
1889 QAbstractItemView::mousePressEvent(event);
1890}
1891
1892/*!
1893 \reimp
1894*/
1895void QTreeView::mouseReleaseEvent(QMouseEvent *event)
1896{
1897 Q_D(QTreeView);
1898 if (d->itemDecorationAt(event->pos()) == -1) {
1899 QAbstractItemView::mouseReleaseEvent(event);
1900 } else {
1901 if (state() == QAbstractItemView::DragSelectingState || state() == QAbstractItemView::DraggingState)
1902 setState(QAbstractItemView::NoState);
1903 if (style()->styleHint(QStyle::SH_ListViewExpand_SelectMouseType, 0, this) == QEvent::MouseButtonRelease)
1904 d->expandOrCollapseItemAtPos(event->pos());
1905 }
1906}
1907
1908/*!
1909 \reimp
1910*/
1911void QTreeView::mouseDoubleClickEvent(QMouseEvent *event)
1912{
1913 Q_D(QTreeView);
1914 if (state() != NoState || !d->viewport->rect().contains(event->pos()))
1915 return;
1916
1917 int i = d->itemDecorationAt(event->pos());
1918 if (i == -1) {
1919 i = d->itemAtCoordinate(event->y());
1920 if (i == -1)
1921 return; // user clicked outside the items
1922
1923 const QPersistentModelIndex firstColumnIndex = d->viewItems.at(i).index;
1924 const QPersistentModelIndex persistent = indexAt(event->pos());
1925
1926 if (d->pressedIndex != persistent) {
1927 mousePressEvent(event);
1928 return;
1929 }
1930
1931 // signal handlers may change the model
1932 emit doubleClicked(persistent);
1933
1934 if (!persistent.isValid())
1935 return;
1936
1937 if (edit(persistent, DoubleClicked, event) || state() != NoState)
1938 return; // the double click triggered editing
1939
1940 if (!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this))
1941 emit activated(persistent);
1942
1943 d->executePostedLayout(); // we need to make sure viewItems is updated
1944 if (d->itemsExpandable
1945 && d->expandsOnDoubleClick
1946 && d->hasVisibleChildren(persistent)) {
1947 if (!((i < d->viewItems.count()) && (d->viewItems.at(i).index == firstColumnIndex))) {
1948 // find the new index of the item
1949 for (i = 0; i < d->viewItems.count(); ++i) {
1950 if (d->viewItems.at(i).index == firstColumnIndex)
1951 break;
1952 }
1953 if (i == d->viewItems.count())
1954 return;
1955 }
1956 if (d->viewItems.at(i).expanded)
1957 d->collapse(i, true);
1958 else
1959 d->expand(i, true);
1960 updateGeometries();
1961 viewport()->update();
1962 }
1963 }
1964}
1965
1966/*!
1967 \reimp
1968*/
1969void QTreeView::mouseMoveEvent(QMouseEvent *event)
1970{
1971 Q_D(QTreeView);
1972 if (d->itemDecorationAt(event->pos()) == -1) // ### what about expanding/collapsing state ?
1973 QAbstractItemView::mouseMoveEvent(event);
1974}
1975
1976/*!
1977 \reimp
1978*/
1979void QTreeView::keyPressEvent(QKeyEvent *event)
1980{
1981 Q_D(QTreeView);
1982 QModelIndex current = currentIndex();
1983 //this is the management of the expansion
1984 if (d->isIndexValid(current) && d->model && d->itemsExpandable) {
1985 switch (event->key()) {
1986 case Qt::Key_Asterisk: {
1987 // do layouting only once after expanding is done
1988 d->doDelayedItemsLayout();
1989 QStack<QModelIndex> parents;
1990 parents.push(current);
1991 while (!parents.isEmpty()) {
1992 QModelIndex parent = parents.pop();
1993 for (int row = 0; row < d->model->rowCount(parent); ++row) {
1994 QModelIndex child = d->model->index(row, 0, parent);
1995 if (!d->isIndexValid(child))
1996 break;
1997 parents.push(child);
1998 expand(child);
1999 }
2000 }
2001 expand(current);
2002 break; }
2003 case Qt::Key_Plus:
2004 expand(current);
2005 break;
2006 case Qt::Key_Minus:
2007 collapse(current);
2008 break;
2009 }
2010 }
2011
2012 QAbstractItemView::keyPressEvent(event);
2013}
2014
2015/*!
2016 \reimp
2017*/
2018QModelIndex QTreeView::indexAt(const QPoint &point) const
2019{
2020 Q_D(const QTreeView);
2021 d->executePostedLayout();
2022
2023 int visualIndex = d->itemAtCoordinate(point.y());
2024 QModelIndex idx = d->modelIndex(visualIndex);
2025 if (!idx.isValid())
2026 return QModelIndex();
2027
2028 if (d->viewItems.at(visualIndex).spanning)
2029 return idx;
2030
2031 int column = d->columnAt(point.x());
2032 if (column == idx.column())
2033 return idx;
2034 if (column < 0)
2035 return QModelIndex();
2036 return idx.sibling(idx.row(), column);
2037}
2038
2039/*!
2040 Returns the model index of the item above \a index.
2041*/
2042QModelIndex QTreeView::indexAbove(const QModelIndex &index) const
2043{
2044 Q_D(const QTreeView);
2045 if (!d->isIndexValid(index))
2046 return QModelIndex();
2047 d->executePostedLayout();
2048 int i = d->viewIndex(index);
2049 if (--i < 0)
2050 return QModelIndex();
2051 const QModelIndex firstColumnIndex = d->viewItems.at(i).index;
2052 return firstColumnIndex.sibling(firstColumnIndex.row(), index.column());
2053}
2054
2055/*!
2056 Returns the model index of the item below \a index.
2057*/
2058QModelIndex QTreeView::indexBelow(const QModelIndex &index) const
2059{
2060 Q_D(const QTreeView);
2061 if (!d->isIndexValid(index))
2062 return QModelIndex();
2063 d->executePostedLayout();
2064 int i = d->viewIndex(index);
2065 if (++i >= d->viewItems.count())
2066 return QModelIndex();
2067 const QModelIndex firstColumnIndex = d->viewItems.at(i).index;
2068 return firstColumnIndex.sibling(firstColumnIndex.row(), index.column());
2069}
2070
2071/*!
2072 \internal
2073
2074 Lays out the items in the tree view.
2075*/
2076void QTreeView::doItemsLayout()
2077{
2078 Q_D(QTreeView);
2079 if (!d->customIndent) {
2080 // ### Qt 6: move to event()
2081 // QAbstractItemView calls this method in case of a style change,
2082 // so update the indentation here if it wasn't set manually.
2083 d->updateIndentationFromStyle();
2084 }
2085 if (d->hasRemovedItems) {
2086 //clean the QSet that may contains old (and this invalid) indexes
2087 d->hasRemovedItems = false;
2088 QSet<QPersistentModelIndex>::iterator it = d->expandedIndexes.begin();
2089 while (it != d->expandedIndexes.end()) {
2090 if (!it->isValid())
2091 it = d->expandedIndexes.erase(it);
2092 else
2093 ++it;
2094 }
2095 it = d->hiddenIndexes.begin();
2096 while (it != d->hiddenIndexes.end()) {
2097 if (!it->isValid())
2098 it = d->hiddenIndexes.erase(it);
2099 else
2100 ++it;
2101 }
2102 }
2103 d->viewItems.clear(); // prepare for new layout
2104 QModelIndex parent = d->root;
2105 if (d->model->hasChildren(parent)) {
2106 d->layout(-1);
2107 }
2108 QAbstractItemView::doItemsLayout();
2109 d->header->doItemsLayout();
2110}
2111
2112/*!
2113 \reimp
2114*/
2115void QTreeView::reset()
2116{
2117 Q_D(QTreeView);
2118 d->expandedIndexes.clear();
2119 d->hiddenIndexes.clear();
2120 d->spanningIndexes.clear();
2121 d->viewItems.clear();
2122 QAbstractItemView::reset();
2123}
2124
2125/*!
2126 Returns the horizontal offset of the items in the treeview.
2127
2128 Note that the tree view uses the horizontal header section
2129 positions to determine the positions of columns in the view.
2130
2131 \sa verticalOffset()
2132*/
2133int QTreeView::horizontalOffset() const
2134{
2135 Q_D(const QTreeView);
2136 return d->header->offset();
2137}
2138
2139/*!
2140 Returns the vertical offset of the items in the tree view.
2141
2142 \sa horizontalOffset()
2143*/
2144int QTreeView::verticalOffset() const
2145{
2146 Q_D(const QTreeView);
2147 if (d->verticalScrollMode == QAbstractItemView::ScrollPerItem) {
2148 if (d->uniformRowHeights)
2149 return verticalScrollBar()->value() * d->defaultItemHeight;
2150 // If we are scrolling per item and have non-uniform row heights,
2151 // finding the vertical offset in pixels is going to be relatively slow.
2152 // ### find a faster way to do this
2153 d->executePostedLayout();
2154 int offset = 0;
2155 for (int i = 0; i < d->viewItems.count(); ++i) {
2156 if (i == verticalScrollBar()->value())
2157 return offset;
2158 offset += d->itemHeight(i);
2159 }
2160 return 0;
2161 }
2162 // scroll per pixel
2163 return verticalScrollBar()->value();
2164}
2165
2166/*!
2167 Move the cursor in the way described by \a cursorAction, using the
2168 information provided by the button \a modifiers.
2169*/
2170QModelIndex QTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
2171{
2172 Q_D(QTreeView);
2173 Q_UNUSED(modifiers);
2174
2175 d->executePostedLayout();
2176
2177 QModelIndex current = currentIndex();
2178 if (!current.isValid()) {
2179 int i = d->below(-1);
2180 int c = 0;
2181 while (c < d->header->count() && d->header->isSectionHidden(d->header->logicalIndex(c)))
2182 ++c;
2183 if (i < d->viewItems.count() && c < d->header->count()) {
2184 return d->modelIndex(i, d->header->logicalIndex(c));
2185 }
2186 return QModelIndex();
2187 }
2188 int vi = -1;
2189#if 0 /* Used to be included in Qt4 for Q_WS_MAC */ && QT_CONFIG(style_mac)
2190 // Selection behavior is slightly different on the Mac.
2191 if (d->selectionMode == QAbstractItemView::ExtendedSelection
2192 && d->selectionModel
2193 && d->selectionModel->hasSelection()) {
2194
2195 const bool moveUpDown = (cursorAction == MoveUp || cursorAction == MoveDown);
2196 const bool moveNextPrev = (cursorAction == MoveNext || cursorAction == MovePrevious);
2197 const bool contiguousSelection = moveUpDown && (modifiers & Qt::ShiftModifier);
2198
2199 // Use the outermost index in the selection as the current index
2200 if (!contiguousSelection && (moveUpDown || moveNextPrev)) {
2201
2202 // Find outermost index.
2203 const bool useTopIndex = (cursorAction == MoveUp || cursorAction == MovePrevious);
2204 int index = useTopIndex ? INT_MAX : INT_MIN;
2205 const QItemSelection selection = d->selectionModel->selection();
2206 for (int i = 0; i < selection.count(); ++i) {
2207 const QItemSelectionRange &range = selection.at(i);
2208 int candidate = d->viewIndex(useTopIndex ? range.topLeft() : range.bottomRight());
2209 if (candidate >= 0)
2210 index = useTopIndex ? qMin(index, candidate) : qMax(index, candidate);
2211 }
2212
2213 if (index >= 0 && index < INT_MAX)
2214 vi = index;
2215 }
2216 }
2217#endif
2218 if (vi < 0)
2219 vi = qMax(0, d->viewIndex(current));
2220
2221 if (isRightToLeft()) {
2222 if (cursorAction == MoveRight)
2223 cursorAction = MoveLeft;
2224 else if (cursorAction == MoveLeft)
2225 cursorAction = MoveRight;
2226 }
2227 switch (cursorAction) {
2228 case MoveNext:
2229 case MoveDown:
2230#ifdef QT_KEYPAD_NAVIGATION
2231 if (vi == d->viewItems.count()-1 && QApplication::keypadNavigationEnabled())
2232 return d->model->index(0, current.column(), d->root);
2233#endif
2234 return d->modelIndex(d->below(vi), current.column());
2235 case MovePrevious:
2236 case MoveUp:
2237#ifdef QT_KEYPAD_NAVIGATION
2238 if (vi == 0 && QApplication::keypadNavigationEnabled())
2239 return d->modelIndex(d->viewItems.count() - 1, current.column());
2240#endif
2241 return d->modelIndex(d->above(vi), current.column());
2242 case MoveLeft: {
2243 QScrollBar *sb = horizontalScrollBar();
2244 if (vi < d->viewItems.count() && d->viewItems.at(vi).expanded && d->itemsExpandable && sb->value() == sb->minimum()) {
2245 d->collapse(vi, true);
2246 d->moveCursorUpdatedView = true;
2247 } else {
2248 bool descend = style()->styleHint(QStyle::SH_ItemView_ArrowKeysNavigateIntoChildren, 0, this);
2249 if (descend) {
2250 QModelIndex par = current.parent();
2251 if (par.isValid() && par != rootIndex())
2252 return par;
2253 else
2254 descend = false;
2255 }
2256 if (!descend) {
2257 if (d->selectionBehavior == SelectItems || d->selectionBehavior == SelectColumns) {
2258 int visualColumn = d->header->visualIndex(current.column()) - 1;
2259 while (visualColumn >= 0 && isColumnHidden(d->header->logicalIndex(visualColumn)))
2260 visualColumn--;
2261 int newColumn = d->header->logicalIndex(visualColumn);
2262 QModelIndex next = current.sibling(current.row(), newColumn);
2263 if (next.isValid())
2264 return next;
2265 }
2266
2267 int oldValue = sb->value();
2268 sb->setValue(sb->value() - sb->singleStep());
2269 if (oldValue != sb->value())
2270 d->moveCursorUpdatedView = true;
2271 }
2272
2273 }
2274 updateGeometries();
2275 viewport()->update();
2276 break;
2277 }
2278 case MoveRight:
2279 if (vi < d->viewItems.count() && !d->viewItems.at(vi).expanded && d->itemsExpandable
2280 && d->hasVisibleChildren(d->viewItems.at(vi).index)) {
2281 d->expand(vi, true);
2282 d->moveCursorUpdatedView = true;
2283 } else {
2284 bool descend = style()->styleHint(QStyle::SH_ItemView_ArrowKeysNavigateIntoChildren, 0, this);
2285 if (descend) {
2286 QModelIndex idx = d->modelIndex(d->below(vi));
2287 if (idx.parent() == current)
2288 return idx;
2289 else
2290 descend = false;
2291 }
2292 if (!descend) {
2293 if (d->selectionBehavior == SelectItems || d->selectionBehavior == SelectColumns) {
2294 int visualColumn = d->header->visualIndex(current.column()) + 1;
2295 while (visualColumn < d->model->columnCount(current.parent()) && isColumnHidden(d->header->logicalIndex(visualColumn)))
2296 visualColumn++;
2297 const int newColumn = d->header->logicalIndex(visualColumn);
2298 const QModelIndex next = current.sibling(current.row(), newColumn);
2299 if (next.isValid())
2300 return next;
2301 }
2302
2303 //last restort: we change the scrollbar value
2304 QScrollBar *sb = horizontalScrollBar();
2305 int oldValue = sb->value();
2306 sb->setValue(sb->value() + sb->singleStep());
2307 if (oldValue != sb->value())
2308 d->moveCursorUpdatedView = true;
2309 }
2310 }
2311 updateGeometries();
2312 viewport()->update();
2313 break;
2314 case MovePageUp:
2315 return d->modelIndex(d->pageUp(vi), current.column());
2316 case MovePageDown:
2317 return d->modelIndex(d->pageDown(vi), current.column());
2318 case MoveHome:
2319 return d->model->index(0, current.column(), d->root);
2320 case MoveEnd:
2321 return d->modelIndex(d->viewItems.count() - 1, current.column());
2322 }
2323 return current;
2324}
2325
2326/*!
2327 Applies the selection \a command to the items in or touched by the
2328 rectangle, \a rect.
2329
2330 \sa selectionCommand()
2331*/
2332void QTreeView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
2333{
2334 Q_D(QTreeView);
2335 if (!selectionModel() || rect.isNull())
2336 return;
2337
2338 d->executePostedLayout();
2339 QPoint tl(isRightToLeft() ? qMax(rect.left(), rect.right())
2340 : qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom()));
2341 QPoint br(isRightToLeft() ? qMin(rect.left(), rect.right()) :
2342 qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom()));
2343 QModelIndex topLeft = indexAt(tl);
2344 QModelIndex bottomRight = indexAt(br);
2345 if (!topLeft.isValid() && !bottomRight.isValid()) {
2346 if (command & QItemSelectionModel::Clear)
2347 selectionModel()->clear();
2348 return;
2349 }
2350 if (!topLeft.isValid() && !d->viewItems.isEmpty())
2351 topLeft = d->viewItems.constFirst().index;
2352 if (!bottomRight.isValid() && !d->viewItems.isEmpty()) {
2353 const int column = d->header->logicalIndex(d->header->count() - 1);
2354 const QModelIndex index = d->viewItems.constLast().index;
2355 bottomRight = index.sibling(index.row(), column);
2356 }
2357
2358 if (!d->isIndexEnabled(topLeft) || !d->isIndexEnabled(bottomRight))
2359 return;
2360
2361 d->select(topLeft, bottomRight, command);
2362}
2363
2364/*!
2365 Returns the rectangle from the viewport of the items in the given
2366 \a selection.
2367
2368 Since 4.7, the returned region only contains rectangles intersecting
2369 (or included in) the viewport.
2370*/
2371QRegion QTreeView::visualRegionForSelection(const QItemSelection &selection) const
2372{
2373 Q_D(const QTreeView);
2374 if (selection.isEmpty())
2375 return QRegion();
2376
2377 QRegion selectionRegion;
2378 const QRect &viewportRect = d->viewport->rect();
2379 for (const auto &range : selection) {
2380 if (!range.isValid())
2381 continue;
2382 QModelIndex parent = range.parent();
2383 QModelIndex leftIndex = range.topLeft();
2384 int columnCount = d->model->columnCount(parent);
2385 while (leftIndex.isValid() && isIndexHidden(leftIndex)) {
2386 if (leftIndex.column() + 1 < columnCount)
2387 leftIndex = d->model->index(leftIndex.row(), leftIndex.column() + 1, parent);
2388 else
2389 leftIndex = QModelIndex();
2390 }
2391 if (!leftIndex.isValid())
2392 continue;
2393 const QRect leftRect = visualRect(leftIndex);
2394 int top = leftRect.top();
2395 QModelIndex rightIndex = range.bottomRight();
2396 while (rightIndex.isValid() && isIndexHidden(rightIndex)) {
2397 if (rightIndex.column() - 1 >= 0)
2398 rightIndex = d->model->index(rightIndex.row(), rightIndex.column() - 1, parent);
2399 else
2400 rightIndex = QModelIndex();
2401 }
2402 if (!rightIndex.isValid())
2403 continue;
2404 const QRect rightRect = visualRect(rightIndex);
2405 int bottom = rightRect.bottom();
2406 if (top > bottom)
2407 qSwap<int>(top, bottom);
2408 int height = bottom - top + 1;
2409 if (d->header->sectionsMoved()) {
2410 for (int c = range.left(); c <= range.right(); ++c) {
2411 const QRect rangeRect(columnViewportPosition(c), top, columnWidth(c), height);
2412 if (viewportRect.intersects(rangeRect))
2413 selectionRegion += rangeRect;
2414 }
2415 } else {
2416 QRect combined = leftRect|rightRect;
2417 combined.setX(columnViewportPosition(isRightToLeft() ? range.right() : range.left()));
2418 if (viewportRect.intersects(combined))
2419 selectionRegion += combined;
2420 }
2421 }
2422 return selectionRegion;
2423}
2424
2425/*!
2426 \reimp
2427*/
2428QModelIndexList QTreeView::selectedIndexes() const
2429{
2430 QModelIndexList viewSelected;
2431 QModelIndexList modelSelected;
2432 if (selectionModel())
2433 modelSelected = selectionModel()->selectedIndexes();
2434 for (int i = 0; i < modelSelected.count(); ++i) {
2435 // check that neither the parents nor the index is hidden before we add
2436 QModelIndex index = modelSelected.at(i);
2437 while (index.isValid() && !isIndexHidden(index))
2438 index = index.parent();
2439 if (index.isValid())
2440 continue;
2441 viewSelected.append(modelSelected.at(i));
2442 }
2443 return viewSelected;
2444}
2445
2446/*!
2447 Scrolls the contents of the tree view by (\a dx, \a dy).
2448*/
2449void QTreeView::scrollContentsBy(int dx, int dy)
2450{
2451 Q_D(QTreeView);
2452
2453 d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling
2454
2455 dx = isRightToLeft() ? -dx : dx;
2456 if (dx) {
2457 int oldOffset = d->header->offset();
2458 d->header->d_func()->setScrollOffset(horizontalScrollBar(), horizontalScrollMode());
2459 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
2460 int newOffset = d->header->offset();
2461 dx = isRightToLeft() ? newOffset - oldOffset : oldOffset - newOffset;
2462 }
2463 }
2464
2465 const int itemHeight = d->defaultItemHeight <= 0 ? sizeHintForRow(0) : d->defaultItemHeight;
2466 if (d->viewItems.isEmpty() || itemHeight == 0)
2467 return;
2468
2469 // guestimate the number of items in the viewport
2470 int viewCount = d->viewport->height() / itemHeight;
2471 int maxDeltaY = qMin(d->viewItems.count(), viewCount);
2472 // no need to do a lot of work if we are going to redraw the whole thing anyway
2473 if (qAbs(dy) > qAbs(maxDeltaY) && d->editorIndexHash.isEmpty()) {
2474 verticalScrollBar()->update();
2475 d->viewport->update();
2476 return;
2477 }
2478
2479 if (dy && verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
2480 int currentScrollbarValue = verticalScrollBar()->value();
2481 int previousScrollbarValue = currentScrollbarValue + dy; // -(-dy)
2482 int currentViewIndex = currentScrollbarValue; // the first visible item
2483 int previousViewIndex = previousScrollbarValue;
2484 dy = 0;
2485 if (previousViewIndex < currentViewIndex) { // scrolling down
2486 for (int i = previousViewIndex; i < currentViewIndex; ++i) {
2487 if (i < d->viewItems.count())
2488 dy -= d->itemHeight(i);
2489 }
2490 } else if (previousViewIndex > currentViewIndex) { // scrolling up
2491 for (int i = previousViewIndex - 1; i >= currentViewIndex; --i) {
2492 if (i < d->viewItems.count())
2493 dy += d->itemHeight(i);
2494 }
2495 }
2496 }
2497
2498 d->scrollContentsBy(dx, dy);
2499}
2500
2501/*!
2502 This slot is called whenever a column has been moved.
2503*/
2504void QTreeView::columnMoved()
2505{
2506 Q_D(QTreeView);
2507 updateEditorGeometries();
2508 d->viewport->update();
2509}
2510
2511/*!
2512 \internal
2513*/
2514void QTreeView::reexpand()
2515{
2516 // do nothing
2517}
2518
2519/*!
2520 Informs the view that the rows from the \a start row to the \a end row
2521 inclusive have been inserted into the \a parent model item.
2522*/
2523void QTreeView::rowsInserted(const QModelIndex &parent, int start, int end)
2524{
2525 Q_D(QTreeView);
2526 // if we are going to do a complete relayout anyway, there is no need to update
2527 if (d->delayedPendingLayout) {
2528 QAbstractItemView::rowsInserted(parent, start, end);
2529 return;
2530 }
2531
2532 //don't add a hierarchy on a column != 0
2533 if (parent.column() != 0 && parent.isValid()) {
2534 QAbstractItemView::rowsInserted(parent, start, end);
2535 return;
2536 }
2537
2538 const int parentRowCount = d->model->rowCount(parent);
2539 const int delta = end - start + 1;
2540 if (parent != d->root && !d->isIndexExpanded(parent) && parentRowCount > delta) {
2541 QAbstractItemView::rowsInserted(parent, start, end);
2542 return;
2543 }
2544
2545 const int parentItem = d->viewIndex(parent);
2546 if (((parentItem != -1) && d->viewItems.at(parentItem).expanded)
2547 || (parent == d->root)) {
2548 d->doDelayedItemsLayout();
2549 } else if (parentItem != -1 && parentRowCount == delta) {
2550 // the parent just went from 0 children to more. update to re-paint the decoration
2551 d->viewItems[parentItem].hasChildren = true;
2552 viewport()->update();
2553 }
2554 QAbstractItemView::rowsInserted(parent, start, end);
2555}
2556
2557/*!
2558 Informs the view that the rows from the \a start row to the \a end row
2559 inclusive are about to removed from the given \a parent model item.
2560*/
2561void QTreeView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
2562{
2563 Q_D(QTreeView);
2564 QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
2565 d->viewItems.clear();
2566}
2567
2568/*!
2569 \since 4.1
2570
2571 Informs the view that the rows from the \a start row to the \a end row
2572 inclusive have been removed from the given \a parent model item.
2573*/
2574void QTreeView::rowsRemoved(const QModelIndex &parent, int start, int end)
2575{
2576 Q_D(QTreeView);
2577 d->viewItems.clear();
2578 d->doDelayedItemsLayout();
2579 d->hasRemovedItems = true;
2580 d->_q_rowsRemoved(parent, start, end);
2581}
2582
2583/*!
2584 Informs the tree view that the number of columns in the tree view has
2585 changed from \a oldCount to \a newCount.
2586*/
2587void QTreeView::columnCountChanged(int oldCount, int newCount)
2588{
2589 Q_D(QTreeView);
2590 if (oldCount == 0 && newCount > 0) {
2591 //if the first column has just been added we need to relayout.
2592 d->doDelayedItemsLayout();
2593 }
2594
2595 if (isVisible())
2596 updateGeometries();
2597 viewport()->update();
2598}
2599
2600/*!
2601 Resizes the \a column given to the size of its contents.
2602
2603 \sa columnWidth(), setColumnWidth(), sizeHintForColumn(), QHeaderView::resizeContentsPrecision()
2604*/
2605void QTreeView::resizeColumnToContents(int column)
2606{
2607 Q_D(QTreeView);
2608 d->executePostedLayout();
2609 if (column < 0 || column >= d->header->count())
2610 return;
2611 int contents = sizeHintForColumn(column);
2612 int header = d->header->isHidden() ? 0 : d->header->sectionSizeHint(column);
2613 d->header->resizeSection(column, qMax(contents, header));
2614}
2615
2616/*!
2617 \obsolete
2618 \overload
2619
2620 Sorts the model by the values in the given \a column.
2621*/
2622void QTreeView::sortByColumn(int column)
2623{
2624 Q_D(QTreeView);
2625 sortByColumn(column, d->header->sortIndicatorOrder());
2626}
2627
2628/*!
2629 \since 4.2
2630
2631 Sets the model up for sorting by the values in the given \a column and \a order.
2632
2633 \a column may be -1, in which case no sort indicator will be shown
2634 and the model will return to its natural, unsorted order. Note that not
2635 all models support this and may even crash in this case.
2636
2637 \sa sortingEnabled
2638*/
2639void QTreeView::sortByColumn(int column, Qt::SortOrder order)
2640{
2641 Q_D(QTreeView);
2642
2643 //If sorting is enabled will emit a signal connected to _q_sortIndicatorChanged, which then actually sorts
2644 d->header->setSortIndicator(column, order);
2645 //If sorting is not enabled, force to sort now.
2646 if (!d->sortingEnabled)
2647 d->model->sort(column, order);
2648}
2649
2650/*!
2651 \reimp
2652*/
2653void QTreeView::selectAll()
2654{
2655 Q_D(QTreeView);
2656 if (!selectionModel())
2657 return;
2658 SelectionMode mode = d->selectionMode;
2659 d->executePostedLayout(); //make sure we lay out the items
2660 if (mode != SingleSelection && mode != NoSelection && !d->viewItems.isEmpty()) {
2661 const QModelIndex &idx = d->viewItems.constLast().index;
2662 QModelIndex lastItemIndex = idx.sibling(idx.row(), d->model->columnCount(idx.parent()) - 1);
2663 d->select(d->viewItems.constFirst().index, lastItemIndex,
2664 QItemSelectionModel::ClearAndSelect
2665 |QItemSelectionModel::Rows);
2666 }
2667}
2668
2669/*!
2670 \reimp
2671*/
2672QSize QTreeView::viewportSizeHint() const
2673{
2674 Q_D(const QTreeView);
2675 d->executePostedLayout(); // Make sure that viewItems are up to date.
2676
2677 if (d->viewItems.size() == 0)
2678 return QAbstractItemView::viewportSizeHint();
2679
2680 // Get rect for last item
2681 const QRect deepestRect = visualRect(d->viewItems.last().index);
2682
2683 if (!deepestRect.isValid())
2684 return QAbstractItemView::viewportSizeHint();
2685
2686 QSize result = QSize(d->header->length(), deepestRect.bottom() + 1);
2687
2688 // add size for header
2689 result += QSize(0, d->header->isVisible() ? d->header->height() : 0);
2690
2691 // add size for scrollbars
2692 result += QSize(verticalScrollBar()->isVisible() ? verticalScrollBar()->width() : 0,
2693 horizontalScrollBar()->isVisible() ? horizontalScrollBar()->height() : 0);
2694
2695 return result;
2696}
2697
2698/*!
2699 \since 4.2
2700 Expands all expandable items.
2701
2702 Warning: if the model contains a large number of items,
2703 this function will take some time to execute.
2704
2705 \sa collapseAll(), expand(), collapse(), setExpanded()
2706*/
2707void QTreeView::expandAll()
2708{
2709 Q_D(QTreeView);
2710 d->viewItems.clear();
2711 d->interruptDelayedItemsLayout();
2712 d->layout(-1, true);
2713 updateGeometries();
2714 d->viewport->update();
2715}
2716
2717/*!
2718 \since 4.2
2719
2720 Collapses all expanded items.
2721
2722 \sa expandAll(), expand(), collapse(), setExpanded()
2723*/
2724void QTreeView::collapseAll()
2725{
2726 Q_D(QTreeView);
2727 QSet<QPersistentModelIndex> old_expandedIndexes;
2728 old_expandedIndexes = d->expandedIndexes;
2729 d->expandedIndexes.clear();
2730 if (!signalsBlocked() && isSignalConnected(QMetaMethod::fromSignal(&QTreeView::collapsed))) {
2731 QSet<QPersistentModelIndex>::const_iterator i = old_expandedIndexes.constBegin();
2732 for (; i != old_expandedIndexes.constEnd(); ++i) {
2733 const QPersistentModelIndex &mi = (*i);
2734 if (mi.isValid() && !(mi.flags() & Qt::ItemNeverHasChildren))
2735 emit collapsed(mi);
2736 }
2737 }
2738 doItemsLayout();
2739}
2740
2741/*!
2742 \since 4.3
2743 Expands all expandable items to the given \a depth.
2744
2745 \sa expandAll(), collapseAll(), expand(), collapse(), setExpanded()
2746*/
2747void QTreeView::expandToDepth(int depth)
2748{
2749 Q_D(QTreeView);
2750 d->viewItems.clear();
2751 QSet<QPersistentModelIndex> old_expandedIndexes;
2752 old_expandedIndexes = d->expandedIndexes;
2753 d->expandedIndexes.clear();
2754 d->interruptDelayedItemsLayout();
2755 d->layout(-1);
2756 for (int i = 0; i < d->viewItems.count(); ++i) {
2757 if (d->viewItems.at(i).level <= (uint)depth) {
2758 d->viewItems[i].expanded = true;
2759 d->layout(i);
2760 d->storeExpanded(d->viewItems.at(i).index);
2761 }
2762 }
2763
2764 bool someSignalEnabled = isSignalConnected(QMetaMethod::fromSignal(&QTreeView::collapsed));
2765 someSignalEnabled |= isSignalConnected(QMetaMethod::fromSignal(&QTreeView::expanded));
2766
2767 if (!signalsBlocked() && someSignalEnabled) {
2768 // emit signals
2769 QSet<QPersistentModelIndex> collapsedIndexes = old_expandedIndexes - d->expandedIndexes;
2770 QSet<QPersistentModelIndex>::const_iterator i = collapsedIndexes.constBegin();
2771 for (; i != collapsedIndexes.constEnd(); ++i) {
2772 const QPersistentModelIndex &mi = (*i);
2773 if (mi.isValid() && !(mi.flags() & Qt::ItemNeverHasChildren))
2774 emit collapsed(mi);
2775 }
2776
2777 QSet<QPersistentModelIndex> expandedIndexs = d->expandedIndexes - old_expandedIndexes;
2778 i = expandedIndexs.constBegin();
2779 for (; i != expandedIndexs.constEnd(); ++i) {
2780 const QPersistentModelIndex &mi = (*i);
2781 if (mi.isValid() && !(mi.flags() & Qt::ItemNeverHasChildren))
2782 emit expanded(mi);
2783 }
2784 }
2785
2786 updateGeometries();
2787 d->viewport->update();
2788}
2789
2790/*!
2791 This function is called whenever \a{column}'s size is changed in
2792 the header. \a oldSize and \a newSize give the previous size and
2793 the new size in pixels.
2794
2795 \sa setColumnWidth()
2796*/
2797void QTreeView::columnResized(int column, int /* oldSize */, int /* newSize */)
2798{
2799 Q_D(QTreeView);
2800 d->columnsToUpdate.append(column);
2801 if (d->columnResizeTimerID == 0)
2802 d->columnResizeTimerID = startTimer(0);
2803}
2804
2805/*!
2806 \reimp
2807*/
2808void QTreeView::updateGeometries()
2809{
2810 Q_D(QTreeView);
2811 if (d->header) {
2812 if (d->