1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui 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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include "qtreeview.h"
42 
43 #ifndef QT_NO_TREEVIEW
44 #include <qheaderview.h>
45 #include <qitemdelegate.h>
46 #include <qapplication.h>
47 #include <qscrollbar.h>
48 #include <qpainter.h>
49 #include <qstack.h>
50 #include <qstyle.h>
51 #include <qstyleoption.h>
52 #include <qevent.h>
53 #include <qpen.h>
54 #include <qdebug.h>
55 #ifndef QT_NO_ACCESSIBILITY
56 #include <qaccessible.h>
57 #include <qaccessible2.h>
58 #endif
59 
60 #include <private/qtreeview_p.h>
61 
62 QT_BEGIN_NAMESPACE
63 
64 /*!
65     \class QTreeView
66     \brief The QTreeView class provides a default model/view implementation of a tree view.
67 
68     \ingroup model-view
69     \ingroup advanced
70 
71 
72     A QTreeView implements a tree representation of items from a
73     model. This class is used to provide standard hierarchical lists that
74     were previously provided by the \c QListView class, but using the more
75     flexible approach provided by Qt's model/view architecture.
76 
77     The QTreeView class is one of the \l{Model/View Classes} and is part of
78     Qt's \l{Model/View Programming}{model/view framework}.
79 
80     QTreeView implements the interfaces defined by the
81     QAbstractItemView class to allow it to display data provided by
82     models derived from the QAbstractItemModel class.
83 
84     It is simple to construct a tree view displaying data from a
85     model. In the following example, the contents of a directory are
86     supplied by a QFileSystemModel and displayed as a tree:
87 
88     \snippet doc/src/snippets/shareddirmodel/main.cpp 3
89     \snippet doc/src/snippets/shareddirmodel/main.cpp 6
90 
91     The model/view architecture ensures that the contents of the tree view
92     are updated as the model changes.
93 
94     Items that have children can be in an expanded (children are
95     visible) or collapsed (children are hidden) state. When this state
96     changes a collapsed() or expanded() signal is emitted with the
97     model index of the relevant item.
98 
99     The amount of indentation used to indicate levels of hierarchy is
100     controlled by the \l indentation property.
101 
102     Headers in tree views are constructed using the QHeaderView class and can
103     be hidden using \c{header()->hide()}. Note that each header is configured
104     with its \l{QHeaderView::}{stretchLastSection} property set to true,
105     ensuring that the view does not waste any of the space assigned to it for
106     its header. If this value is set to true, this property will override the
107     resize mode set on the last section in the header.
108 
109 
110     \section1 Key Bindings
111 
112     QTreeView supports a set of key bindings that enable the user to
113     navigate in the view and interact with the contents of items:
114 
115     \table
116     \header \o Key \o Action
117     \row \o Up   \o Moves the cursor to the item in the same column on
118          the previous row. If the parent of the current item has no more rows to
119          navigate to, the cursor moves to the relevant item in the last row
120          of the sibling that precedes the parent.
121     \row \o Down \o Moves the cursor to the item in the same column on
122          the next row. If the parent of the current item has no more rows to
123          navigate to, the cursor moves to the relevant item in the first row
124          of the sibling that follows the parent.
125     \row \o Left  \o Hides the children of the current item (if present)
126          by collapsing a branch.
127     \row \o Minus  \o Same as LeftArrow.
128     \row \o Right \o Reveals the children of the current item (if present)
129          by expanding a branch.
130     \row \o Plus  \o Same as RightArrow.
131     \row \o Asterisk  \o Expands all children of the current item (if present).
132     \row \o PageUp   \o Moves the cursor up one page.
133     \row \o PageDown \o Moves the cursor down one page.
134     \row \o Home \o Moves the cursor to an item in the same column of the first
135          row of the first top-level item in the model.
136     \row \o End  \o Moves the cursor to an item in the same column of the last
137          row of the last top-level item in the model.
138     \row \o F2   \o In editable models, this opens the current item for editing.
139          The Escape key can be used to cancel the editing process and revert
140          any changes to the data displayed.
141     \endtable
142 
143     \omit
144     Describe the expanding/collapsing concept if not covered elsewhere.
145     \endomit
146 
147     \table 100%
148     \row \o \inlineimage windowsxp-treeview.png Screenshot of a Windows XP style tree view
149          \o \inlineimage macintosh-treeview.png Screenshot of a Macintosh style tree view
150          \o \inlineimage plastique-treeview.png Screenshot of a Plastique style tree view
151     \row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} tree view.
152          \o A \l{Macintosh Style Widget Gallery}{Macintosh style} tree view.
153          \o A \l{Plastique Style Widget Gallery}{Plastique style} tree view.
154     \endtable
155 
156     \section1 Improving Performance
157 
158     It is possible to give the view hints about the data it is handling in order
159     to improve its performance when displaying large numbers of items. One approach
160     that can be taken for views that are intended to display items with equal heights
161     is to set the \l uniformRowHeights property to true.
162 
163     \sa QListView, QTreeWidget, {View Classes}, QAbstractItemModel, QAbstractItemView,
164         {Dir View Example}
165 */
166 
167 
168 /*!
169   \fn void QTreeView::expanded(const QModelIndex &index)
170 
171   This signal is emitted when the item specified by \a index is expanded.
172 */
173 
174 
175 /*!
176   \fn void QTreeView::collapsed(const QModelIndex &index)
177 
178   This signal is emitted when the item specified by \a index is collapsed.
179 */
180 
181 /*!
182     Constructs a tree view with a \a parent to represent a model's
183     data. Use setModel() to set the model.
184 
185     \sa QAbstractItemModel
186 */
QTreeView(QWidget * parent)187 QTreeView::QTreeView(QWidget *parent)
188     : QAbstractItemView(*new QTreeViewPrivate, parent)
189 {
190     Q_D(QTreeView);
191     d->initialize();
192 }
193 
194 /*!
195   \internal
196 */
QTreeView(QTreeViewPrivate & dd,QWidget * parent)197 QTreeView::QTreeView(QTreeViewPrivate &dd, QWidget *parent)
198     : QAbstractItemView(dd, parent)
199 {
200     Q_D(QTreeView);
201     d->initialize();
202 }
203 
204 /*!
205   Destroys the tree view.
206 */
~QTreeView()207 QTreeView::~QTreeView()
208 {
209 }
210 
211 /*!
212   \reimp
213 */
setModel(QAbstractItemModel * model)214 void QTreeView::setModel(QAbstractItemModel *model)
215 {
216     Q_D(QTreeView);
217     if (model == d->model)
218         return;
219     if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
220         disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
221                 this, SLOT(rowsRemoved(QModelIndex,int,int)));
222 
223         disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_modelAboutToBeReset()));
224     }
225 
226     if (d->selectionModel) { // support row editing
227         disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
228                    d->model, SLOT(submit()));
229         disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
230                    this, SLOT(rowsRemoved(QModelIndex,int,int)));
231         disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_modelAboutToBeReset()));
232     }
233     d->viewItems.clear();
234     d->expandedIndexes.clear();
235     d->hiddenIndexes.clear();
236     d->header->setModel(model);
237     QAbstractItemView::setModel(model);
238 
239     // QAbstractItemView connects to a private slot
240     disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
241                this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
242     // do header layout after the tree
243     disconnect(d->model, SIGNAL(layoutChanged()),
244                d->header, SLOT(_q_layoutChanged()));
245     // QTreeView has a public slot for this
246     connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
247             this, SLOT(rowsRemoved(QModelIndex,int,int)));
248 
249     connect(d->model, SIGNAL(modelAboutToBeReset()), SLOT(_q_modelAboutToBeReset()));
250 
251     if (d->sortingEnabled)
252         d->_q_sortIndicatorChanged(header()->sortIndicatorSection(), header()->sortIndicatorOrder());
253 }
254 
255 /*!
256   \reimp
257 */
setRootIndex(const QModelIndex & index)258 void QTreeView::setRootIndex(const QModelIndex &index)
259 {
260     Q_D(QTreeView);
261     d->header->setRootIndex(index);
262     QAbstractItemView::setRootIndex(index);
263 }
264 
265 /*!
266   \reimp
267 */
setSelectionModel(QItemSelectionModel * selectionModel)268 void QTreeView::setSelectionModel(QItemSelectionModel *selectionModel)
269 {
270     Q_D(QTreeView);
271     Q_ASSERT(selectionModel);
272     if (d->selectionModel) {
273         // support row editing
274         disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
275                    d->model, SLOT(submit()));
276     }
277 
278     d->header->setSelectionModel(selectionModel);
279     QAbstractItemView::setSelectionModel(selectionModel);
280 
281     if (d->selectionModel) {
282         // support row editing
283         connect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
284                 d->model, SLOT(submit()));
285     }
286 }
287 
288 /*!
289   Returns the header for the tree view.
290 
291   \sa QAbstractItemModel::headerData()
292 */
header() const293 QHeaderView *QTreeView::header() const
294 {
295     Q_D(const QTreeView);
296     return d->header;
297 }
298 
299 /*!
300     Sets the header for the tree view, to the given \a header.
301 
302     The view takes ownership over the given \a header and deletes it
303     when a new header is set.
304 
305     \sa QAbstractItemModel::headerData()
306 */
setHeader(QHeaderView * header)307 void QTreeView::setHeader(QHeaderView *header)
308 {
309     Q_D(QTreeView);
310     if (header == d->header || !header)
311         return;
312     if (d->header && d->header->parent() == this)
313         delete d->header;
314     d->header = header;
315     d->header->setParent(this);
316 
317     if (!d->header->model()) {
318         d->header->setModel(d->model);
319         if (d->selectionModel)
320             d->header->setSelectionModel(d->selectionModel);
321     }
322 
323     connect(d->header, SIGNAL(sectionResized(int,int,int)),
324             this, SLOT(columnResized(int,int,int)));
325     connect(d->header, SIGNAL(sectionMoved(int,int,int)),
326             this, SLOT(columnMoved()));
327     connect(d->header, SIGNAL(sectionCountChanged(int,int)),
328             this, SLOT(columnCountChanged(int,int)));
329     connect(d->header, SIGNAL(sectionHandleDoubleClicked(int)),
330             this, SLOT(resizeColumnToContents(int)));
331     connect(d->header, SIGNAL(geometriesChanged()),
332             this, SLOT(updateGeometries()));
333 
334     setSortingEnabled(d->sortingEnabled);
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 */
autoExpandDelay() const348 int QTreeView::autoExpandDelay() const
349 {
350     Q_D(const QTreeView);
351     return d->autoExpandDelay;
352 }
353 
setAutoExpandDelay(int delay)354 void 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, this property has a value of 20.
370 */
indentation() const371 int QTreeView::indentation() const
372 {
373     Q_D(const QTreeView);
374     return d->indent;
375 }
376 
setIndentation(int i)377 void QTreeView::setIndentation(int i)
378 {
379     Q_D(QTreeView);
380     if (i != d->indent) {
381         d->indent = i;
382         d->viewport->update();
383     }
384 }
385 
386 /*!
387   \property QTreeView::rootIsDecorated
388   \brief whether to show controls for expanding and collapsing top-level items
389 
390   Items with children are typically shown with controls to expand and collapse
391   them, allowing their children to be shown or hidden. If this property is
392   false, these controls are not shown for top-level items. This can be used to
393   make a single level tree structure appear like a simple list of items.
394 
395   By default, this property is true.
396 */
rootIsDecorated() const397 bool QTreeView::rootIsDecorated() const
398 {
399     Q_D(const QTreeView);
400     return d->rootDecoration;
401 }
402 
setRootIsDecorated(bool show)403 void QTreeView::setRootIsDecorated(bool show)
404 {
405     Q_D(QTreeView);
406     if (show != d->rootDecoration) {
407         d->rootDecoration = show;
408         d->viewport->update();
409     }
410 }
411 
412 /*!
413   \property QTreeView::uniformRowHeights
414   \brief whether all items in the treeview have the same height
415 
416   This property should only be set to true if it is guaranteed that all items
417   in the view has the same height. This enables the view to do some
418   optimizations.
419 
420   The height is obtained from the first item in the view.  It is updated
421   when the data changes on that item.
422 
423   By default, this property is false.
424 */
uniformRowHeights() const425 bool QTreeView::uniformRowHeights() const
426 {
427     Q_D(const QTreeView);
428     return d->uniformRowHeights;
429 }
430 
setUniformRowHeights(bool uniform)431 void QTreeView::setUniformRowHeights(bool uniform)
432 {
433     Q_D(QTreeView);
434     d->uniformRowHeights = uniform;
435 }
436 
437 /*!
438   \property QTreeView::itemsExpandable
439   \brief whether the items are expandable by the user.
440 
441   This property holds whether the user can expand and collapse items
442   interactively.
443 
444   By default, this property is true.
445 
446 */
itemsExpandable() const447 bool QTreeView::itemsExpandable() const
448 {
449     Q_D(const QTreeView);
450     return d->itemsExpandable;
451 }
452 
setItemsExpandable(bool enable)453 void QTreeView::setItemsExpandable(bool enable)
454 {
455     Q_D(QTreeView);
456     d->itemsExpandable = enable;
457 }
458 
459 /*!
460   \property QTreeView::expandsOnDoubleClick
461   \since 4.4
462   \brief whether the items can be expanded by double-clicking.
463 
464   This property holds whether the user can expand and collapse items
465   by double-clicking. The default value is true.
466 
467   \sa itemsExpandable
468 */
expandsOnDoubleClick() const469 bool QTreeView::expandsOnDoubleClick() const
470 {
471     Q_D(const QTreeView);
472     return d->expandsOnDoubleClick;
473 }
474 
setExpandsOnDoubleClick(bool enable)475 void QTreeView::setExpandsOnDoubleClick(bool enable)
476 {
477     Q_D(QTreeView);
478     d->expandsOnDoubleClick = enable;
479 }
480 
481 /*!
482   Returns the horizontal position of the \a column in the viewport.
483 */
columnViewportPosition(int column) const484 int QTreeView::columnViewportPosition(int column) const
485 {
486     Q_D(const QTreeView);
487     return d->header->sectionViewportPosition(column);
488 }
489 
490 /*!
491   Returns the width of the \a column.
492 
493   \sa resizeColumnToContents(), setColumnWidth()
494 */
columnWidth(int column) const495 int QTreeView::columnWidth(int column) const
496 {
497     Q_D(const QTreeView);
498     return d->header->sectionSize(column);
499 }
500 
501 /*!
502   \since 4.2
503 
504   Sets the width of the given \a column to the \a width specified.
505 
506   \sa columnWidth(), resizeColumnToContents()
507 */
setColumnWidth(int column,int width)508 void QTreeView::setColumnWidth(int column, int width)
509 {
510     Q_D(QTreeView);
511     d->header->resizeSection(column, width);
512 }
513 
514 /*!
515   Returns the column in the tree view whose header covers the \a x
516   coordinate given.
517 */
columnAt(int x) const518 int QTreeView::columnAt(int x) const
519 {
520     Q_D(const QTreeView);
521     return d->header->logicalIndexAt(x);
522 }
523 
524 /*!
525     Returns true if the \a column is hidden; otherwise returns false.
526 
527     \sa hideColumn(), isRowHidden()
528 */
isColumnHidden(int column) const529 bool QTreeView::isColumnHidden(int column) const
530 {
531     Q_D(const QTreeView);
532     return d->header->isSectionHidden(column);
533 }
534 
535 /*!
536   If \a hide is true the \a column is hidden, otherwise the \a column is shown.
537 
538   \sa hideColumn(), setRowHidden()
539 */
setColumnHidden(int column,bool hide)540 void QTreeView::setColumnHidden(int column, bool hide)
541 {
542     Q_D(QTreeView);
543     if (column < 0 || column >= d->header->count())
544         return;
545     d->header->setSectionHidden(column, hide);
546 }
547 
548 /*!
549   \property QTreeView::headerHidden
550   \brief whether the header is shown or not.
551   \since 4.4
552 
553   If this property is true, the header is not shown otherwise it is.
554   The default value is false.
555 
556   \sa header()
557 */
isHeaderHidden() const558 bool QTreeView::isHeaderHidden() const
559 {
560     Q_D(const QTreeView);
561     return d->header->isHidden();
562 }
563 
setHeaderHidden(bool hide)564 void QTreeView::setHeaderHidden(bool hide)
565 {
566     Q_D(QTreeView);
567     d->header->setHidden(hide);
568 }
569 
570 /*!
571     Returns true if the item in the given \a row of the \a parent is hidden;
572     otherwise returns false.
573 
574     \sa setRowHidden(), isColumnHidden()
575 */
isRowHidden(int row,const QModelIndex & parent) const576 bool QTreeView::isRowHidden(int row, const QModelIndex &parent) const
577 {
578     Q_D(const QTreeView);
579     if (!d->model)
580         return false;
581     return d->isRowHidden(d->model->index(row, 0, parent));
582 }
583 
584 /*!
585   If \a hide is true the \a row with the given \a parent is hidden, otherwise the \a row is shown.
586 
587   \sa isRowHidden(), setColumnHidden()
588 */
setRowHidden(int row,const QModelIndex & parent,bool hide)589 void QTreeView::setRowHidden(int row, const QModelIndex &parent, bool hide)
590 {
591     Q_D(QTreeView);
592     if (!d->model)
593         return;
594     QModelIndex index = d->model->index(row, 0, parent);
595     if (!index.isValid())
596         return;
597 
598     if (hide) {
599         d->hiddenIndexes.insert(index);
600     } else if(d->isPersistent(index)) { //if the index is not persistent, it cannot be in the set
601         d->hiddenIndexes.remove(index);
602     }
603 
604     d->doDelayedItemsLayout();
605 }
606 
607 /*!
608   \since 4.3
609 
610   Returns true if the item in first column in the given \a row
611   of the \a parent is spanning all the columns; otherwise returns false.
612 
613   \sa setFirstColumnSpanned()
614 */
isFirstColumnSpanned(int row,const QModelIndex & parent) const615 bool QTreeView::isFirstColumnSpanned(int row, const QModelIndex &parent) const
616 {
617     Q_D(const QTreeView);
618     if (d->spanningIndexes.isEmpty() || !d->model)
619         return false;
620     QModelIndex index = d->model->index(row, 0, parent);
621     for (int i = 0; i < d->spanningIndexes.count(); ++i)
622         if (d->spanningIndexes.at(i) == index)
623             return true;
624     return false;
625 }
626 
627 /*!
628   \since 4.3
629 
630   If \a span is true the item in the first column in the \a row
631   with the given \a parent is set to span all columns, otherwise all items
632   on the \a row are shown.
633 
634   \sa isFirstColumnSpanned()
635 */
setFirstColumnSpanned(int row,const QModelIndex & parent,bool span)636 void QTreeView::setFirstColumnSpanned(int row, const QModelIndex &parent, bool span)
637 {
638     Q_D(QTreeView);
639     if (!d->model)
640         return;
641     QModelIndex index = d->model->index(row, 0, parent);
642     if (!index.isValid())
643         return;
644 
645     if (span) {
646         QPersistentModelIndex persistent(index);
647         if (!d->spanningIndexes.contains(persistent))
648             d->spanningIndexes.append(persistent);
649     } else {
650         QPersistentModelIndex persistent(index);
651         int i = d->spanningIndexes.indexOf(persistent);
652         if (i >= 0)
653             d->spanningIndexes.remove(i);
654     }
655 
656     d->executePostedLayout();
657     int i = d->viewIndex(index);
658     if (i >= 0)
659         d->viewItems[i].spanning = span;
660 
661     d->viewport->update();
662 }
663 
664 /*!
665   \reimp
666 */
dataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight)667 void QTreeView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
668 {
669     Q_D(QTreeView);
670 
671     // if we are going to do a complete relayout anyway, there is no need to update
672     if (d->delayedPendingLayout)
673         return;
674 
675     // refresh the height cache here; we don't really lose anything by getting the size hint,
676     // since QAbstractItemView::dataChanged() will get the visualRect for the items anyway
677 
678     bool sizeChanged = false;
679     int topViewIndex = d->viewIndex(topLeft);
680     if (topViewIndex == 0) {
681         int newDefaultItemHeight = indexRowSizeHint(topLeft);
682         sizeChanged = d->defaultItemHeight != newDefaultItemHeight;
683         d->defaultItemHeight = newDefaultItemHeight;
684     }
685 
686     if (topViewIndex != -1) {
687         if (topLeft.row() == bottomRight.row()) {
688             int oldHeight = d->itemHeight(topViewIndex);
689             d->invalidateHeightCache(topViewIndex);
690             sizeChanged |= (oldHeight != d->itemHeight(topViewIndex));
691             if (topLeft.column() == 0)
692                 d->viewItems[topViewIndex].hasChildren = d->hasVisibleChildren(topLeft);
693         } else {
694             int bottomViewIndex = d->viewIndex(bottomRight);
695             for (int i = topViewIndex; i <= bottomViewIndex; ++i) {
696                 int oldHeight = d->itemHeight(i);
697                 d->invalidateHeightCache(i);
698                 sizeChanged |= (oldHeight != d->itemHeight(i));
699                 if (topLeft.column() == 0)
700                     d->viewItems[i].hasChildren = d->hasVisibleChildren(d->viewItems.at(i).index);
701             }
702         }
703     }
704 
705     if (sizeChanged) {
706         d->updateScrollBars();
707         d->viewport->update();
708     }
709     QAbstractItemView::dataChanged(topLeft, bottomRight);
710 }
711 
712 /*!
713   Hides the \a column given.
714 
715   \note This function should only be called after the model has been
716   initialized, as the view needs to know the number of columns in order to
717   hide \a column.
718 
719   \sa showColumn(), setColumnHidden()
720 */
hideColumn(int column)721 void QTreeView::hideColumn(int column)
722 {
723     Q_D(QTreeView);
724     d->header->hideSection(column);
725 }
726 
727 /*!
728   Shows the given \a column in the tree view.
729 
730   \sa hideColumn(), setColumnHidden()
731 */
showColumn(int column)732 void QTreeView::showColumn(int column)
733 {
734     Q_D(QTreeView);
735     d->header->showSection(column);
736 }
737 
738 /*!
739   \fn void QTreeView::expand(const QModelIndex &index)
740 
741   Expands the model item specified by the \a index.
742 
743   \sa expanded()
744 */
expand(const QModelIndex & index)745 void QTreeView::expand(const QModelIndex &index)
746 {
747     Q_D(QTreeView);
748     if (!d->isIndexValid(index))
749         return;
750     if (d->delayedPendingLayout) {
751         //A complete relayout is going to be performed, just store the expanded index, no need to layout.
752         if (d->storeExpanded(index))
753             emit expanded(index);
754         return;
755     }
756 
757     int i = d->viewIndex(index);
758     if (i != -1) { // is visible
759         d->expand(i, true);
760         if (!d->isAnimating()) {
761             updateGeometries();
762             d->viewport->update();
763         }
764     } else if (d->storeExpanded(index)) {
765         emit expanded(index);
766     }
767 }
768 
769 /*!
770   \fn void QTreeView::collapse(const QModelIndex &index)
771 
772   Collapses the model item specified by the \a index.
773 
774   \sa collapsed()
775 */
collapse(const QModelIndex & index)776 void QTreeView::collapse(const QModelIndex &index)
777 {
778     Q_D(QTreeView);
779     if (!d->isIndexValid(index))
780         return;
781     //if the current item is now invisible, the autoscroll will expand the tree to see it, so disable the autoscroll
782     d->delayedAutoScroll.stop();
783 
784     if (d->delayedPendingLayout) {
785         //A complete relayout is going to be performed, just un-store the expanded index, no need to layout.
786         if (d->isPersistent(index) && d->expandedIndexes.remove(index))
787             emit collapsed(index);
788         return;
789     }
790     int i = d->viewIndex(index);
791     if (i != -1) { // is visible
792         d->collapse(i, true);
793         if (!d->isAnimating()) {
794             updateGeometries();
795             viewport()->update();
796         }
797     } else {
798         if (d->isPersistent(index) && d->expandedIndexes.remove(index))
799             emit collapsed(index);
800     }
801 }
802 
803 /*!
804   \fn bool QTreeView::isExpanded(const QModelIndex &index) const
805 
806   Returns true if the model item \a index is expanded; otherwise returns
807   false.
808 
809   \sa expand(), expanded(), setExpanded()
810 */
isExpanded(const QModelIndex & index) const811 bool QTreeView::isExpanded(const QModelIndex &index) const
812 {
813     Q_D(const QTreeView);
814     return d->isIndexExpanded(index);
815 }
816 
817 /*!
818   Sets the item referred to by \a index to either collapse or expanded,
819   depending on the value of \a expanded.
820 
821   \sa expanded(), expand(), isExpanded()
822 */
setExpanded(const QModelIndex & index,bool expanded)823 void QTreeView::setExpanded(const QModelIndex &index, bool expanded)
824 {
825     if (expanded)
826         this->expand(index);
827     else
828         this->collapse(index);
829 }
830 
831 /*!
832     \since 4.2
833     \property QTreeView::sortingEnabled
834     \brief whether sorting is enabled
835 
836     If this property is true, sorting is enabled for the tree; if the property
837     is false, sorting is not enabled. The default value is false.
838 
839     \note In order to avoid performance issues, it is recommended that
840     sorting is enabled \e after inserting the items into the tree.
841     Alternatively, you could also insert the items into a list before inserting
842     the items into the tree.
843 
844     \sa sortByColumn()
845 */
846 
setSortingEnabled(bool enable)847 void QTreeView::setSortingEnabled(bool enable)
848 {
849     Q_D(QTreeView);
850     header()->setSortIndicatorShown(enable);
851     header()->setClickable(enable);
852     if (enable) {
853         //sortByColumn has to be called before we connect or set the sortingEnabled flag
854         // because otherwise it will not call sort on the model.
855         sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder());
856         connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
857                 this, SLOT(_q_sortIndicatorChanged(int,Qt::SortOrder)), Qt::UniqueConnection);
858     } else {
859         disconnect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
860                    this, SLOT(_q_sortIndicatorChanged(int,Qt::SortOrder)));
861     }
862     d->sortingEnabled = enable;
863 }
864 
isSortingEnabled() const865 bool QTreeView::isSortingEnabled() const
866 {
867     Q_D(const QTreeView);
868     return d->sortingEnabled;
869 }
870 
871 /*!
872     \since 4.2
873     \property QTreeView::animated
874     \brief whether animations are enabled
875 
876     If this property is true the treeview will animate expandsion
877     and collasping of branches. If this property is false, the treeview
878     will expand or collapse branches immediately without showing
879     the animation.
880 
881     By default, this property is false.
882 */
883 
setAnimated(bool animate)884 void QTreeView::setAnimated(bool animate)
885 {
886     Q_D(QTreeView);
887     d->animationsEnabled = animate;
888 }
889 
isAnimated() const890 bool QTreeView::isAnimated() const
891 {
892     Q_D(const QTreeView);
893     return d->animationsEnabled;
894 }
895 
896 /*!
897     \since 4.2
898     \property QTreeView::allColumnsShowFocus
899     \brief whether items should show keyboard focus using all columns
900 
901     If this property is true all columns will show focus, otherwise only
902     one column will show focus.
903 
904     The default is false.
905 */
906 
setAllColumnsShowFocus(bool enable)907 void QTreeView::setAllColumnsShowFocus(bool enable)
908 {
909     Q_D(QTreeView);
910     if (d->allColumnsShowFocus == enable)
911         return;
912     d->allColumnsShowFocus = enable;
913     d->viewport->update();
914 }
915 
allColumnsShowFocus() const916 bool QTreeView::allColumnsShowFocus() const
917 {
918     Q_D(const QTreeView);
919     return d->allColumnsShowFocus;
920 }
921 
922 /*!
923     \property QTreeView::wordWrap
924     \brief the item text word-wrapping policy
925     \since 4.3
926 
927     If this property is true then the item text is wrapped where
928     necessary at word-breaks; otherwise it is not wrapped at all.
929     This property is false by default.
930 
931     Note that even if wrapping is enabled, the cell will not be
932     expanded to fit all text. Ellipsis will be inserted according to
933     the current \l{QAbstractItemView::}{textElideMode}.
934 */
setWordWrap(bool on)935 void QTreeView::setWordWrap(bool on)
936 {
937     Q_D(QTreeView);
938     if (d->wrapItemText == on)
939         return;
940     d->wrapItemText = on;
941     d->doDelayedItemsLayout();
942 }
943 
wordWrap() const944 bool QTreeView::wordWrap() const
945 {
946     Q_D(const QTreeView);
947     return d->wrapItemText;
948 }
949 
950 
951 /*!
952   \reimp
953  */
keyboardSearch(const QString & search)954 void QTreeView::keyboardSearch(const QString &search)
955 {
956     Q_D(QTreeView);
957     if (!d->model->rowCount(d->root) || !d->model->columnCount(d->root))
958         return;
959 
960     QModelIndex start;
961     if (currentIndex().isValid())
962         start = currentIndex();
963     else
964         start = d->model->index(0, 0, d->root);
965 
966     bool skipRow = false;
967     bool keyboardTimeWasValid = d->keyboardInputTime.isValid();
968     qint64 keyboardInputTimeElapsed = d->keyboardInputTime.restart();
969     if (search.isEmpty() || !keyboardTimeWasValid
970         || keyboardInputTimeElapsed > QApplication::keyboardInputInterval()) {
971         d->keyboardInput = search;
972         skipRow = currentIndex().isValid(); //if it is not valid we should really start at QModelIndex(0,0)
973     } else {
974         d->keyboardInput += search;
975     }
976 
977     // special case for searches with same key like 'aaaaa'
978     bool sameKey = false;
979     if (d->keyboardInput.length() > 1) {
980         int c = d->keyboardInput.count(d->keyboardInput.at(d->keyboardInput.length() - 1));
981         sameKey = (c == d->keyboardInput.length());
982         if (sameKey)
983             skipRow = true;
984     }
985 
986     // skip if we are searching for the same key or a new search started
987     if (skipRow) {
988         if (indexBelow(start).isValid())
989             start = indexBelow(start);
990         else
991             start = d->model->index(0, start.column(), d->root);
992     }
993 
994     d->executePostedLayout();
995     int startIndex = d->viewIndex(start);
996     if (startIndex <= -1)
997         return;
998 
999     int previousLevel = -1;
1000     int bestAbove = -1;
1001     int bestBelow = -1;
1002     QString searchString = sameKey ? QString(d->keyboardInput.at(0)) : d->keyboardInput;
1003     for (int i = 0; i < d->viewItems.count(); ++i) {
1004         if ((int)d->viewItems.at(i).level > previousLevel) {
1005             QModelIndex searchFrom = d->viewItems.at(i).index;
1006             if (searchFrom.parent() == start.parent())
1007                 searchFrom = start;
1008             QModelIndexList match = d->model->match(searchFrom, Qt::DisplayRole, searchString);
1009             if (match.count()) {
1010                 int hitIndex = d->viewIndex(match.at(0));
1011                 if (hitIndex >= 0 && hitIndex < startIndex)
1012                     bestAbove = bestAbove == -1 ? hitIndex : qMin(hitIndex, bestAbove);
1013                 else if (hitIndex >= startIndex)
1014                     bestBelow = bestBelow == -1 ? hitIndex : qMin(hitIndex, bestBelow);
1015             }
1016         }
1017         previousLevel = d->viewItems.at(i).level;
1018     }
1019 
1020     QModelIndex index;
1021     if (bestBelow > -1)
1022         index = d->viewItems.at(bestBelow).index;
1023     else if (bestAbove > -1)
1024         index = d->viewItems.at(bestAbove).index;
1025 
1026     if (index.isValid()) {
1027         QItemSelectionModel::SelectionFlags flags = (d->selectionMode == SingleSelection
1028                                                      ? QItemSelectionModel::SelectionFlags(
1029                                                          QItemSelectionModel::ClearAndSelect
1030                                                          |d->selectionBehaviorFlags())
1031                                                      : QItemSelectionModel::SelectionFlags(
1032                                                          QItemSelectionModel::NoUpdate));
1033         selectionModel()->setCurrentIndex(index, flags);
1034     }
1035 }
1036 
1037 /*!
1038   Returns the rectangle on the viewport occupied by the item at \a index.
1039   If the index is not visible or explicitly hidden, the returned rectangle is invalid.
1040 */
visualRect(const QModelIndex & index) const1041 QRect QTreeView::visualRect(const QModelIndex &index) const
1042 {
1043     Q_D(const QTreeView);
1044 
1045     if (!d->isIndexValid(index) || isIndexHidden(index))
1046         return QRect();
1047 
1048     d->executePostedLayout();
1049 
1050     int vi = d->viewIndex(index);
1051     if (vi < 0)
1052         return QRect();
1053 
1054     bool spanning = d->viewItems.at(vi).spanning;
1055 
1056     // if we have a spanning item, make the selection stretch from left to right
1057     int x = (spanning ? 0 : columnViewportPosition(index.column()));
1058     int w = (spanning ? d->header->length() : columnWidth(index.column()));
1059     // handle indentation
1060     if (index.column() == 0) {
1061         int i = d->indentationForItem(vi);
1062         w -= i;
1063         if (!isRightToLeft())
1064             x += i;
1065     }
1066 
1067     int y = d->coordinateForItem(vi);
1068     int h = d->itemHeight(vi);
1069 
1070     return QRect(x, y, w, h);
1071 }
1072 
1073 /*!
1074     Scroll the contents of the tree view until the given model item
1075     \a index is visible. The \a hint parameter specifies more
1076     precisely where the item should be located after the
1077     operation.
1078     If any of the parents of the model item are collapsed, they will
1079     be expanded to ensure that the model item is visible.
1080 */
scrollTo(const QModelIndex & index,ScrollHint hint)1081 void QTreeView::scrollTo(const QModelIndex &index, ScrollHint hint)
1082 {
1083     Q_D(QTreeView);
1084 
1085     if (!d->isIndexValid(index))
1086         return;
1087 
1088     d->executePostedLayout();
1089     d->updateScrollBars();
1090 
1091     // Expand all parents if the parent(s) of the node are not expanded.
1092     QModelIndex parent = index.parent();
1093     while (parent.isValid() && state() == NoState && d->itemsExpandable) {
1094         if (!isExpanded(parent))
1095             expand(parent);
1096         parent = d->model->parent(parent);
1097     }
1098 
1099     int item = d->viewIndex(index);
1100     if (item < 0)
1101         return;
1102 
1103     QRect area = d->viewport->rect();
1104 
1105     // vertical
1106     if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
1107         int top = verticalScrollBar()->value();
1108         int bottom = top + verticalScrollBar()->pageStep();
1109         if (hint == EnsureVisible && item >= top && item < bottom) {
1110             // nothing to do
1111         } else if (hint == PositionAtTop || (hint == EnsureVisible && item < top)) {
1112             verticalScrollBar()->setValue(item);
1113         } else { // PositionAtBottom or PositionAtCenter
1114             const int currentItemHeight = d->itemHeight(item);
1115             int y = (hint == PositionAtCenter
1116                  //we center on the current item with a preference to the top item (ie. -1)
1117                      ? area.height() / 2 + currentItemHeight - 1
1118                  //otherwise we simply take the whole space
1119                      : area.height());
1120             if (y > currentItemHeight) {
1121                 while (item >= 0) {
1122                     y -= d->itemHeight(item);
1123                     if (y < 0) { //there is no more space left
1124                         item++;
1125                         break;
1126                     }
1127                     item--;
1128                 }
1129             }
1130             verticalScrollBar()->setValue(item);
1131         }
1132     } else { // ScrollPerPixel
1133         QRect rect(columnViewportPosition(index.column()),
1134                    d->coordinateForItem(item), // ### slow for items outside the view
1135                    columnWidth(index.column()),
1136                    d->itemHeight(item));
1137 
1138         if (rect.isEmpty()) {
1139             // nothing to do
1140         } else if (hint == EnsureVisible && area.contains(rect)) {
1141             d->viewport->update(rect);
1142             // nothing to do
1143         } else {
1144             bool above = (hint == EnsureVisible
1145                         && (rect.top() < area.top()
1146                             || area.height() < rect.height()));
1147             bool below = (hint == EnsureVisible
1148                         && rect.bottom() > area.bottom()
1149                         && rect.height() < area.height());
1150 
1151             int verticalValue = verticalScrollBar()->value();
1152             if (hint == PositionAtTop || above)
1153                 verticalValue += rect.top();
1154             else if (hint == PositionAtBottom || below)
1155                 verticalValue += rect.bottom() - area.height();
1156             else if (hint == PositionAtCenter)
1157                 verticalValue += rect.top() - ((area.height() - rect.height()) / 2);
1158             verticalScrollBar()->setValue(verticalValue);
1159         }
1160     }
1161     // horizontal
1162     int viewportWidth = d->viewport->width();
1163     int horizontalOffset = d->header->offset();
1164     int horizontalPosition = d->header->sectionPosition(index.column());
1165     int cellWidth = d->header->sectionSize(index.column());
1166 
1167     if (hint == PositionAtCenter) {
1168         horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2));
1169     } else {
1170         if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth)
1171             horizontalScrollBar()->setValue(horizontalPosition);
1172         else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth)
1173             horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth);
1174     }
1175 }
1176 
1177 /*!
1178   \reimp
1179 */
timerEvent(QTimerEvent * event)1180 void QTreeView::timerEvent(QTimerEvent *event)
1181 {
1182     Q_D(QTreeView);
1183     if (event->timerId() == d->columnResizeTimerID) {
1184         updateGeometries();
1185         killTimer(d->columnResizeTimerID);
1186         d->columnResizeTimerID = 0;
1187         QRect rect;
1188         int viewportHeight = d->viewport->height();
1189         int viewportWidth = d->viewport->width();
1190         for (int i = d->columnsToUpdate.size() - 1; i >= 0; --i) {
1191             int column = d->columnsToUpdate.at(i);
1192             int x = columnViewportPosition(column);
1193             if (isRightToLeft())
1194                 rect |= QRect(0, 0, x + columnWidth(column), viewportHeight);
1195             else
1196                 rect |= QRect(x, 0, viewportWidth - x, viewportHeight);
1197         }
1198         d->viewport->update(rect.normalized());
1199         d->columnsToUpdate.clear();
1200     } else if (event->timerId() == d->openTimer.timerId()) {
1201         QPoint pos = d->viewport->mapFromGlobal(QCursor::pos());
1202         if (state() == QAbstractItemView::DraggingState
1203             && d->viewport->rect().contains(pos)) {
1204             QModelIndex index = indexAt(pos);
1205             setExpanded(index, !isExpanded(index));
1206         }
1207         d->openTimer.stop();
1208     }
1209 
1210     QAbstractItemView::timerEvent(event);
1211 }
1212 
1213 /*!
1214   \reimp
1215 */
1216 #ifndef QT_NO_DRAGANDDROP
dragMoveEvent(QDragMoveEvent * event)1217 void QTreeView::dragMoveEvent(QDragMoveEvent *event)
1218 {
1219     Q_D(QTreeView);
1220     if (d->autoExpandDelay >= 0)
1221         d->openTimer.start(d->autoExpandDelay, this);
1222     QAbstractItemView::dragMoveEvent(event);
1223 }
1224 #endif
1225 
1226 /*!
1227   \reimp
1228 */
viewportEvent(QEvent * event)1229 bool QTreeView::viewportEvent(QEvent *event)
1230 {
1231     Q_D(QTreeView);
1232     switch (event->type()) {
1233     case QEvent::HoverEnter:
1234     case QEvent::HoverLeave:
1235     case QEvent::HoverMove: {
1236         QHoverEvent *he = static_cast<QHoverEvent*>(event);
1237         int oldBranch = d->hoverBranch;
1238         d->hoverBranch = d->itemDecorationAt(he->pos());
1239         QModelIndex newIndex = indexAt(he->pos());
1240         if (d->hover != newIndex || d->hoverBranch != oldBranch) {
1241             // Update the whole hovered over row. No need to update the old hovered
1242             // row, that is taken care in superclass hover handling.
1243             QRect rect = visualRect(newIndex);
1244             rect.setX(0);
1245             rect.setWidth(viewport()->width());
1246             viewport()->update(rect);
1247         }
1248         break; }
1249     default:
1250         break;
1251     }
1252     return QAbstractItemView::viewportEvent(event);
1253 }
1254 
1255 /*!
1256   \reimp
1257 */
paintEvent(QPaintEvent * event)1258 void QTreeView::paintEvent(QPaintEvent *event)
1259 {
1260     Q_D(QTreeView);
1261     d->executePostedLayout();
1262     QPainter painter(viewport());
1263 #ifndef QT_NO_ANIMATION
1264     if (d->isAnimating()) {
1265         drawTree(&painter, event->region() - d->animatedOperation.rect());
1266         d->drawAnimatedOperation(&painter);
1267     } else
1268 #endif //QT_NO_ANIMATION
1269     {
1270         drawTree(&painter, event->region());
1271 #ifndef QT_NO_DRAGANDDROP
1272         d->paintDropIndicator(&painter);
1273 #endif
1274     }
1275 }
1276 
paintAlternatingRowColors(QPainter * painter,QStyleOptionViewItemV4 * option,int y,int bottom) const1277 void QTreeViewPrivate::paintAlternatingRowColors(QPainter *painter, QStyleOptionViewItemV4 *option, int y, int bottom) const
1278 {
1279     Q_Q(const QTreeView);
1280     if (!alternatingColors || !q->style()->styleHint(QStyle::SH_ItemView_PaintAlternatingRowColorsForEmptyArea, option, q))
1281         return;
1282     int rowHeight = defaultItemHeight;
1283     if (rowHeight <= 0) {
1284         rowHeight = itemDelegate->sizeHint(*option, QModelIndex()).height();
1285         if (rowHeight <= 0)
1286             return;
1287     }
1288     while (y <= bottom) {
1289         option->rect.setRect(0, y, viewport->width(), rowHeight);
1290         if (current & 1) {
1291             option->features |= QStyleOptionViewItemV2::Alternate;
1292         } else {
1293             option->features &= ~QStyleOptionViewItemV2::Alternate;
1294         }
1295         ++current;
1296         q->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, option, painter, q);
1297         y += rowHeight;
1298     }
1299 }
1300 
expandOrCollapseItemAtPos(const QPoint & pos)1301 bool QTreeViewPrivate::expandOrCollapseItemAtPos(const QPoint &pos)
1302 {
1303     Q_Q(QTreeView);
1304     // we want to handle mousePress in EditingState (persistent editors)
1305     if ((state != QAbstractItemView::NoState
1306 		&& state != QAbstractItemView::EditingState)
1307 		|| !viewport->rect().contains(pos))
1308         return true;
1309 
1310     int i = itemDecorationAt(pos);
1311     if ((i != -1) && itemsExpandable && hasVisibleChildren(viewItems.at(i).index)) {
1312         if (viewItems.at(i).expanded)
1313             collapse(i, true);
1314         else
1315             expand(i, true);
1316         if (!isAnimating()) {
1317             q->updateGeometries();
1318             viewport->update();
1319         }
1320         return true;
1321     }
1322     return false;
1323 }
1324 
_q_modelDestroyed()1325 void QTreeViewPrivate::_q_modelDestroyed()
1326 {
1327     //we need to clear that list because it contais QModelIndex to
1328     //the model currently being destroyed
1329     viewItems.clear();
1330     QAbstractItemViewPrivate::_q_modelDestroyed();
1331 }
1332 
1333 /*!
1334   \reimp
1335 
1336   We have a QTreeView way of knowing what elements are on the viewport
1337 */
draggablePaintPairs(const QModelIndexList & indexes,QRect * r) const1338 QItemViewPaintPairs QTreeViewPrivate::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const
1339 {
1340     Q_ASSERT(r);
1341     Q_Q(const QTreeView);
1342     if (spanningIndexes.isEmpty())
1343         return QAbstractItemViewPrivate::draggablePaintPairs(indexes, r);
1344     QModelIndexList list;
1345     foreach (const QModelIndex &idx, indexes) {
1346         if (idx.column() > 0 && q->isFirstColumnSpanned(idx.row(), idx.parent()))
1347             continue;
1348         list << idx;
1349     }
1350     return QAbstractItemViewPrivate::draggablePaintPairs(list, r);
1351 }
1352 
adjustViewOptionsForIndex(QStyleOptionViewItemV4 * option,const QModelIndex & current) const1353 void QTreeViewPrivate::adjustViewOptionsForIndex(QStyleOptionViewItemV4 *option, const QModelIndex &current) const
1354 {
1355     const int row = viewIndex(current); // get the index in viewItems[]
1356     option->state = option->state | (viewItems.at(row).expanded ? QStyle::State_Open : QStyle::State_None)
1357                                   | (viewItems.at(row).hasChildren ? QStyle::State_Children : QStyle::State_None)
1358                                   | (viewItems.at(row).hasMoreSiblings ? QStyle::State_Sibling : QStyle::State_None);
1359 
1360     option->showDecorationSelected = (selectionBehavior & QTreeView::SelectRows)
1361                                      || option->showDecorationSelected;
1362 
1363     QVector<int> logicalIndices; // index = visual index of visible columns only. data = logical index.
1364     QVector<QStyleOptionViewItemV4::ViewItemPosition> viewItemPosList; // vector of left/middle/end for each logicalIndex, visible columns only.
1365     const bool spanning = viewItems.at(row).spanning;
1366     const int left = (spanning ? header->visualIndex(0) : 0);
1367     const int right = (spanning ? header->visualIndex(0) : header->count() - 1 );
1368     calcLogicalIndices(&logicalIndices, &viewItemPosList, left, right);
1369 
1370     const int visualIndex = logicalIndices.indexOf(current.column());
1371     option->viewItemPosition = viewItemPosList.at(visualIndex);
1372 }
1373 
1374 
1375 /*!
1376   \since 4.2
1377   Draws the part of the tree intersecting the given \a region using the specified
1378   \a painter.
1379 
1380   \sa paintEvent()
1381 */
drawTree(QPainter * painter,const QRegion & region) const1382 void QTreeView::drawTree(QPainter *painter, const QRegion &region) const
1383 {
1384     Q_D(const QTreeView);
1385     const QVector<QTreeViewItem> viewItems = d->viewItems;
1386 
1387     QStyleOptionViewItemV4 option = d->viewOptionsV4();
1388     const QStyle::State state = option.state;
1389     d->current = 0;
1390 
1391     if (viewItems.count() == 0 || d->header->count() == 0 || !d->itemDelegate) {
1392         d->paintAlternatingRowColors(painter, &option, 0, region.boundingRect().bottom()+1);
1393         return;
1394     }
1395 
1396     int firstVisibleItemOffset = 0;
1397     const int firstVisibleItem = d->firstVisibleItem(&firstVisibleItemOffset);
1398     if (firstVisibleItem < 0) {
1399         d->paintAlternatingRowColors(painter, &option, 0, region.boundingRect().bottom()+1);
1400         return;
1401     }
1402 
1403     const int viewportWidth = d->viewport->width();
1404 
1405     QPoint hoverPos = d->viewport->mapFromGlobal(QCursor::pos());
1406     d->hoverBranch = d->itemDecorationAt(hoverPos);
1407 
1408     QVector<QRect> rects = region.rects();
1409     QVector<int> drawn;
1410     bool multipleRects = (rects.size() > 1);
1411     for (int a = 0; a < rects.size(); ++a) {
1412         const QRect area = (multipleRects
1413                             ? QRect(0, rects.at(a).y(), viewportWidth, rects.at(a).height())
1414                             : rects.at(a));
1415         d->leftAndRight = d->startAndEndColumns(area);
1416 
1417         int i = firstVisibleItem; // the first item at the top of the viewport
1418         int y = firstVisibleItemOffset; // we may only see part of the first item
1419 
1420         // start at the top of the viewport  and iterate down to the update area
1421         for (; i < viewItems.count(); ++i) {
1422             const int itemHeight = d->itemHeight(i);
1423             if (y + itemHeight > area.top())
1424                 break;
1425             y += itemHeight;
1426         }
1427 
1428         // paint the visible rows
1429         for (; i < viewItems.count() && y <= area.bottom(); ++i) {
1430             const int itemHeight = d->itemHeight(i);
1431             option.rect.setRect(0, y, viewportWidth, itemHeight);
1432             option.state = state | (viewItems.at(i).expanded ? QStyle::State_Open : QStyle::State_None)
1433                                  | (viewItems.at(i).hasChildren ? QStyle::State_Children : QStyle::State_None)
1434                                  | (viewItems.at(i).hasMoreSiblings ? QStyle::State_Sibling : QStyle::State_None);
1435             d->current = i;
1436             d->spanning = viewItems.at(i).spanning;
1437             if (!multipleRects || !drawn.contains(i)) {
1438                 drawRow(painter, option, viewItems.at(i).index);
1439                 if (multipleRects)   // even if the rect only intersects the item,
1440                     drawn.append(i); // the entire item will be painted
1441             }
1442             y += itemHeight;
1443         }
1444 
1445         if (y <= area.bottom()) {
1446             d->current = i;
1447             d->paintAlternatingRowColors(painter, &option, y, area.bottom());
1448         }
1449     }
1450 }
1451 
1452 /// ### move to QObject :)
ancestorOf(QObject * widget,QObject * other)1453 static inline bool ancestorOf(QObject *widget, QObject *other)
1454 {
1455     for (QObject *parent = other; parent != 0; parent = parent->parent()) {
1456         if (parent == widget)
1457             return true;
1458     }
1459     return false;
1460 }
1461 
calcLogicalIndices(QVector<int> * logicalIndices,QVector<QStyleOptionViewItemV4::ViewItemPosition> * itemPositions,int left,int right) const1462 void QTreeViewPrivate::calcLogicalIndices(QVector<int> *logicalIndices, QVector<QStyleOptionViewItemV4::ViewItemPosition> *itemPositions, int left, int right) const
1463 {
1464     const int columnCount = header->count();
1465     /* 'left' and 'right' are the left-most and right-most visible visual indices.
1466        Compute the first visible logical indices before and after the left and right.
1467        We will use these values to determine the QStyleOptionViewItemV4::viewItemPosition. */
1468     int logicalIndexBeforeLeft = -1, logicalIndexAfterRight = -1;
1469     for (int visualIndex = left - 1; visualIndex >= 0; --visualIndex) {
1470         int logicalIndex = header->logicalIndex(visualIndex);
1471         if (!header->isSectionHidden(logicalIndex)) {
1472             logicalIndexBeforeLeft = logicalIndex;
1473             break;
1474         }
1475     }
1476 
1477     for (int visualIndex = left; visualIndex < columnCount; ++visualIndex) {
1478         int logicalIndex = header->logicalIndex(visualIndex);
1479         if (!header->isSectionHidden(logicalIndex)) {
1480             if (visualIndex > right) {
1481                 logicalIndexAfterRight = logicalIndex;
1482                 break;
1483             }
1484             logicalIndices->append(logicalIndex);
1485         }
1486     }
1487 
1488     itemPositions->resize(logicalIndices->count());
1489     for (int currentLogicalSection = 0; currentLogicalSection < logicalIndices->count(); ++currentLogicalSection) {
1490         const int headerSection = logicalIndices->at(currentLogicalSection);
1491         // determine the viewItemPosition depending on the position of column 0
1492         int nextLogicalSection = currentLogicalSection + 1 >= logicalIndices->count()
1493                                  ? logicalIndexAfterRight
1494                                  : logicalIndices->at(currentLogicalSection + 1);
1495         int prevLogicalSection = currentLogicalSection - 1 < 0
1496                                  ? logicalIndexBeforeLeft
1497                                  : logicalIndices->at(currentLogicalSection - 1);
1498         QStyleOptionViewItemV4::ViewItemPosition pos;
1499         if (columnCount == 1 || (nextLogicalSection == 0 && prevLogicalSection == -1)
1500             || (headerSection == 0 && nextLogicalSection == -1) || spanning)
1501             pos = QStyleOptionViewItemV4::OnlyOne;
1502         else if (headerSection == 0 || (nextLogicalSection != 0 && prevLogicalSection == -1))
1503             pos = QStyleOptionViewItemV4::Beginning;
1504         else if (nextLogicalSection == 0 || nextLogicalSection == -1)
1505             pos = QStyleOptionViewItemV4::End;
1506         else
1507             pos = QStyleOptionViewItemV4::Middle;
1508         (*itemPositions)[currentLogicalSection] = pos;
1509     }
1510 }
1511 
1512 
1513 /*!
1514     Draws the row in the tree view that contains the model item \a index,
1515     using the \a painter given. The \a option control how the item is
1516     displayed.
1517 
1518     \sa setAlternatingRowColors()
1519 */
drawRow(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const1520 void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option,
1521                         const QModelIndex &index) const
1522 {
1523     Q_D(const QTreeView);
1524     QStyleOptionViewItemV4 opt = option;
1525     const QPoint offset = d->scrollDelayOffset;
1526     const int y = option.rect.y() + offset.y();
1527     const QModelIndex parent = index.parent();
1528     const QHeaderView *header = d->header;
1529     const QModelIndex current = currentIndex();
1530     const QModelIndex hover = d->hover;
1531     const bool reverse = isRightToLeft();
1532     const QStyle::State state = opt.state;
1533     const bool spanning = d->spanning;
1534     const int left = (spanning ? header->visualIndex(0) : d->leftAndRight.first);
1535     const int right = (spanning ? header->visualIndex(0) : d->leftAndRight.second);
1536     const bool alternate = d->alternatingColors;
1537     const bool enabled = (state & QStyle::State_Enabled) != 0;
1538     const bool allColumnsShowFocus = d->allColumnsShowFocus;
1539 
1540 
1541     // when the row contains an index widget which has focus,
1542     // we want to paint the entire row as active
1543     bool indexWidgetHasFocus = false;
1544     if ((current.row() == index.row()) && !d->editorIndexHash.isEmpty()) {
1545         const int r = index.row();
1546         QWidget *fw = QApplication::focusWidget();
1547         for (int c = 0; c < header->count(); ++c) {
1548             QModelIndex idx = d->model->index(r, c, parent);
1549             if (QWidget *editor = indexWidget(idx)) {
1550                 if (ancestorOf(editor, fw)) {
1551                     indexWidgetHasFocus = true;
1552                     break;
1553                 }
1554             }
1555         }
1556     }
1557 
1558     const bool widgetHasFocus = hasFocus();
1559     bool currentRowHasFocus = false;
1560     if (allColumnsShowFocus && widgetHasFocus && current.isValid()) {
1561         // check if the focus index is before or after the visible columns
1562         const int r = index.row();
1563         for (int c = 0; c < left && !currentRowHasFocus; ++c) {
1564             QModelIndex idx = d->model->index(r, c, parent);
1565             currentRowHasFocus = (idx == current);
1566         }
1567         QModelIndex parent = d->model->parent(index);
1568         for (int c = right; c < header->count() && !currentRowHasFocus; ++c) {
1569             currentRowHasFocus = (d->model->index(r, c, parent) == current);
1570         }
1571     }
1572 
1573     // ### special case: treeviews with multiple columns draw
1574     // the selections differently than with only one column
1575     opt.showDecorationSelected = (d->selectionBehavior & SelectRows)
1576                                  || option.showDecorationSelected;
1577 
1578     int width, height = option.rect.height();
1579     int position;
1580     QModelIndex modelIndex;
1581     const bool hoverRow = selectionBehavior() == QAbstractItemView::SelectRows
1582                   && index.parent() == hover.parent()
1583                   && index.row() == hover.row();
1584 
1585     QVector<int> logicalIndices;
1586     QVector<QStyleOptionViewItemV4::ViewItemPosition> viewItemPosList; // vector of left/middle/end for each logicalIndex
1587     d->calcLogicalIndices(&logicalIndices, &viewItemPosList, left, right);
1588 
1589     for (int currentLogicalSection = 0; currentLogicalSection < logicalIndices.count(); ++currentLogicalSection) {
1590         int headerSection = logicalIndices.at(currentLogicalSection);
1591         position = columnViewportPosition(headerSection) + offset.x();
1592         width = header->sectionSize(headerSection);
1593 
1594         if (spanning) {
1595             int lastSection = header->logicalIndex(header->count() - 1);
1596             if (!reverse) {
1597                 width = columnViewportPosition(lastSection) + header->sectionSize(lastSection) - position;
1598             } else {
1599                 width += position - columnViewportPosition(lastSection);
1600                 position = columnViewportPosition(lastSection);
1601             }
1602         }
1603 
1604         modelIndex = d->model->index(index.row(), headerSection, parent);
1605         if (!modelIndex.isValid())
1606             continue;
1607         opt.state = state;
1608 
1609         opt.viewItemPosition = viewItemPosList.at(currentLogicalSection);
1610 
1611         // fake activeness when row editor has focus
1612         if (indexWidgetHasFocus)
1613             opt.state |= QStyle::State_Active;
1614 
1615         if (d->selectionModel->isSelected(modelIndex))
1616             opt.state |= QStyle::State_Selected;
1617         if (widgetHasFocus && (current == modelIndex)) {
1618             if (allColumnsShowFocus)
1619                 currentRowHasFocus = true;
1620             else
1621                 opt.state |= QStyle::State_HasFocus;
1622         }
1623         if ((hoverRow || modelIndex == hover)
1624             && (option.showDecorationSelected || (d->hoverBranch == -1)))
1625             opt.state |= QStyle::State_MouseOver;
1626         else
1627             opt.state &= ~QStyle::State_MouseOver;
1628 
1629         if (enabled) {
1630             QPalette::ColorGroup cg;
1631             if ((d->model->flags(modelIndex) & Qt::ItemIsEnabled) == 0) {
1632                 opt.state &= ~QStyle::State_Enabled;
1633                 cg = QPalette::Disabled;
1634             } else if (opt.state & QStyle::State_Active) {
1635                 cg = QPalette::Active;
1636             } else {
1637                 cg = QPalette::Inactive;
1638             }
1639             opt.palette.setCurrentColorGroup(cg);
1640         }
1641 
1642         if (alternate) {
1643             if (d->current & 1) {
1644                 opt.features |= QStyleOptionViewItemV2::Alternate;
1645             } else {
1646                 opt.features &= ~QStyleOptionViewItemV2::Alternate;
1647             }
1648         }
1649 
1650         /* Prior to Qt 4.3, the background of the branch (in selected state and
1651            alternate row color was provided by the view. For backward compatibility,
1652            this is now delegated to the style using PE_PanelViewItemRow which
1653            does the appropriate fill */
1654         if (headerSection == 0) {
1655             const int i = d->indentationForItem(d->current);
1656             QRect branches(reverse ? position + width - i : position, y, i, height);
1657             const bool setClipRect = branches.width() > width;
1658             if (setClipRect) {
1659                 painter->save();
1660                 painter->setClipRect(QRect(position, y, width, height));
1661             }
1662             // draw background for the branch (selection + alternate row)
1663             opt.rect = branches;
1664             style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);
1665 
1666             // draw background of the item (only alternate row). rest of the background
1667             // is provided by the delegate
1668             QStyle::State oldState = opt.state;
1669             opt.state &= ~QStyle::State_Selected;
1670             opt.rect.setRect(reverse ? position : i + position, y, width - i, height);
1671             style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);
1672             opt.state = oldState;
1673 
1674             drawBranches(painter, branches, index);
1675             if (setClipRect)
1676                 painter->restore();
1677         } else {
1678             QStyle::State oldState = opt.state;
1679             opt.state &= ~QStyle::State_Selected;
1680             opt.rect.setRect(position, y, width, height);
1681             style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);
1682             opt.state = oldState;
1683         }
1684 
1685         d->delegateForIndex(modelIndex)->paint(painter, opt, modelIndex);
1686     }
1687 
1688     if (currentRowHasFocus) {
1689         QStyleOptionFocusRect o;
1690         o.QStyleOption::operator=(option);
1691         o.state |= QStyle::State_KeyboardFocusChange;
1692         QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
1693                                   ? QPalette::Normal : QPalette::Disabled;
1694         o.backgroundColor = option.palette.color(cg, d->selectionModel->isSelected(index)
1695                                                  ? QPalette::Highlight : QPalette::Background);
1696         int x = 0;
1697         if (!option.showDecorationSelected)
1698             x = header->sectionPosition(0) + d->indentationForItem(d->current);
1699 	QRect focusRect(x - header->offset(), y, header->length() - x, height);
1700         o.rect = style()->visualRect(layoutDirection(), d->viewport->rect(), focusRect);
1701         style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter);
1702         // if we show focus on all columns and the first section is moved,
1703         // we have to split the focus rect into two rects
1704         if (allColumnsShowFocus && !option.showDecorationSelected
1705             && header->sectionsMoved() && (header->visualIndex(0) != 0)) {
1706 	    QRect sectionRect(0, y, header->sectionPosition(0), height);
1707             o.rect = style()->visualRect(layoutDirection(), d->viewport->rect(), sectionRect);
1708             style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter);
1709         }
1710     }
1711 }
1712 
1713 /*!
1714   Draws the branches in the tree view on the same row as the model item
1715   \a index, using the \a painter given. The branches are drawn in the
1716   rectangle specified by \a rect.
1717 */
drawBranches(QPainter * painter,const QRect & rect,const QModelIndex & index) const1718 void QTreeView::drawBranches(QPainter *painter, const QRect &rect,
1719                              const QModelIndex &index) const
1720 {
1721     Q_D(const QTreeView);
1722     const bool reverse = isRightToLeft();
1723     const int indent = d->indent;
1724     const int outer = d->rootDecoration ? 0 : 1;
1725     const int item = d->current;
1726     const QTreeViewItem &viewItem = d->viewItems.at(item);
1727     int level = viewItem.level;
1728     QRect primitive(reverse ? rect.left() : rect.right() + 1, rect.top(), indent, rect.height());
1729 
1730     QModelIndex parent = index.parent();
1731     QModelIndex current = parent;
1732     QModelIndex ancestor = current.parent();
1733 
1734     QStyleOptionViewItemV2 opt = viewOptions();
1735     QStyle::State extraFlags = QStyle::State_None;
1736     if (isEnabled())
1737         extraFlags |= QStyle::State_Enabled;
1738     if (window()->isActiveWindow())
1739         extraFlags |= QStyle::State_Active;
1740     QPoint oldBO = painter->brushOrigin();
1741     if (verticalScrollMode() == QAbstractItemView::ScrollPerPixel)
1742         painter->setBrushOrigin(QPoint(0, verticalOffset()));
1743 
1744     if (d->alternatingColors) {
1745         if (d->current & 1) {
1746             opt.features |= QStyleOptionViewItemV2::Alternate;
1747         } else {
1748             opt.features &= ~QStyleOptionViewItemV2::Alternate;
1749         }
1750     }
1751 
1752     // When hovering over a row, pass State_Hover for painting the branch
1753     // indicators if it has the decoration (aka branch) selected.
1754     bool hoverRow = selectionBehavior() == QAbstractItemView::SelectRows
1755                     && opt.showDecorationSelected
1756                     && index.parent() == d->hover.parent()
1757                     && index.row() == d->hover.row();
1758 
1759     if (d->selectionModel->isSelected(index))
1760         extraFlags |= QStyle::State_Selected;
1761 
1762     if (level >= outer) {
1763         // start with the innermost branch
1764         primitive.moveLeft(reverse ? primitive.left() : primitive.left() - indent);
1765         opt.rect = primitive;
1766 
1767         const bool expanded = viewItem.expanded;
1768         const bool children = viewItem.hasChildren;
1769         bool moreSiblings = viewItem.hasMoreSiblings;
1770 
1771         opt.state = QStyle::State_Item | extraFlags
1772                     | (moreSiblings ? QStyle::State_Sibling : QStyle::State_None)
1773                     | (children ? QStyle::State_Children : QStyle::State_None)
1774                     | (expanded ? QStyle::State_Open : QStyle::State_None);
1775         if (hoverRow || item == d->hoverBranch)
1776             opt.state |= QStyle::State_MouseOver;
1777         else
1778             opt.state &= ~QStyle::State_MouseOver;
1779         style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);
1780     }
1781     // then go out level by level
1782     for (--level; level >= outer; --level) { // we have already drawn the innermost branch
1783         primitive.moveLeft(reverse ? primitive.left() + indent : primitive.left() - indent);
1784         opt.rect = primitive;
1785         opt.state = extraFlags;
1786         bool moreSiblings = false;
1787         if (d->hiddenIndexes.isEmpty()) {
1788             moreSiblings = (d->model->rowCount(ancestor) - 1 > current.row());
1789         } else {
1790             int successor = item + viewItem.total + 1;
1791             while (successor < d->viewItems.size()
1792                    && d->viewItems.at(successor).level >= uint(level)) {
1793                 const QTreeViewItem &successorItem = d->viewItems.at(successor);
1794                 if (successorItem.level == uint(level)) {
1795                     moreSiblings = true;
1796                     break;
1797                 }
1798                 successor += successorItem.total + 1;
1799             }
1800         }
1801         if (moreSiblings)
1802             opt.state |= QStyle::State_Sibling;
1803         if (hoverRow || item == d->hoverBranch)
1804             opt.state |= QStyle::State_MouseOver;
1805         else
1806             opt.state &= ~QStyle::State_MouseOver;
1807         style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);
1808         current = ancestor;
1809         ancestor = current.parent();
1810     }
1811     painter->setBrushOrigin(oldBO);
1812 }
1813 
1814 /*!
1815   \reimp
1816 */
mousePressEvent(QMouseEvent * event)1817 void QTreeView::mousePressEvent(QMouseEvent *event)
1818 {
1819 	Q_D(QTreeView);
1820     bool handled = false;
1821     if (style()->styleHint(QStyle::SH_Q3ListViewExpand_SelectMouseType, 0, this) == QEvent::MouseButtonPress)
1822         handled = d->expandOrCollapseItemAtPos(event->pos());
1823 	if (!handled && d->itemDecorationAt(event->pos()) == -1)
1824         QAbstractItemView::mousePressEvent(event);
1825 }
1826 
1827 /*!
1828   \reimp
1829 */
mouseReleaseEvent(QMouseEvent * event)1830 void QTreeView::mouseReleaseEvent(QMouseEvent *event)
1831 {
1832     Q_D(QTreeView);
1833     if (d->itemDecorationAt(event->pos()) == -1) {
1834         QAbstractItemView::mouseReleaseEvent(event);
1835     } else {
1836         if (state() == QAbstractItemView::DragSelectingState)
1837             setState(QAbstractItemView::NoState);
1838         if (style()->styleHint(QStyle::SH_Q3ListViewExpand_SelectMouseType, 0, this) == QEvent::MouseButtonRelease)
1839             d->expandOrCollapseItemAtPos(event->pos());
1840     }
1841 }
1842 
1843 /*!
1844   \reimp
1845 */
mouseDoubleClickEvent(QMouseEvent * event)1846 void QTreeView::mouseDoubleClickEvent(QMouseEvent *event)
1847 {
1848     Q_D(QTreeView);
1849     if (state() != NoState || !d->viewport->rect().contains(event->pos()))
1850         return;
1851 
1852     int i = d->itemDecorationAt(event->pos());
1853     if (i == -1) {
1854         i = d->itemAtCoordinate(event->y());
1855         if (i == -1)
1856             return; // user clicked outside the items
1857 
1858         const QPersistentModelIndex firstColumnIndex = d->viewItems.at(i).index;
1859         const QPersistentModelIndex persistent = indexAt(event->pos());
1860 
1861         if (d->pressedIndex != persistent) {
1862             mousePressEvent(event);
1863             return;
1864         }
1865 
1866         // signal handlers may change the model
1867         emit doubleClicked(persistent);
1868 
1869         if (!persistent.isValid())
1870             return;
1871 
1872         if (edit(persistent, DoubleClicked, event) || state() != NoState)
1873             return; // the double click triggered editing
1874 
1875         if (!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this))
1876             emit activated(persistent);
1877 
1878         d->executePostedLayout(); // we need to make sure viewItems is updated
1879         if (d->itemsExpandable
1880             && d->expandsOnDoubleClick
1881             && d->hasVisibleChildren(persistent)) {
1882             if (!((i < d->viewItems.count()) && (d->viewItems.at(i).index == firstColumnIndex))) {
1883                 // find the new index of the item
1884                 for (i = 0; i < d->viewItems.count(); ++i) {
1885                     if (d->viewItems.at(i).index == firstColumnIndex)
1886                         break;
1887                 }
1888                 if (i == d->viewItems.count())
1889                     return;
1890             }
1891             if (d->viewItems.at(i).expanded)
1892                 d->collapse(i, true);
1893             else
1894                 d->expand(i, true);
1895             updateGeometries();
1896             viewport()->update();
1897         }
1898     }
1899 }
1900 
1901 /*!
1902   \reimp
1903 */
mouseMoveEvent(QMouseEvent * event)1904 void QTreeView::mouseMoveEvent(QMouseEvent *event)
1905 {
1906     Q_D(QTreeView);
1907     if (d->itemDecorationAt(event->pos()) == -1) // ### what about expanding/collapsing state ?
1908         QAbstractItemView::mouseMoveEvent(event);
1909 }
1910 
1911 /*!
1912   \reimp
1913 */
keyPressEvent(QKeyEvent * event)1914 void QTreeView::keyPressEvent(QKeyEvent *event)
1915 {
1916     Q_D(QTreeView);
1917     QModelIndex current = currentIndex();
1918     //this is the management of the expansion
1919     if (d->isIndexValid(current) && d->model && d->itemsExpandable) {
1920         switch (event->key()) {
1921         case Qt::Key_Asterisk: {
1922             QStack<QModelIndex> parents;
1923             parents.push(current);
1924                 while (!parents.isEmpty()) {
1925                     QModelIndex parent = parents.pop();
1926                     for (int row = 0; row < d->model->rowCount(parent); ++row) {
1927                         QModelIndex child = d->model->index(row, 0, parent);
1928                         if (!d->isIndexValid(child))
1929                             break;
1930                         parents.push(child);
1931                         expand(child);
1932                     }
1933                 }
1934                 expand(current);
1935             break; }
1936         case Qt::Key_Plus:
1937             expand(current);
1938             break;
1939         case Qt::Key_Minus:
1940             collapse(current);
1941             break;
1942         }
1943     }
1944 
1945     QAbstractItemView::keyPressEvent(event);
1946 }
1947 
1948 /*!
1949   \reimp
1950 */
indexAt(const QPoint & point) const1951 QModelIndex QTreeView::indexAt(const QPoint &point) const
1952 {
1953     Q_D(const QTreeView);
1954     d->executePostedLayout();
1955 
1956     int visualIndex = d->itemAtCoordinate(point.y());
1957     QModelIndex idx = d->modelIndex(visualIndex);
1958     if (!idx.isValid())
1959         return QModelIndex();
1960 
1961     if (d->viewItems.at(visualIndex).spanning)
1962         return idx;
1963 
1964     int column = d->columnAt(point.x());
1965     if (column == idx.column())
1966         return idx;
1967     if (column < 0)
1968         return QModelIndex();
1969     return idx.sibling(idx.row(), column);
1970 }
1971 
1972 /*!
1973   Returns the model index of the item above \a index.
1974 */
indexAbove(const QModelIndex & index) const1975 QModelIndex QTreeView::indexAbove(const QModelIndex &index) const
1976 {
1977     Q_D(const QTreeView);
1978     if (!d->isIndexValid(index))
1979         return QModelIndex();
1980     d->executePostedLayout();
1981     int i = d->viewIndex(index);
1982     if (--i < 0)
1983         return QModelIndex();
1984     return d->viewItems.at(i).index;
1985 }
1986 
1987 /*!
1988   Returns the model index of the item below \a index.
1989 */
indexBelow(const QModelIndex & index) const1990 QModelIndex QTreeView::indexBelow(const QModelIndex &index) const
1991 {
1992     Q_D(const QTreeView);
1993     if (!d->isIndexValid(index))
1994         return QModelIndex();
1995     d->executePostedLayout();
1996     int i = d->viewIndex(index);
1997     if (++i >= d->viewItems.count())
1998         return QModelIndex();
1999     return d->viewItems.at(i).index;
2000 }
2001 
2002 /*!
2003     \internal
2004 
2005     Lays out the items in the tree view.
2006 */
doItemsLayout()2007 void QTreeView::doItemsLayout()
2008 {
2009     Q_D(QTreeView);
2010     if (d->hasRemovedItems) {
2011         //clean the QSet that may contains old (and this invalid) indexes
2012         d->hasRemovedItems = false;
2013         QSet<QPersistentModelIndex>::iterator it = d->expandedIndexes.begin();
2014         while (it != d->expandedIndexes.constEnd()) {
2015             if (!it->isValid())
2016                 it = d->expandedIndexes.erase(it);
2017             else
2018                 ++it;
2019         }
2020         it = d->hiddenIndexes.begin();
2021         while (it != d->hiddenIndexes.constEnd()) {
2022             if (!it->isValid())
2023                 it = d->hiddenIndexes.erase(it);
2024             else
2025                 ++it;
2026         }
2027     }
2028     d->viewItems.clear(); // prepare for new layout
2029     QModelIndex parent = d->root;
2030     if (d->model->hasChildren(parent)) {
2031         d->layout(-1);
2032     }
2033     QAbstractItemView::doItemsLayout();
2034     d->header->doItemsLayout();
2035 }
2036 
2037 /*!
2038   \reimp
2039 */
reset()2040 void QTreeView::reset()
2041 {
2042     Q_D(QTreeView);
2043     d->expandedIndexes.clear();
2044     d->hiddenIndexes.clear();
2045     d->spanningIndexes.clear();
2046     d->viewItems.clear();
2047     QAbstractItemView::reset();
2048 }
2049 
2050 /*!
2051   Returns the horizontal offset of the items in the treeview.
2052 
2053   Note that the tree view uses the horizontal header section
2054   positions to determine the positions of columns in the view.
2055 
2056   \sa verticalOffset()
2057 */
horizontalOffset() const2058 int QTreeView::horizontalOffset() const
2059 {
2060     Q_D(const QTreeView);
2061     return d->header->offset();
2062 }
2063 
2064 /*!
2065   Returns the vertical offset of the items in the tree view.
2066 
2067   \sa horizontalOffset()
2068 */
verticalOffset() const2069 int QTreeView::verticalOffset() const
2070 {
2071     Q_D(const QTreeView);
2072     if (d->verticalScrollMode == QAbstractItemView::ScrollPerItem) {
2073         if (d->uniformRowHeights)
2074             return verticalScrollBar()->value() * d->defaultItemHeight;
2075         // If we are scrolling per item and have non-uniform row heights,
2076         // finding the vertical offset in pixels is going to be relatively slow.
2077         // ### find a faster way to do this
2078         d->executePostedLayout();
2079         int offset = 0;
2080         for (int i = 0; i < d->viewItems.count(); ++i) {
2081             if (i == verticalScrollBar()->value())
2082                 return offset;
2083             offset += d->itemHeight(i);
2084         }
2085         return 0;
2086     }
2087     // scroll per pixel
2088     return verticalScrollBar()->value();
2089 }
2090 
2091 /*!
2092     Move the cursor in the way described by \a cursorAction, using the
2093     information provided by the button \a modifiers.
2094 */
moveCursor(CursorAction cursorAction,Qt::KeyboardModifiers modifiers)2095 QModelIndex QTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
2096 {
2097     Q_D(QTreeView);
2098     Q_UNUSED(modifiers);
2099 
2100     d->executePostedLayout();
2101 
2102     QModelIndex current = currentIndex();
2103     if (!current.isValid()) {
2104         int i = d->below(-1);
2105         int c = 0;
2106         while (c < d->header->count() && d->header->isSectionHidden(c))
2107             ++c;
2108         if (i < d->viewItems.count() && c < d->header->count()) {
2109             return d->modelIndex(i, c);
2110         }
2111         return QModelIndex();
2112     }
2113     int vi = -1;
2114 #if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
2115     // Selection behavior is slightly different on the Mac.
2116     if (d->selectionMode == QAbstractItemView::ExtendedSelection
2117         && d->selectionModel
2118         && d->selectionModel->hasSelection()) {
2119 
2120         const bool moveUpDown = (cursorAction == MoveUp || cursorAction == MoveDown);
2121         const bool moveNextPrev = (cursorAction == MoveNext || cursorAction == MovePrevious);
2122         const bool contiguousSelection = moveUpDown && (modifiers & Qt::ShiftModifier);
2123 
2124         // Use the outermost index in the selection as the current index
2125         if (!contiguousSelection && (moveUpDown || moveNextPrev)) {
2126 
2127             // Find outermost index.
2128             const bool useTopIndex = (cursorAction == MoveUp || cursorAction == MovePrevious);
2129             int index = useTopIndex ? INT_MAX : INT_MIN;
2130             const QItemSelection selection = d->selectionModel->selection();
2131             for (int i = 0; i < selection.count(); ++i) {
2132                 const QItemSelectionRange &range = selection.at(i);
2133                 int candidate = d->viewIndex(useTopIndex ? range.topLeft() : range.bottomRight());
2134                 if (candidate >= 0)
2135                     index = useTopIndex ? qMin(index, candidate) : qMax(index, candidate);
2136             }
2137 
2138             if (index >= 0 && index < INT_MAX)
2139                 vi = index;
2140         }
2141     }
2142 #endif
2143     if (vi < 0)
2144         vi = qMax(0, d->viewIndex(current));
2145 
2146     if (isRightToLeft()) {
2147         if (cursorAction == MoveRight)
2148             cursorAction = MoveLeft;
2149         else if (cursorAction == MoveLeft)
2150             cursorAction = MoveRight;
2151     }
2152     switch (cursorAction) {
2153     case MoveNext:
2154     case MoveDown:
2155 #ifdef QT_KEYPAD_NAVIGATION
2156         if (vi == d->viewItems.count()-1 && QApplication::keypadNavigationEnabled())
2157             return d->model->index(0, current.column(), d->root);
2158 #endif
2159         return d->modelIndex(d->below(vi), current.column());
2160     case MovePrevious:
2161     case MoveUp:
2162 #ifdef QT_KEYPAD_NAVIGATION
2163         if (vi == 0 && QApplication::keypadNavigationEnabled())
2164             return d->modelIndex(d->viewItems.count() - 1, current.column());
2165 #endif
2166         return d->modelIndex(d->above(vi), current.column());
2167     case MoveLeft: {
2168         QScrollBar *sb = horizontalScrollBar();
2169         if (vi < d->viewItems.count() && d->viewItems.at(vi).expanded && d->itemsExpandable && sb->value() == sb->minimum()) {
2170             d->collapse(vi, true);
2171             d->moveCursorUpdatedView = true;
2172         } else {
2173             bool descend = style()->styleHint(QStyle::SH_ItemView_ArrowKeysNavigateIntoChildren, 0, this);
2174             if (descend) {
2175                 QModelIndex par = current.parent();
2176                 if (par.isValid() && par != rootIndex())
2177                     return par;
2178                 else
2179                     descend = false;
2180             }
2181             if (!descend) {
2182                 if (d->selectionBehavior == SelectItems || d->selectionBehavior == SelectColumns) {
2183                     int visualColumn = d->header->visualIndex(current.column()) - 1;
2184                     while (visualColumn >= 0 && isColumnHidden(d->header->logicalIndex(visualColumn)))
2185                         visualColumn--;
2186                     int newColumn = d->header->logicalIndex(visualColumn);
2187                     QModelIndex next = current.sibling(current.row(), newColumn);
2188                     if (next.isValid())
2189                         return next;
2190                 }
2191 
2192                 int oldValue = sb->value();
2193                 sb->setValue(sb->value() - sb->singleStep());
2194                 if (oldValue != sb->value())
2195                     d->moveCursorUpdatedView = true;
2196             }
2197 
2198         }
2199         updateGeometries();
2200         viewport()->update();
2201         break;
2202     }
2203     case MoveRight:
2204         if (vi < d->viewItems.count() && !d->viewItems.at(vi).expanded && d->itemsExpandable
2205             && d->hasVisibleChildren(d->viewItems.at(vi).index)) {
2206             d->expand(vi, true);
2207             d->moveCursorUpdatedView = true;
2208         } else {
2209             bool descend = style()->styleHint(QStyle::SH_ItemView_ArrowKeysNavigateIntoChildren, 0, this);
2210             if (descend) {
2211                 QModelIndex idx = d->modelIndex(d->below(vi));
2212                 if (idx.parent() == current)
2213                     return idx;
2214                 else
2215                     descend = false;
2216             }
2217             if (!descend) {
2218                 if (d->selectionBehavior == SelectItems || d->selectionBehavior == SelectColumns) {
2219                     int visualColumn = d->header->visualIndex(current.column()) + 1;
2220                     while (visualColumn < d->model->columnCount(current.parent()) && isColumnHidden(d->header->logicalIndex(visualColumn)))
2221                         visualColumn++;
2222 
2223                     QModelIndex next = current.sibling(current.row(), visualColumn);
2224                     if (next.isValid())
2225                         return next;
2226                 }
2227 
2228                 //last restort: we change the scrollbar value
2229                 QScrollBar *sb = horizontalScrollBar();
2230                 int oldValue = sb->value();
2231                 sb->setValue(sb->value() + sb->singleStep());
2232                 if (oldValue != sb->value())
2233                     d->moveCursorUpdatedView = true;
2234             }
2235         }
2236         updateGeometries();
2237         viewport()->update();
2238         break;
2239     case MovePageUp:
2240         return d->modelIndex(d->pageUp(vi), current.column());
2241     case MovePageDown:
2242         return d->modelIndex(d->pageDown(vi), current.column());
2243     case MoveHome:
2244         return d->model->index(0, current.column(), d->root);
2245     case MoveEnd:
2246         return d->modelIndex(d->viewItems.count() - 1, current.column());
2247     }
2248     return current;
2249 }
2250 
2251 /*!
2252   Applies the selection \a command to the items in or touched by the
2253   rectangle, \a rect.
2254 
2255   \sa selectionCommand()
2256 */
setSelection(const QRect & rect,QItemSelectionModel::SelectionFlags command)2257 void QTreeView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
2258 {
2259     Q_D(QTreeView);
2260     if (!selectionModel() || rect.isNull())
2261         return;
2262 
2263     d->executePostedLayout();
2264     QPoint tl(isRightToLeft() ? qMax(rect.left(), rect.right())
2265               : qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom()));
2266     QPoint br(isRightToLeft() ? qMin(rect.left(), rect.right()) :
2267               qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom()));
2268     QModelIndex topLeft = indexAt(tl);
2269     QModelIndex bottomRight = indexAt(br);
2270     if (!topLeft.isValid() && !bottomRight.isValid()) {
2271         if (command & QItemSelectionModel::Clear)
2272             selectionModel()->clear();
2273         return;
2274     }
2275     if (!topLeft.isValid() && !d->viewItems.isEmpty())
2276         topLeft = d->viewItems.first().index;
2277     if (!bottomRight.isValid() && !d->viewItems.isEmpty()) {
2278         const int column = d->header->logicalIndex(d->header->count() - 1);
2279         const QModelIndex index = d->viewItems.last().index;
2280         bottomRight = index.sibling(index.row(), column);
2281     }
2282 
2283     if (!d->isIndexEnabled(topLeft) || !d->isIndexEnabled(bottomRight))
2284         return;
2285 
2286     d->select(topLeft, bottomRight, command);
2287 }
2288 
2289 /*!
2290   Returns the rectangle from the viewport of the items in the given
2291   \a selection.
2292 
2293   Since 4.7, the returned region only contains rectangles intersecting
2294   (or included in) the viewport.
2295 */
visualRegionForSelection(const QItemSelection & selection) const2296 QRegion QTreeView::visualRegionForSelection(const QItemSelection &selection) const
2297 {
2298     Q_D(const QTreeView);
2299     if (selection.isEmpty())
2300         return QRegion();
2301 
2302     QRegion selectionRegion;
2303     const QRect &viewportRect = d->viewport->rect();
2304     for (int i = 0; i < selection.count(); ++i) {
2305         QItemSelectionRange range = selection.at(i);
2306         if (!range.isValid())
2307             continue;
2308         QModelIndex parent = range.parent();
2309         QModelIndex leftIndex = range.topLeft();
2310         int columnCount = d->model->columnCount(parent);
2311         while (leftIndex.isValid() && isIndexHidden(leftIndex)) {
2312             if (leftIndex.column() + 1 < columnCount)
2313                 leftIndex = d->model->index(leftIndex.row(), leftIndex.column() + 1, parent);
2314             else
2315                 leftIndex = QModelIndex();
2316         }
2317         if (!leftIndex.isValid())
2318             continue;
2319         const QRect leftRect = visualRect(leftIndex);
2320         int top = leftRect.top();
2321         QModelIndex rightIndex = range.bottomRight();
2322         while (rightIndex.isValid() && isIndexHidden(rightIndex)) {
2323             if (rightIndex.column() - 1 >= 0)
2324                 rightIndex = d->model->index(rightIndex.row(), rightIndex.column() - 1, parent);
2325             else
2326                 rightIndex = QModelIndex();
2327         }
2328         if (!rightIndex.isValid())
2329             continue;
2330         const QRect rightRect = visualRect(rightIndex);
2331         int bottom = rightRect.bottom();
2332         if (top > bottom)
2333             qSwap<int>(top, bottom);
2334         int height = bottom - top + 1;
2335         if (d->header->sectionsMoved()) {
2336             for (int c = range.left(); c <= range.right(); ++c) {
2337                 const QRect rangeRect(columnViewportPosition(c), top, columnWidth(c), height);
2338                 if (viewportRect.intersects(rangeRect))
2339                     selectionRegion += rangeRect;
2340             }
2341         } else {
2342             QRect combined = leftRect|rightRect;
2343             combined.setX(columnViewportPosition(isRightToLeft() ? range.right() : range.left()));
2344             if (viewportRect.intersects(combined))
2345                 selectionRegion += combined;
2346         }
2347     }
2348     return selectionRegion;
2349 }
2350 
2351 /*!
2352   \reimp
2353 */
selectedIndexes() const2354 QModelIndexList QTreeView::selectedIndexes() const
2355 {
2356     QModelIndexList viewSelected;
2357     QModelIndexList modelSelected;
2358     if (selectionModel())
2359         modelSelected = selectionModel()->selectedIndexes();
2360     for (int i = 0; i < modelSelected.count(); ++i) {
2361         // check that neither the parents nor the index is hidden before we add
2362         QModelIndex index = modelSelected.at(i);
2363         while (index.isValid() && !isIndexHidden(index))
2364             index = index.parent();
2365         if (index.isValid())
2366             continue;
2367         viewSelected.append(modelSelected.at(i));
2368     }
2369     return viewSelected;
2370 }
2371 
2372 /*!
2373   Scrolls the contents of the tree view by (\a dx, \a dy).
2374 */
scrollContentsBy(int dx,int dy)2375 void QTreeView::scrollContentsBy(int dx, int dy)
2376 {
2377     Q_D(QTreeView);
2378 
2379     d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling
2380 
2381     dx = isRightToLeft() ? -dx : dx;
2382     if (dx) {
2383         if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
2384             int oldOffset = d->header->offset();
2385             if (horizontalScrollBar()->value() == horizontalScrollBar()->maximum())
2386                 d->header->setOffsetToLastSection();
2387             else
2388                 d->header->setOffsetToSectionPosition(horizontalScrollBar()->value());
2389             int newOffset = d->header->offset();
2390             dx = isRightToLeft() ? newOffset - oldOffset : oldOffset - newOffset;
2391         } else {
2392             d->header->setOffset(horizontalScrollBar()->value());
2393         }
2394     }
2395 
2396     const int itemHeight = d->defaultItemHeight <= 0 ? sizeHintForRow(0) : d->defaultItemHeight;
2397     if (d->viewItems.isEmpty() || itemHeight == 0)
2398         return;
2399 
2400     // guestimate the number of items in the viewport
2401     int viewCount = d->viewport->height() / itemHeight;
2402     int maxDeltaY = qMin(d->viewItems.count(), viewCount);
2403     // no need to do a lot of work if we are going to redraw the whole thing anyway
2404     if (qAbs(dy) > qAbs(maxDeltaY) && d->editorIndexHash.isEmpty()) {
2405         verticalScrollBar()->update();
2406         d->viewport->update();
2407         return;
2408     }
2409 
2410     if (dy && verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
2411         int currentScrollbarValue = verticalScrollBar()->value();
2412         int previousScrollbarValue = currentScrollbarValue + dy; // -(-dy)
2413         int currentViewIndex = currentScrollbarValue; // the first visible item
2414         int previousViewIndex = previousScrollbarValue;
2415         const QVector<QTreeViewItem> viewItems = d->viewItems;
2416         dy = 0;
2417         if (previousViewIndex < currentViewIndex) { // scrolling down
2418             for (int i = previousViewIndex; i < currentViewIndex; ++i) {
2419                 if (i < d->viewItems.count())
2420                     dy -= d->itemHeight(i);
2421             }
2422         } else if (previousViewIndex > currentViewIndex) { // scrolling up
2423             for (int i = previousViewIndex - 1; i >= currentViewIndex; --i) {
2424                 if (i < d->viewItems.count())
2425                     dy += d->itemHeight(i);
2426             }
2427         }
2428     }
2429 
2430     d->scrollContentsBy(dx, dy);
2431 }
2432 
2433 /*!
2434   This slot is called whenever a column has been moved.
2435 */
columnMoved()2436 void QTreeView::columnMoved()
2437 {
2438     Q_D(QTreeView);
2439     updateEditorGeometries();
2440     d->viewport->update();
2441 }
2442 
2443 /*!
2444   \internal
2445 */
reexpand()2446 void QTreeView::reexpand()
2447 {
2448     // do nothing
2449 }
2450 
2451 /*!
2452   Informs the view that the rows from the \a start row to the \a end row
2453   inclusive have been inserted into the \a parent model item.
2454 */
rowsInserted(const QModelIndex & parent,int start,int end)2455 void QTreeView::rowsInserted(const QModelIndex &parent, int start, int end)
2456 {
2457     Q_D(QTreeView);
2458     // if we are going to do a complete relayout anyway, there is no need to update
2459     if (d->delayedPendingLayout) {
2460         QAbstractItemView::rowsInserted(parent, start, end);
2461         return;
2462     }
2463 
2464     //don't add a hierarchy on a column != 0
2465     if (parent.column() != 0 && parent.isValid()) {
2466         QAbstractItemView::rowsInserted(parent, start, end);
2467         return;
2468     }
2469 
2470     const int parentRowCount = d->model->rowCount(parent);
2471     const int delta = end - start + 1;
2472     if (parent != d->root && !d->isIndexExpanded(parent) && parentRowCount > delta) {
2473         QAbstractItemView::rowsInserted(parent, start, end);
2474         return;
2475     }
2476 
2477     const int parentItem = d->viewIndex(parent);
2478     if (((parentItem != -1) && d->viewItems.at(parentItem).expanded)
2479         || (parent == d->root)) {
2480         d->doDelayedItemsLayout();
2481     } else if (parentItem != -1 && (d->model->rowCount(parent) == end - start + 1)) {
2482         // the parent just went from 0 children to more. update to re-paint the decoration
2483         d->viewItems[parentItem].hasChildren = true;
2484         viewport()->update();
2485     }
2486     QAbstractItemView::rowsInserted(parent, start, end);
2487 }
2488 
2489 /*!
2490   Informs the view that the rows from the \a start row to the \a end row
2491   inclusive are about to removed from the given \a parent model item.
2492 */
rowsAboutToBeRemoved(const QModelIndex & parent,int start,int end)2493 void QTreeView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
2494 {
2495     Q_D(QTreeView);
2496     QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
2497     d->viewItems.clear();
2498 }
2499 
2500 /*!
2501     \since 4.1
2502 
2503     Informs the view that the rows from the \a start row to the \a end row
2504     inclusive have been removed from the given \a parent model item.
2505 */
rowsRemoved(const QModelIndex & parent,int start,int end)2506 void QTreeView::rowsRemoved(const QModelIndex &parent, int start, int end)
2507 {
2508     Q_D(QTreeView);
2509     d->viewItems.clear();
2510     d->doDelayedItemsLayout();
2511     d->hasRemovedItems = true;
2512     d->_q_rowsRemoved(parent, start, end);
2513 }
2514 
2515 /*!
2516   Informs the tree view that the number of columns in the tree view has
2517   changed from \a oldCount to \a newCount.
2518 */
columnCountChanged(int oldCount,int newCount)2519 void QTreeView::columnCountChanged(int oldCount, int newCount)
2520 {
2521     Q_D(QTreeView);
2522     if (oldCount == 0 && newCount > 0) {
2523         //if the first column has just been added we need to relayout.
2524         d->doDelayedItemsLayout();
2525     }
2526 
2527     if (isVisible())
2528         updateGeometries();
2529 	viewport()->update();
2530 }
2531 
2532 /*!
2533   Resizes the \a column given to the size of its contents.
2534 
2535   \sa columnWidth(), setColumnWidth()
2536 */
resizeColumnToContents(int column)2537 void QTreeView::resizeColumnToContents(int column)
2538 {
2539     Q_D(QTreeView);
2540     d->executePostedLayout();
2541     if (column < 0 || column >= d->header->count())
2542         return;
2543     int contents = sizeHintForColumn(column);
2544     int header = d->header->isHidden() ? 0 : d->header->sectionSizeHint(column);
2545     d->header->resizeSection(column, qMax(contents, header));
2546 }
2547 
2548 /*!
2549   \obsolete
2550   \overload
2551 
2552   Sorts the model by the values in the given \a column.
2553 */
sortByColumn(int column)2554 void QTreeView::sortByColumn(int column)
2555 {
2556     Q_D(QTreeView);
2557     sortByColumn(column, d->header->sortIndicatorOrder());
2558 }
2559 
2560 /*!
2561   \since 4.2
2562 
2563   Sets the model up for sorting by the values in the given \a column and \a order.
2564 
2565   \a column may be -1, in which case no sort indicator will be shown
2566   and the model will return to its natural, unsorted order. Note that not
2567   all models support this and may even crash in this case.
2568 
2569   \sa sortingEnabled
2570 */
sortByColumn(int column,Qt::SortOrder order)2571 void QTreeView::sortByColumn(int column, Qt::SortOrder order)
2572 {
2573     Q_D(QTreeView);
2574 
2575     //If sorting is enabled  will emit a signal connected to _q_sortIndicatorChanged, which then actually sorts
2576     d->header->setSortIndicator(column, order);
2577     //If sorting is not enabled, force to sort now.
2578     if (!d->sortingEnabled)
2579         d->model->sort(column, order);
2580 }
2581 
2582 /*!
2583   \reimp
2584 */
selectAll()2585 void QTreeView::selectAll()
2586 {
2587     Q_D(QTreeView);
2588     if (!selectionModel())
2589         return;
2590     SelectionMode mode = d->selectionMode;
2591     d->executePostedLayout(); //make sure we lay out the items
2592     if (mode != SingleSelection && !d->viewItems.isEmpty()) {
2593         const QModelIndex &idx = d->viewItems.last().index;
2594         QModelIndex lastItemIndex = idx.sibling(idx.row(), d->model->columnCount(idx.parent()) - 1);
2595         d->select(d->viewItems.first().index, lastItemIndex,
2596                   QItemSelectionModel::ClearAndSelect
2597                   |QItemSelectionModel::Rows);
2598     }
2599 }
2600 
2601 /*!
2602   \since 4.2
2603   Expands all expandable items.
2604 
2605   Warning: if the model contains a large number of items,
2606   this function will take some time to execute.
2607 
2608   \sa collapseAll() expand()  collapse() setExpanded()
2609 */
expandAll()2610 void QTreeView::expandAll()
2611 {
2612     Q_D(QTreeView);
2613     d->viewItems.clear();
2614     d->interruptDelayedItemsLayout();
2615     d->layout(-1, true);
2616     updateGeometries();
2617     d->viewport->update();
2618 }
2619 
2620 /*!
2621   \since 4.2
2622 
2623   Collapses all expanded items.
2624 
2625   \sa expandAll() expand()  collapse() setExpanded()
2626 */
collapseAll()2627 void QTreeView::collapseAll()
2628 {
2629     Q_D(QTreeView);
2630     d->expandedIndexes.clear();
2631     doItemsLayout();
2632 }
2633 
2634 /*!
2635   \since 4.3
2636   Expands all expandable items to the given \a depth.
2637 
2638   \sa expandAll() collapseAll() expand()  collapse() setExpanded()
2639 */
expandToDepth(int depth)2640 void QTreeView::expandToDepth(int depth)
2641 {
2642     Q_D(QTreeView);
2643     d->viewItems.clear();
2644     d->expandedIndexes.clear();
2645     d->interruptDelayedItemsLayout();
2646     d->layout(-1);
2647     for (int i = 0; i < d->viewItems.count(); ++i) {
2648         if (d->viewItems.at(i).level <= (uint)depth) {
2649             d->viewItems[i].expanded = true;
2650             d->layout(i);
2651             d->storeExpanded(d->viewItems.at(i).index);
2652         }
2653     }
2654     updateGeometries();
2655     d->viewport->update();
2656 }
2657 
2658 /*!
2659     This function is called whenever \a{column}'s size is changed in
2660     the header. \a oldSize and \a newSize give the previous size and
2661     the new size in pixels.
2662 
2663     \sa setColumnWidth()
2664 */
columnResized(int column,int,int)2665 void QTreeView::columnResized(int column, int /* oldSize */, int /* newSize */)
2666 {
2667     Q_D(QTreeView);
2668     d->columnsToUpdate.append(column);
2669     if (d->columnResizeTimerID == 0)
2670         d->columnResizeTimerID = startTimer(0);
2671 }
2672 
2673 /*!
2674   \reimp
2675 */
updateGeometries()2676 void QTreeView::updateGeometries()
2677 {
2678     Q_D(QTreeView);
2679     if (d->header) {
2680         if (d->geometryRecursionBlock)
2681             return;
2682         d->geometryRecursionBlock = true;
2683         QSize hint = d->header->isHidden() ? QSize(0, 0) : d->header->sizeHint();
2684         setViewportMargins(0, hint.height(), 0, 0);
2685         QRect vg = d->viewport->geometry();
2686         QRect geometryRect(vg.left(), vg.top() - hint.height(), vg.width(), hint.height());
2687         d->header->setGeometry(geometryRect);
2688         //d->header->setOffset(horizontalScrollBar()->value()); // ### bug ???
2689         QMetaObject::invokeMethod(d->header, "updateGeometries");
2690         d->updateScrollBars();
2691         d->geometryRecursionBlock = false;
2692     }
2693     QAbstractItemView::updateGeometries();
2694 }
2695 
2696 /*!
2697   Returns the size hint for the \a column's width or -1 if there is no
2698   model.
2699 
2700   If you need to set the width of a given column to a fixed value, call
2701   QHeaderView::resizeSection() on the view's header.
2702 
2703   If you reimplement this function in a subclass, note that the value you
2704   return is only used when resizeColumnToContents() is called. In that case,
2705   if a larger column width is required by either the view's header or
2706   the item delegate, that width will be used instead.
2707 
2708   \sa QWidget::sizeHint, header()
2709 */
sizeHintForColumn(int column) const2710 int QTreeView::sizeHintForColumn(int column) const
2711 {
2712     Q_D(const QTreeView);
2713     d->executePostedLayout();
2714     if (d->viewItems.isEmpty())
2715         return -1;
2716     ensurePolished();
2717     int w = 0;
2718     QStyleOptionViewItemV4 option = d->viewOptionsV4();
2719     const QVector<QTreeViewItem> viewItems = d->viewItems;
2720 
2721     int start = 0;
2722     int end = viewItems.count();
2723     if(end > 1000) { //if we have too many item this function would be too slow.
2724         //we get a good approximation by only iterate over 1000 items.
2725         start = qMax(0, d->firstVisibleItem() - 100);
2726         end = qMin(end, start + 900);
2727     }
2728 
2729     for (int i = start; i < end; ++i) {
2730         if (viewItems.at(i).spanning)
2731             continue; // we have no good size hint
2732         QModelIndex index = viewItems.at(i).index;
2733         index = index.sibling(index.row(), column);
2734         QWidget *editor = d->editorForIndex(index).widget.data();
2735         if (editor && d->persistent.contains(editor)) {
2736             w = qMax(w, editor->sizeHint().width());
2737             int min = editor->minimumSize().width();
2738             int max = editor->maximumSize().width();
2739             w = qBound(min, w, max);
2740         }
2741         int hint = d->delegateForIndex(index)->sizeHint(option, index).width();
2742         w = qMax(w, hint + (column == 0 ? d->indentationForItem(i) : 0));
2743     }
2744     return w;
2745 }
2746 
2747 /*!
2748   Returns the size hint for the row indicated by \a index.
2749 
2750   \sa sizeHintForColumn(), uniformRowHeights()
2751 */
indexRowSizeHint(const QModelIndex & index) const2752 int QTreeView::indexRowSizeHint(const QModelIndex &index) const
2753 {
2754     Q_D(const QTreeView);
2755     if (!d->isIndexValid(index) || !d->itemDelegate)
2756         return 0;
2757 
2758     int start = -1;
2759     int end = -1;
2760     int indexRow = index.row();
2761     int count = d->header->count();
2762     bool emptyHeader = (count == 0);
2763     QModelIndex parent = index.parent();
2764 
2765     if (count && isVisible()) {
2766         // If the sections have moved, we end up checking too many or too few
2767         start = d->header->visualIndexAt(0);
2768     } else {
2769         // If the header has not been laid out yet, we use the model directly
2770         count = d->model->columnCount(parent);
2771     }
2772 
2773     if (isRightToLeft()) {
2774         start = (start == -1 ? count - 1 : start);
2775         end = 0;
2776     } else {
2777         start = (start == -1 ? 0 : start);
2778         end = count - 1;
2779     }
2780 
2781     if (end < start)
2782         qSwap(end, start);
2783 
2784     int height = -1;
2785     QStyleOptionViewItemV4 option = d->viewOptionsV4();
2786     // ### If we want word wrapping in the items,
2787     // ### we need to go through all the columns
2788     // ### and set the width of the column
2789 
2790     // Hack to speed up the function
2791     option.rect.setWidth(-1);
2792 
2793     for (int column = start; column <= end; ++column) {
2794         int logicalColumn = emptyHeader ? column : d->header->logicalIndex(column);
2795         if (d->header->isSectionHidden(logicalColumn))
2796             continue;
2797         QModelIndex idx = d->model->index(indexRow, logicalColumn, parent);
2798         if (idx.isValid()) {
2799             QWidget *editor = d->editorForIndex(idx).widget.data();
2800             if (editor && d->persistent.contains(editor)) {
2801                 height = qMax(height, editor->sizeHint().height());
2802                 int min = editor->minimumSize().height();
2803                 int max = editor->maximumSize().height();
2804                 height = qBound(min, height, max);
2805             }
2806             int hint = d->delegateForIndex(idx)->sizeHint(option, idx).height();
2807             height = qMax(height, hint);
2808         }
2809     }
2810 
2811     return height;
2812 }
2813 
2814 /*!
2815     \since 4.3
2816     Returns the height of the row indicated by the given \a index.
2817     \sa indexRowSizeHint()
2818 */
rowHeight(const QModelIndex & index) const2819 int QTreeView::rowHeight(const QModelIndex &index) const
2820 {
2821     Q_D(const QTreeView);
2822     d->executePostedLayout();
2823     int i = d->viewIndex(index);
2824     if (i == -1)
2825         return 0;
2826     return d->itemHeight(i);
2827 }
2828 
2829 /*!
2830   \internal
2831 */
horizontalScrollbarAction(int action)2832 void QTreeView::horizontalScrollbarAction(int action)
2833 {
2834     QAbstractItemView::horizontalScrollbarAction(action);
2835 }
2836 
2837 /*!
2838   \reimp
2839 */
isIndexHidden(const QModelIndex & index) const2840 bool QTreeView::isIndexHidden(const QModelIndex &index) const
2841 {
2842     return (isColumnHidden(index.column()) || isRowHidden(index.row(), index.parent()));
2843 }
2844 
2845 /*
2846   private implementation
2847 */
initialize()2848 void QTreeViewPrivate::initialize()
2849 {
2850     Q_Q(QTreeView);
2851     updateStyledFrameWidths();
2852     q->setSelectionBehavior(QAbstractItemView::SelectRows);
2853     q->setSelectionMode(QAbstractItemView::SingleSelection);
2854     q->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
2855     q->setAttribute(Qt::WA_MacShowFocusRect);
2856 
2857     QHeaderView *header = new QHeaderView(Qt::Horizontal, q);
2858     header->setMovable(true);
2859     header->setStretchLastSection(true);
2860     header->setDefaultAlignment(Qt::AlignLeft|Qt::AlignVCenter);
2861     q->setHeader(header);
2862 #ifndef QT_NO_ANIMATION
2863     QObject::connect(&animatedOperation, SIGNAL(finished()), q, SLOT(_q_endAnimatedOperation()));
2864 #endif //QT_NO_ANIMATION
2865 }
2866 
expand(int item,bool emitSignal)2867 void QTreeViewPrivate::expand(int item, bool emitSignal)
2868 {
2869     Q_Q(QTreeView);
2870 
2871     if (item == -1 || viewItems.at(item).expanded)
2872         return;
2873 
2874 #ifndef QT_NO_ANIMATION
2875     if (emitSignal && animationsEnabled)
2876         prepareAnimatedOperation(item, QVariantAnimation::Forward);
2877 #endif //QT_NO_ANIMATION
2878      //if already animating, stateBeforeAnimation is set to the correct value
2879     if (state != QAbstractItemView::AnimatingState)
2880         stateBeforeAnimation = state;
2881     q->setState(QAbstractItemView::ExpandingState);
2882     const QModelIndex index = viewItems.at(item).index;
2883     storeExpanded(index);
2884     viewItems[item].expanded = true;
2885     layout(item);
2886     q->setState(stateBeforeAnimation);
2887 
2888     if (model->canFetchMore(index))
2889         model->fetchMore(index);
2890     if (emitSignal) {
2891         emit q->expanded(index);
2892 #ifndef QT_NO_ANIMATION
2893         if (animationsEnabled)
2894             beginAnimatedOperation();
2895 #endif //QT_NO_ANIMATION
2896     }
2897 }
2898 
insertViewItems(int pos,int count,const QTreeViewItem & viewItem)2899 void QTreeViewPrivate::insertViewItems(int pos, int count, const QTreeViewItem &viewItem)
2900 {
2901     viewItems.insert(pos, count, viewItem);
2902     QTreeViewItem *items = viewItems.data();
2903     for (int i = pos + count; i < viewItems.count(); i++)
2904         if (items[i].parentItem >= pos)
2905             items[i].parentItem += count;
2906 #ifndef QT_NO_ACCESSIBILITY
2907 #ifdef Q_WS_X11
2908     Q_Q(QTreeView);
2909     if (QAccessible::isActive()) {
2910         QAccessible::updateAccessibility(q, 0, QAccessible::TableModelChanged);
2911     }
2912 #endif
2913 #endif
2914 }
2915 
removeViewItems(int pos,int count)2916 void QTreeViewPrivate::removeViewItems(int pos, int count)
2917 {
2918     viewItems.remove(pos, count);
2919     QTreeViewItem *items = viewItems.data();
2920     for (int i = pos; i < viewItems.count(); i++)
2921         if (items[i].parentItem >= pos)
2922             items[i].parentItem -= count;
2923 #ifndef QT_NO_ACCESSIBILITY
2924 #ifdef Q_WS_X11
2925     Q_Q(QTreeView);
2926     if (QAccessible::isActive()) {
2927         QAccessible::updateAccessibility(q, 0, QAccessible::TableModelChanged);
2928     }
2929 #endif
2930 #endif
2931 }
2932 
2933 #if 0
2934 bool QTreeViewPrivate::checkViewItems() const
2935 {
2936     for (int i = 0; i < viewItems.count(); ++i) {
2937         const QTreeViewItem &vi = viewItems.at(i);
2938         if (vi.parentItem == -1) {
2939             Q_ASSERT(!vi.index.parent().isValid() || vi.index.parent() == root);
2940         } else {
2941             Q_ASSERT(vi.index.parent() == viewItems.at(vi.parentItem).index);
2942         }
2943     }
2944     return true;
2945 }
2946 #endif
2947 
collapse(int item,bool emitSignal)2948 void QTreeViewPrivate::collapse(int item, bool emitSignal)
2949 {
2950     Q_Q(QTreeView);
2951 
2952     if (item == -1 || expandedIndexes.isEmpty())
2953         return;
2954 
2955     //if the current item is now invisible, the autoscroll will expand the tree to see it, so disable the autoscroll
2956     delayedAutoScroll.stop();
2957 
2958     int total = viewItems.at(item).total;
2959     const QModelIndex &modelIndex = viewItems.at(item).index;
2960     if (!isPersistent(modelIndex))
2961         return; // if the index is not persistent, no chances it is expanded
2962     QSet<QPersistentModelIndex>::iterator it = expandedIndexes.find(modelIndex);
2963     if (it == expandedIndexes.end() || viewItems.at(item).expanded == false)
2964         return; // nothing to do
2965 
2966 #ifndef QT_NO_ANIMATION
2967     if (emitSignal && animationsEnabled)
2968         prepareAnimatedOperation(item, QVariantAnimation::Backward);
2969 #endif //QT_NO_ANIMATION
2970 
2971     //if already animating, stateBeforeAnimation is set to the correct value
2972     if (state != QAbstractItemView::AnimatingState)
2973         stateBeforeAnimation = state;
2974     q->setState(QAbstractItemView::CollapsingState);
2975     expandedIndexes.erase(it);
2976     viewItems[item].expanded = false;
2977     int index = item;
2978     while (index > -1) {
2979         viewItems[index].total -= total;
2980         index = viewItems[index].parentItem;
2981     }
2982     removeViewItems(item + 1, total); // collapse
2983     q->setState(stateBeforeAnimation);
2984 
2985     if (emitSignal) {
2986         emit q->collapsed(modelIndex);
2987 #ifndef QT_NO_ANIMATION
2988         if (animationsEnabled)
2989             beginAnimatedOperation();
2990 #endif //QT_NO_ANIMATION
2991     }
2992 }
2993 
2994 #ifndef QT_NO_ANIMATION
prepareAnimatedOperation(int item,QVariantAnimation::Direction direction)2995 void QTreeViewPrivate::prepareAnimatedOperation(int item, QVariantAnimation::Direction direction)
2996 {
2997     animatedOperation.item = item;
2998     animatedOperation.viewport = viewport;
2999     animatedOperation.setDirection(direction);
3000 
3001     int top = coordinateForItem(item) + itemHeight(item);
3002     QRect rect = viewport->rect();
3003     rect.setTop(top);
3004     if (direction == QVariantAnimation::Backward) {
3005         const int limit = rect.height() * 2;
3006         int h = 0;
3007         int c = item + viewItems.at(item).total + 1;
3008         for (int i = item + 1; i < c && h < limit; ++i)
3009             h += itemHeight(i);
3010         rect.setHeight(h);
3011         animatedOperation.setEndValue(top + h);
3012     }
3013     animatedOperation.setStartValue(top);
3014     animatedOperation.before = renderTreeToPixmapForAnimation(rect);
3015 }
3016 
beginAnimatedOperation()3017 void QTreeViewPrivate::beginAnimatedOperation()
3018 {
3019     Q_Q(QTreeView);
3020 
3021     QRect rect = viewport->rect();
3022     rect.setTop(animatedOperation.top());
3023     if (animatedOperation.direction() == QVariantAnimation::Forward) {
3024         const int limit = rect.height() * 2;
3025         int h = 0;
3026         int c = animatedOperation.item + viewItems.at(animatedOperation.item).total + 1;
3027         for (int i = animatedOperation.item + 1; i < c && h < limit; ++i)
3028             h += itemHeight(i);
3029         rect.setHeight(h);
3030         animatedOperation.setEndValue(animatedOperation.top() + h);
3031     }
3032 
3033     if (!rect.isEmpty()) {
3034         animatedOperation.after = renderTreeToPixmapForAnimation(rect);
3035 
3036         q->setState(QAbstractItemView::AnimatingState);
3037         animatedOperation.start(); //let's start the animation
3038     }
3039 }
3040 
drawAnimatedOperation(QPainter * painter) const3041 void QTreeViewPrivate::drawAnimatedOperation(QPainter *painter) const
3042 {
3043     const int start = animatedOperation.startValue().toInt(),
3044         end = animatedOperation.endValue().toInt(),
3045         current = animatedOperation.currentValue().toInt();
3046     bool collapsing = animatedOperation.direction() == QVariantAnimation::Backward;
3047     const QPixmap top = collapsing ? animatedOperation.before : animatedOperation.after;
3048     painter->drawPixmap(0, start, top, 0, end - current - 1, top.width(), top.height());
3049     const QPixmap bottom = collapsing ? animatedOperation.after : animatedOperation.before;
3050     painter->drawPixmap(0, current, bottom);
3051 }
3052 
renderTreeToPixmapForAnimation(const QRect & rect) const3053 QPixmap QTreeViewPrivate::renderTreeToPixmapForAnimation(const QRect &rect) const
3054 {
3055     Q_Q(const QTreeView);
3056     QPixmap pixmap(rect.size());
3057     if (rect.size().isEmpty())
3058         return pixmap;
3059     pixmap.fill(Qt::transparent); //the base might not be opaque, and we don't want uninitialized pixels.
3060     QPainter painter(&pixmap);
3061     painter.fillRect(QRect(QPoint(0,0), rect.size()), q->palette().base());
3062     painter.translate(0, -rect.top());
3063     q->drawTree(&painter, QRegion(rect));
3064     painter.end();
3065 
3066     //and now let's render the editors the editors
3067     QStyleOptionViewItemV4 option = viewOptionsV4();
3068     for (QEditorIndexHash::const_iterator it = editorIndexHash.constBegin(); it != editorIndexHash.constEnd(); ++it) {
3069         QWidget *editor = it.key();
3070         const QModelIndex &index = it.value();
3071         option.rect = q->visualRect(index);
3072         if (option.rect.isValid()) {
3073 
3074             if (QAbstractItemDelegate *delegate = delegateForIndex(index))
3075                 delegate->updateEditorGeometry(editor, option, index);
3076 
3077             const QPoint pos = editor->pos();
3078             if (rect.contains(pos)) {
3079                 editor->render(&pixmap, pos - rect.topLeft());
3080                 //the animation uses pixmap to display the treeview's content
3081                 //the editor is rendered on this pixmap and thus can (should) be hidden
3082                 editor->hide();
3083             }
3084         }
3085     }
3086 
3087 
3088     return pixmap;
3089 }
3090 
_q_endAnimatedOperation()3091 void QTreeViewPrivate::_q_endAnimatedOperation()
3092 {
3093     Q_Q(QTreeView);
3094     q->setState(stateBeforeAnimation);
3095     q->updateGeometries();
3096     viewport->update();
3097 }
3098 #endif //QT_NO_ANIMATION
3099 
_q_modelAboutToBeReset()3100 void QTreeViewPrivate::_q_modelAboutToBeReset()
3101 {
3102     viewItems.clear();
3103 }
3104 
_q_columnsAboutToBeRemoved(const QModelIndex & parent,int start,int end)3105 void QTreeViewPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
3106 {
3107     if (start <= 0 && 0 <= end)
3108         viewItems.clear();
3109     QAbstractItemViewPrivate::_q_columnsAboutToBeRemoved(parent, start, end);
3110 }
3111 
_q_columnsRemoved(const QModelIndex & parent,int start,int end)3112 void QTreeViewPrivate::_q_columnsRemoved(const QModelIndex &parent, int start, int end)
3113 {
3114     if (start <= 0 && 0 <= end)
3115         doDelayedItemsLayout();
3116     QAbstractItemViewPrivate::_q_columnsRemoved(parent, start, end);
3117 }
3118 
3119 /** \internal
3120     creates and initialize the viewItem structure of the children of the element \i
3121 
3122     set \a recursiveExpanding if the function has to expand all the children (called from expandAll)
3123     \a afterIsUninitialized is when we recurse from layout(-1), it means all the items after 'i' are
3124     not yet initialized and need not to be moved
3125  */
layout(int i,bool recursiveExpanding,bool afterIsUninitialized)3126 void QTreeViewPrivate::layout(int i, bool recursiveExpanding, bool afterIsUninitialized)
3127 {
3128     Q_Q(QTreeView);
3129     QModelIndex current;
3130     QModelIndex parent = (i < 0) ? (QModelIndex)root : modelIndex(i);
3131 
3132     if (i>=0 && !parent.isValid()) {
3133         //modelIndex() should never return something invalid for the real items.
3134         //This can happen if columncount has been set to 0.
3135         //To avoid infinite loop we stop here.
3136         return;
3137     }
3138 
3139     int count = 0;
3140     if (model->hasChildren(parent)) {
3141         if (model->canFetchMore(parent))
3142             model->fetchMore(parent);
3143         count = model->rowCount(parent);
3144     }
3145 
3146     bool expanding = true;
3147     if (i == -1) {
3148         if (uniformRowHeights) {
3149             QModelIndex index = model->index(0, 0, parent);
3150             defaultItemHeight = q->indexRowSizeHint(index);
3151         }
3152         viewItems.resize(count);
3153         afterIsUninitialized = true;
3154     } else if (viewItems[i].total != (uint)count) {
3155         if (!afterIsUninitialized)
3156             insertViewItems(i + 1, count, QTreeViewItem()); // expand
3157         else if (count > 0)
3158             viewItems.resize(viewItems.count() + count);
3159     } else {
3160         expanding = false;
3161     }
3162 
3163     int first = i + 1;
3164     int level = (i >= 0 ? viewItems.at(i).level + 1 : 0);
3165     int hidden = 0;
3166     int last = 0;
3167     int children = 0;
3168     QTreeViewItem *item = 0;
3169     for (int j = first; j < first + count; ++j) {
3170         current = model->index(j - first, 0, parent);
3171         if (isRowHidden(current)) {
3172             ++hidden;
3173             last = j - hidden + children;
3174         } else {
3175             last = j - hidden + children;
3176             if (item)
3177                 item->hasMoreSiblings = true;
3178             item = &viewItems[last];
3179             item->index = current;
3180             item->parentItem = i;
3181             item->level = level;
3182             item->height = 0;
3183             item->spanning = q->isFirstColumnSpanned(current.row(), parent);
3184             item->expanded = false;
3185             item->total = 0;
3186             item->hasMoreSiblings = false;
3187             if (recursiveExpanding || isIndexExpanded(current)) {
3188                 if (recursiveExpanding)
3189                     expandedIndexes.insert(current);
3190                 item->expanded = true;
3191                 layout(last, recursiveExpanding, afterIsUninitialized);
3192                 item = &viewItems[last];
3193                 children += item->total;
3194                 item->hasChildren = item->total > 0;
3195                 last = j - hidden + children;
3196             } else {
3197                 item->hasChildren = hasVisibleChildren(current);
3198             }
3199         }
3200     }
3201 
3202     // remove hidden items
3203     if (hidden > 0) {
3204         if (!afterIsUninitialized)
3205             removeViewItems(last + 1, hidden);
3206         else
3207             viewItems.resize(viewItems.size() - hidden);
3208     }
3209 
3210     if (!expanding)
3211         return; // nothing changed
3212 
3213     while (i > -1) {
3214         viewItems[i].total += count - hidden;
3215         i = viewItems[i].parentItem;
3216     }
3217 }
3218 
pageUp(int i) const3219 int QTreeViewPrivate::pageUp(int i) const
3220 {
3221     int index = itemAtCoordinate(coordinateForItem(i) - viewport->height());
3222     while (isItemHiddenOrDisabled(index))
3223         index--;
3224     return index == -1 ? 0 : index;
3225 }
3226 
pageDown(int i) const3227 int QTreeViewPrivate::pageDown(int i) const
3228 {
3229     int index = itemAtCoordinate(coordinateForItem(i) + viewport->height());
3230     while (isItemHiddenOrDisabled(index))
3231         index++;
3232     return index == -1 ? viewItems.count() - 1 : index;
3233 }
3234 
indentationForItem(int item) const3235 int QTreeViewPrivate::indentationForItem(int item) const
3236 {
3237     if (item < 0 || item >= viewItems.count())
3238         return 0;
3239     int level = viewItems.at(item).level;
3240     if (rootDecoration)
3241         ++level;
3242     return level * indent;
3243 }
3244 
itemHeight(int item) const3245 int QTreeViewPrivate::itemHeight(int item) const
3246 {
3247     if (uniformRowHeights)
3248         return defaultItemHeight;
3249     if (viewItems.isEmpty())
3250         return 0;
3251     const QModelIndex &index = viewItems.at(item).index;
3252     if (!index.isValid())
3253         return 0;
3254     int height = viewItems.at(item).height;
3255     if (height <= 0) {
3256         height = q_func()->indexRowSizeHint(index);
3257         viewItems[item].height = height;
3258     }
3259     return qMax(height, 0);
3260 }
3261 
3262 
3263 /*!
3264   \internal
3265   Returns the viewport y coordinate for \a item.
3266 */
coordinateForItem(int item) const3267 int QTreeViewPrivate::coordinateForItem(int item) const
3268 {
3269     if (verticalScrollMode == QAbstractItemView::ScrollPerPixel) {
3270         if (uniformRowHeights)
3271             return (item * defaultItemHeight) - vbar->value();
3272         // ### optimize (spans or caching)
3273         int y = 0;
3274         for (int i = 0; i < viewItems.count(); ++i) {
3275             if (i == item)
3276                 return y - vbar->value();
3277             y += itemHeight(i);
3278         }
3279     } else { // ScrollPerItem
3280         int topViewItemIndex = vbar->value();
3281         if (uniformRowHeights)
3282             return defaultItemHeight * (item - topViewItemIndex);
3283         if (item >= topViewItemIndex) {
3284             // search in the visible area first and continue down
3285             // ### slow if the item is not visible
3286             int viewItemCoordinate = 0;
3287             int viewItemIndex = topViewItemIndex;
3288             while (viewItemIndex < viewItems.count()) {
3289                 if (viewItemIndex == item)
3290                     return viewItemCoordinate;
3291                 viewItemCoordinate += itemHeight(viewItemIndex);
3292                 ++viewItemIndex;
3293             }
3294             // below the last item in the view
3295             Q_ASSERT(false);
3296             return viewItemCoordinate;
3297         } else {
3298             // search the area above the viewport (used for editor widgets)
3299             int viewItemCoordinate = 0;
3300             for (int viewItemIndex = topViewItemIndex; viewItemIndex > 0; --viewItemIndex) {
3301                 if (viewItemIndex == item)
3302                     return viewItemCoordinate;
3303                 viewItemCoordinate -= itemHeight(viewItemIndex - 1);
3304             }
3305             return viewItemCoordinate;
3306         }
3307     }
3308     return 0;
3309 }
3310 
3311 /*!
3312   \internal
3313   Returns the index of the view item at the
3314   given viewport \a coordinate.
3315 
3316   \sa modelIndex()
3317 */
itemAtCoordinate(int coordinate) const3318 int QTreeViewPrivate::itemAtCoordinate(int coordinate) const
3319 {
3320     const int itemCount = viewItems.count();
3321     if (itemCount == 0)
3322         return -1;
3323     if (uniformRowHeights && defaultItemHeight <= 0)
3324         return -1;
3325     if (verticalScrollMode == QAbstractItemView::ScrollPerPixel) {
3326         if (uniformRowHeights) {
3327             const int viewItemIndex = (coordinate + vbar->value()) / defaultItemHeight;
3328             return ((viewItemIndex >= itemCount || viewItemIndex < 0) ? -1 : viewItemIndex);
3329         }
3330         // ### optimize
3331         int viewItemCoordinate = 0;
3332         const int contentsCoordinate = coordinate + vbar->value();
3333         for (int viewItemIndex = 0; viewItemIndex < viewItems.count(); ++viewItemIndex) {
3334             viewItemCoordinate += itemHeight(viewItemIndex);
3335             if (viewItemCoordinate >= contentsCoordinate)
3336                 return (viewItemIndex >= itemCount ? -1 : viewItemIndex);
3337         }
3338     } else { // ScrollPerItem
3339         int topViewItemIndex = vbar->value();
3340         if (uniformRowHeights) {
3341             if (coordinate < 0)
3342                 coordinate -= defaultItemHeight - 1;
3343             const int viewItemIndex = topViewItemIndex + (coordinate / defaultItemHeight);
3344             return ((viewItemIndex >= itemCount || viewItemIndex < 0) ? -1 : viewItemIndex);
3345         }
3346         if (coordinate >= 0) {
3347             // the coordinate is in or below the viewport
3348             int viewItemCoordinate = 0;
3349             for (int viewItemIndex = topViewItemIndex; viewItemIndex < viewItems.count(); ++viewItemIndex) {
3350                 viewItemCoordinate += itemHeight(viewItemIndex);
3351                 if (viewItemCoordinate > coordinate)
3352                     return (viewItemIndex >= itemCount ? -1 : viewItemIndex);
3353             }
3354         } else {
3355             // the coordinate is above the viewport
3356             int viewItemCoordinate = 0;
3357             for (int viewItemIndex = topViewItemIndex; viewItemIndex >= 0; --viewItemIndex) {
3358                 if (viewItemCoordinate <= coordinate)
3359                     return (viewItemIndex >= itemCount ? -1 : viewItemIndex);
3360                 viewItemCoordinate -= itemHeight(viewItemIndex);
3361             }
3362         }
3363     }
3364     return -1;
3365 }
3366 
viewIndex(const QModelIndex & _index) const3367 int QTreeViewPrivate::viewIndex(const QModelIndex &_index) const
3368 {
3369     if (!_index.isValid() || viewItems.isEmpty())
3370         return -1;
3371 
3372     const int totalCount = viewItems.count();
3373     const QModelIndex index = _index.sibling(_index.row(), 0);
3374     const int row = index.row();
3375     const qint64 internalId = index.internalId();
3376 
3377     // We start nearest to the lastViewedItem
3378     int localCount = qMin(lastViewedItem - 1, totalCount - lastViewedItem);
3379     for (int i = 0; i < localCount; ++i) {
3380         const QModelIndex &idx1 = viewItems.at(lastViewedItem + i).index;
3381         if (idx1.row() == row && idx1.internalId() == internalId) {
3382             lastViewedItem = lastViewedItem + i;
3383             return lastViewedItem;
3384         }
3385         const QModelIndex &idx2 = viewItems.at(lastViewedItem - i - 1).index;
3386         if (idx2.row() == row && idx2.internalId() == internalId) {
3387             lastViewedItem = lastViewedItem - i - 1;
3388             return lastViewedItem;
3389         }
3390     }
3391 
3392     for (int j = qMax(0, lastViewedItem + localCount); j < totalCount; ++j) {
3393         const QModelIndex &idx = viewItems.at(j).index;
3394         if (idx.row() == row && idx.internalId() == internalId) {
3395             lastViewedItem = j;
3396             return j;
3397         }
3398     }
3399     for (int j = qMin(totalCount, lastViewedItem - localCount) - 1; j >= 0; --j) {
3400         const QModelIndex &idx = viewItems.at(j).index;
3401         if (idx.row() == row && idx.internalId() == internalId) {
3402             lastViewedItem = j;
3403             return j;
3404         }
3405     }
3406 
3407     // nothing found
3408     return -1;
3409 }
3410 
modelIndex(int i,int column) const3411 QModelIndex QTreeViewPrivate::modelIndex(int i, int column) const
3412 {
3413     if (i < 0 || i >= viewItems.count())
3414         return QModelIndex();
3415 
3416     QModelIndex ret = viewItems.at(i).index;
3417     if (column)
3418         ret = ret.sibling(ret.row(), column);
3419     return ret;
3420 }
3421 
firstVisibleItem(int * offset) const3422 int QTreeViewPrivate::firstVisibleItem(int *offset) const
3423 {
3424     const int value = vbar->value();
3425     if (verticalScrollMode == QAbstractItemView::ScrollPerItem) {
3426         if (offset)
3427             *offset = 0;
3428         return (value < 0 || value >= viewItems.count()) ? -1 : value;
3429     }
3430     // ScrollMode == ScrollPerPixel
3431     if (uniformRowHeights) {
3432         if (!defaultItemHeight)
3433             return -1;
3434 
3435         if (offset)
3436             *offset = -(value % defaultItemHeight);
3437         return value / defaultItemHeight;
3438     }
3439     int y = 0; // ### optimize (use spans ?)
3440     for (int i = 0; i < viewItems.count(); ++i) {
3441         y += itemHeight(i); // the height value is cached
3442         if (y > value) {
3443             if (offset)
3444                 *offset = y - value - itemHeight(i);
3445             return i;
3446         }
3447     }
3448     return -1;
3449 }
3450 
columnAt(int x) const3451 int QTreeViewPrivate::columnAt(int x) const
3452 {
3453     return header->logicalIndexAt(x);
3454 }
3455 
updateScrollBars()3456 void QTreeViewPrivate::updateScrollBars()
3457 {
3458     Q_Q(QTreeView);
3459     QSize viewportSize = viewport->size();
3460     if (!viewportSize.isValid())
3461         viewportSize = QSize(0, 0);
3462 
3463     if (viewItems.isEmpty()) {
3464         q->doItemsLayout();
3465     }
3466 
3467     int itemsInViewport = 0;
3468     if (uniformRowHeights) {
3469         if (defaultItemHeight <= 0)
3470             itemsInViewport = viewItems.count();
3471         else
3472             itemsInViewport = viewportSize.height() / defaultItemHeight;
3473     } else {
3474         const int itemsCount = viewItems.count();
3475         const int viewportHeight = viewportSize.height();
3476         for (int height = 0, item = itemsCount - 1; item >= 0; --item) {
3477             height += itemHeight(item);
3478             if (height > viewportHeight)
3479                 break;
3480             ++itemsInViewport;
3481         }
3482     }
3483     if (verticalScrollMode == QAbstractItemView::ScrollPerItem) {
3484         if (!viewItems.isEmpty())
3485             itemsInViewport = qMax(1, itemsInViewport);
3486         vbar->setRange(0, viewItems.count() - itemsInViewport);
3487         vbar->setPageStep(itemsInViewport);
3488         vbar->setSingleStep(1);
3489     } else { // scroll per pixel
3490         int contentsHeight = 0;
3491         if (uniformRowHeights) {
3492             contentsHeight = defaultItemHeight * viewItems.count();
3493         } else { // ### optimize (spans or caching)
3494             for (int i = 0; i < viewItems.count(); ++i)
3495                 contentsHeight += itemHeight(i);
3496         }
3497         vbar->setRange(0, contentsHeight - viewportSize.height());
3498         vbar->setPageStep(viewportSize.height());
3499         vbar->setSingleStep(qMax(viewportSize.height() / (itemsInViewport + 1), 2));
3500     }
3501 
3502     const int columnCount = header->count();
3503     const int viewportWidth = viewportSize.width();
3504     int columnsInViewport = 0;
3505     for (int width = 0, column = columnCount - 1; column >= 0; --column) {
3506         int logical = header->logicalIndex(column);
3507         width += header->sectionSize(logical);
3508         if (width > viewportWidth)
3509             break;
3510         ++columnsInViewport;
3511     }
3512     if (columnCount > 0)
3513         columnsInViewport = qMax(1, columnsInViewport);
3514     if (horizontalScrollMode == QAbstractItemView::ScrollPerItem) {
3515         hbar->setRange(0, columnCount - columnsInViewport);
3516         hbar->setPageStep(columnsInViewport);
3517         hbar->setSingleStep(1);
3518     } else { // scroll per pixel
3519         const int horizontalLength = header->length();
3520         const QSize maxSize = q->maximumViewportSize();
3521         if (maxSize.width() >= horizontalLength && vbar->maximum() <= 0)
3522             viewportSize = maxSize;
3523         hbar->setPageStep(viewportSize.width());
3524         hbar->setRange(0, qMax(horizontalLength - viewportSize.width(), 0));
3525         hbar->setSingleStep(qMax(viewportSize.width() / (columnsInViewport + 1), 2));
3526     }
3527 }
3528 
itemDecorationAt(const QPoint & pos) const3529 int QTreeViewPrivate::itemDecorationAt(const QPoint &pos) const
3530 {
3531     executePostedLayout();
3532     int x = pos.x();
3533     int column = header->logicalIndexAt(x);
3534     if (column != 0)
3535         return -1; // no logical index at x
3536 
3537     int viewItemIndex = itemAtCoordinate(pos.y());
3538     QRect returning = itemDecorationRect(modelIndex(viewItemIndex));
3539     if (!returning.contains(pos))
3540         return -1;
3541 
3542     return viewItemIndex;
3543 }
3544 
itemDecorationRect(const QModelIndex & index) const3545 QRect QTreeViewPrivate::itemDecorationRect(const QModelIndex &index) const
3546 {
3547     Q_Q(const QTreeView);
3548     if (!rootDecoration && index.parent() == root)
3549         return QRect(); // no decoration at root
3550 
3551     int viewItemIndex = viewIndex(index);
3552     if (viewItemIndex < 0 || !hasVisibleChildren(viewItems.at(viewItemIndex).index))
3553         return QRect();
3554 
3555     int itemIndentation = indentationForItem(viewItemIndex);
3556     int position = header->sectionViewportPosition(0);
3557     int size = header->sectionSize(0);
3558 
3559     QRect rect;
3560     if (q->isRightToLeft())
3561         rect = QRect(position + size - itemIndentation, coordinateForItem(viewItemIndex),
3562                      indent, itemHeight(viewItemIndex));
3563     else
3564         rect = QRect(position + itemIndentation - indent, coordinateForItem(viewItemIndex),
3565                      indent, itemHeight(viewItemIndex));
3566     QStyleOption opt;
3567     opt.initFrom(q);
3568     opt.rect = rect;
3569     return q->style()->subElementRect(QStyle::SE_TreeViewDisclosureItem, &opt, q);
3570 }
3571 
columnRanges(const QModelIndex & topIndex,const QModelIndex & bottomIndex) const3572 QList<QPair<int, int> > QTreeViewPrivate::columnRanges(const QModelIndex &topIndex,
3573                                                           const QModelIndex &bottomIndex) const
3574 {
3575     const int topVisual = header->visualIndex(topIndex.column()),
3576         bottomVisual = header->visualIndex(bottomIndex.column());
3577 
3578     const int start = qMin(topVisual, bottomVisual);
3579     const int end = qMax(topVisual, bottomVisual);
3580 
3581     QList<int> logicalIndexes;
3582 
3583     //we iterate over the visual indexes to get the logical indexes
3584     for (int c = start; c <= end; c++) {
3585         const int logical = header->logicalIndex(c);
3586         if (!header->isSectionHidden(logical)) {
3587             logicalIndexes << logical;
3588         }
3589     }
3590     //let's sort the list
3591     qSort(logicalIndexes.begin(), logicalIndexes.end());
3592 
3593     QList<QPair<int, int> > ret;
3594     QPair<int, int> current;
3595     current.first = -2; // -1 is not enough because -1+1 = 0
3596     current.second = -2;
3597     for(int i = 0; i < logicalIndexes.count(); ++i) {
3598         const int logicalColumn = logicalIndexes.at(i);
3599         if (current.second + 1 != logicalColumn) {
3600             if (current.first != -2) {
3601                 //let's save the current one
3602                 ret += current;
3603             }
3604             //let's start a new one
3605             current.first = current.second = logicalColumn;
3606         } else {
3607             current.second++;
3608         }
3609     }
3610 
3611     //let's get the last range
3612     if (current.first != -2) {
3613         ret += current;
3614     }
3615 
3616     return ret;
3617 }
3618 
select(const QModelIndex & topIndex,const QModelIndex & bottomIndex,QItemSelectionModel::SelectionFlags command)3619 void QTreeViewPrivate::select(const QModelIndex &topIndex, const QModelIndex &bottomIndex,
3620                               QItemSelectionModel::SelectionFlags command)
3621 {
3622     Q_Q(QTreeView);
3623     QItemSelection selection;
3624     const int top = viewIndex(topIndex),
3625         bottom = viewIndex(bottomIndex);
3626 
3627     const QList< QPair<int, int> > colRanges = columnRanges(topIndex, bottomIndex);
3628     QList< QPair<int, int> >::const_iterator it;
3629     for (it = colRanges.begin(); it != colRanges.end(); ++it) {
3630         const int left = (*it).first,
3631             right = (*it).second;
3632 
3633         QModelIndex previous;
3634         QItemSelectionRange currentRange;
3635         QStack<QItemSelectionRange> rangeStack;
3636         for (int i = top; i <= bottom; ++i) {
3637             QModelIndex index = modelIndex(i);
3638             QModelIndex parent = index.parent();
3639             QModelIndex previousParent = previous.parent();
3640             if (previous.isValid() && parent == previousParent) {
3641                 // same parent
3642                 if (qAbs(previous.row() - index.row()) > 1) {
3643                     //a hole (hidden index inside a range) has been detected
3644                     if (currentRange.isValid()) {
3645                         selection.append(currentRange);
3646                     }
3647                     //let's start a new range
3648                     currentRange = QItemSelectionRange(index.sibling(index.row(), left), index.sibling(index.row(), right));
3649                 } else {
3650                     QModelIndex tl = model->index(currentRange.top(), currentRange.left(),
3651                         currentRange.parent());
3652                     currentRange = QItemSelectionRange(tl, index.sibling(index.row(), right));
3653                 }
3654             } else if (previous.isValid() && parent == model->index(previous.row(), 0, previousParent)) {
3655                 // item is child of previous
3656                 rangeStack.push(currentRange);
3657                 currentRange = QItemSelectionRange(index.sibling(index.row(), left), index.sibling(index.row(), right));
3658             } else {
3659                 if (currentRange.isValid())
3660                     selection.append(currentRange);
3661                 if (rangeStack.isEmpty()) {
3662                     currentRange = QItemSelectionRange(index.sibling(index.row(), left), index.sibling(index.row(), right));
3663                 } else {
3664                     currentRange = rangeStack.pop();
3665                     index = currentRange.bottomRight(); //let's resume the range
3666                     --i; //we process again the current item
3667                 }
3668             }
3669             previous = index;
3670         }
3671         if (currentRange.isValid())
3672             selection.append(currentRange);
3673         for (int i = 0; i < rangeStack.count(); ++i)
3674             selection.append(rangeStack.at(i));
3675     }
3676     q->selectionModel()->select(selection, command);
3677 }
3678 
startAndEndColumns(const QRect & rect) const3679 QPair<int,int> QTreeViewPrivate::startAndEndColumns(const QRect &rect) const
3680 {
3681     Q_Q(const QTreeView);
3682     int start = header->visualIndexAt(rect.left());
3683     int end = header->visualIndexAt(rect.right());
3684     if (q->isRightToLeft()) {
3685         start = (start == -1 ? header->count() - 1 : start);
3686         end = (end == -1 ? 0 : end);
3687     } else {
3688         start = (start == -1 ? 0 : start);
3689         end = (end == -1 ? header->count() - 1 : end);
3690     }
3691     return qMakePair<int,int>(qMin(start, end), qMax(start, end));
3692 }
3693 
hasVisibleChildren(const QModelIndex & parent) const3694 bool QTreeViewPrivate::hasVisibleChildren(const QModelIndex& parent) const
3695 {
3696     Q_Q(const QTreeView);
3697     if (model->hasChildren(parent)) {
3698         if (hiddenIndexes.isEmpty())
3699             return true;
3700         if (q->isIndexHidden(parent))
3701             return false;
3702         int rowCount = model->rowCount(parent);
3703         for (int i = 0; i < rowCount; ++i) {
3704             if (!q->isRowHidden(i, parent))
3705                 return true;
3706         }
3707         if (rowCount == 0)
3708             return true;
3709     }
3710     return false;
3711 }
3712 
_q_sortIndicatorChanged(int column,Qt::SortOrder order)3713 void QTreeViewPrivate::_q_sortIndicatorChanged(int column, Qt::SortOrder order)
3714 {
3715     model->sort(column, order);
3716 }
3717 
3718 /*!
3719   \reimp
3720  */
currentChanged(const QModelIndex & current,const QModelIndex & previous)3721 void QTreeView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
3722 {
3723     QAbstractItemView::currentChanged(current, previous);
3724 
3725     if (allColumnsShowFocus()) {
3726         if (previous.isValid()) {
3727             QRect previousRect = visualRect(previous);
3728             previousRect.setX(0);
3729             previousRect.setWidth(viewport()->width());
3730             viewport()->update(previousRect);
3731         }
3732         if (current.isValid()) {
3733             QRect currentRect = visualRect(current);
3734             currentRect.setX(0);
3735             currentRect.setWidth(viewport()->width());
3736             viewport()->update(currentRect);
3737         }
3738     }
3739 #ifndef QT_NO_ACCESSIBILITY
3740     if (QAccessible::isActive() && current.isValid()) {
3741 #ifdef Q_WS_X11
3742         int entry = (visualIndex(current) + (header()?1:0))*current.model()->columnCount()+current.column() + 1;
3743         QAccessible::updateAccessibility(this, entry, QAccessible::Focus);
3744 #else
3745         int entry = visualIndex(current) + 1;
3746         if (header())
3747             ++entry;
3748         QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus);
3749 #endif
3750     }
3751 #endif
3752 }
3753 
3754 /*!
3755   \reimp
3756  */
selectionChanged(const QItemSelection & selected,const QItemSelection & deselected)3757 void QTreeView::selectionChanged(const QItemSelection &selected,
3758                                  const QItemSelection &deselected)
3759 {
3760     QAbstractItemView::selectionChanged(selected, deselected);
3761 #ifndef QT_NO_ACCESSIBILITY
3762     if (QAccessible::isActive()) {
3763         // ### does not work properly for selection ranges.
3764         QModelIndex sel = selected.indexes().value(0);
3765         if (sel.isValid()) {
3766 #ifdef Q_WS_X11
3767             int entry = (visualIndex(sel) + (header()?1:0))*sel.model()->columnCount()+sel.column() + 1;
3768             Q_ASSERT(entry > 0);
3769             QAccessible::updateAccessibility(this, entry, QAccessible::Selection);
3770 #else
3771             int entry = visualIndex(sel) + 1;
3772             if (header())
3773                 ++entry;
3774             QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection);
3775 #endif
3776         }
3777         QModelIndex desel = deselected.indexes().value(0);
3778         if (desel.isValid()) {
3779 #ifdef Q_WS_X11
3780             int entry = (visualIndex(desel) + (header()?1:0))*desel.model()->columnCount()+desel.column() + 1;
3781             Q_ASSERT(entry > 0);
3782             QAccessible::updateAccessibility(this, entry, QAccessible::SelectionRemove);
3783 #else
3784             int entry = visualIndex(desel) + 1;
3785             if (header())
3786                 ++entry;
3787             QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove);
3788 #endif
3789         }
3790     }
3791 #endif
3792 }
3793 
visualIndex(const QModelIndex & index) const3794 int QTreeView::visualIndex(const QModelIndex &index) const
3795 {
3796     Q_D(const QTreeView);
3797     d->executePostedLayout();
3798     return d->viewIndex(index);
3799 }
3800 
3801 QT_END_NAMESPACE
3802 
3803 #include "moc_qtreeview.cpp"
3804 
3805 #endif // QT_NO_TREEVIEW
3806