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