1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qlistview.h"
43 
44 #ifndef QT_NO_LISTVIEW
45 #include <qabstractitemdelegate.h>
46 #include <qapplication.h>
47 #include <qpainter.h>
48 #include <qbitmap.h>
49 #include <qvector.h>
50 #include <qstyle.h>
51 #include <qevent.h>
52 #include <qscrollbar.h>
53 #include <qrubberband.h>
54 #include <private/qlistview_p.h>
55 #include <qdebug.h>
56 #ifndef QT_NO_ACCESSIBILITY
57 #include <qaccessible.h>
58 #endif
59 
60 QT_BEGIN_NAMESPACE
61 
62 /*!
63     \class QListView
64 
65     \brief The QListView class provides a list or icon view onto a model.
66 
67     \ingroup model-view
68     \ingroup advanced
69 
70 
71     A QListView presents items stored in a model, either as a simple
72     non-hierarchical list, or as a collection of icons. This class is used
73     to provide lists and icon views that were previously provided by the
74     \c QListBox and \c QIconView classes, but using the more flexible
75     approach provided by Qt's model/view architecture.
76 
77     The QListView class is one of the \l{Model/View Classes}
78     and is part of Qt's \l{Model/View Programming}{model/view framework}.
79 
80     This view does not display horizontal or vertical headers; to display
81     a list of items with a horizontal header, use QTreeView instead.
82 
83     QListView implements the interfaces defined by the
84     QAbstractItemView class to allow it to display data provided by
85     models derived from the QAbstractItemModel class.
86 
87     Items in a list view can be displayed using one of two view modes:
88     In \l ListMode, the items are displayed in the form of a simple list;
89     in \l IconMode, the list view takes the form of an \e{icon view} in
90     which the items are displayed with icons like files in a file manager.
91     By default, the list view is in \l ListMode. To change the view mode,
92     use the setViewMode() function, and to determine the current view mode,
93     use viewMode().
94 
95     Items in these views are laid out in the direction specified by the
96     flow() of the list view. The items may be fixed in place, or allowed
97     to move, depending on the view's movement() state.
98 
99     If the items in the model cannot be completely laid out in the
100     direction of flow, they can be wrapped at the boundary of the view
101     widget; this depends on isWrapping(). This property is useful when the
102     items are being represented by an icon view.
103 
104     The resizeMode() and layoutMode() govern how and when the items are
105     laid out. Items are spaced according to their spacing(), and can exist
106     within a notional grid of size specified by gridSize(). The items can
107     be rendered as large or small icons depending on their iconSize().
108 
109     \table 100%
110     \row \o \inlineimage windowsxp-listview.png Screenshot of a Windows XP style list view
111          \o \inlineimage macintosh-listview.png Screenshot of a Macintosh style table view
112          \o \inlineimage plastique-listview.png Screenshot of a Plastique style table view
113     \row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} list view.
114          \o A \l{Macintosh Style Widget Gallery}{Macintosh style} list view.
115          \o A \l{Plastique Style Widget Gallery}{Plastique style} list view.
116     \endtable
117 
118     \section1 Improving Performance
119 
120     It is possible to give the view hints about the data it is handling in order
121     to improve its performance when displaying large numbers of items. One approach
122     that can be taken for views that are intended to display items with equal sizes
123     is to set the \l uniformItemSizes property to true.
124 
125     \sa {View Classes}, QTreeView, QTableView, QListWidget
126 */
127 
128 /*!
129     \enum QListView::ViewMode
130 
131     \value ListMode The items are laid out using TopToBottom flow, with Small size and Static movement
132     \value IconMode The items are laid out using LeftToRight flow, with Large size and Free movement
133 */
134 
135 /*!
136   \enum QListView::Movement
137 
138   \value Static The items cannot be moved by the user.
139   \value Free The items can be moved freely by the user.
140   \value Snap The items snap to the specified grid when moved; see
141   setGridSize().
142 */
143 
144 /*!
145   \enum QListView::Flow
146 
147   \value LeftToRight The items are laid out in the view from the left
148   to the right.
149   \value TopToBottom The items are laid out in the view from the top
150   to the bottom.
151 */
152 
153 /*!
154   \enum QListView::ResizeMode
155 
156   \value Fixed The items will only be laid out the first time the view is shown.
157   \value Adjust The items will be laid out every time the view is resized.
158 */
159 
160 /*!
161   \enum QListView::LayoutMode
162 
163   \value SinglePass The items are laid out all at once.
164   \value Batched The items are laid out in batches of \l batchSize items.
165   \sa batchSize
166 */
167 
168 /*!
169   \since 4.2
170   \fn void QListView::indexesMoved(const QModelIndexList &indexes)
171 
172   This signal is emitted when the specified \a indexes are moved in the view.
173 */
174 
175 /*!
176     Creates a new QListView with the given \a parent to view a model.
177     Use setModel() to set the model.
178 */
QListView(QWidget * parent)179 QListView::QListView(QWidget *parent)
180     : QAbstractItemView(*new QListViewPrivate, parent)
181 {
182     setViewMode(ListMode);
183     setSelectionMode(SingleSelection);
184     setAttribute(Qt::WA_MacShowFocusRect);
185     Q_D(QListView);               // We rely on a qobject_cast for PM_DefaultFrameWidth to change
186     d->updateStyledFrameWidths(); // hence we have to force an update now that the object has been constructed
187 }
188 
189 /*!
190   \internal
191 */
QListView(QListViewPrivate & dd,QWidget * parent)192 QListView::QListView(QListViewPrivate &dd, QWidget *parent)
193     : QAbstractItemView(dd, parent)
194 {
195     setViewMode(ListMode);
196     setSelectionMode(SingleSelection);
197     setAttribute(Qt::WA_MacShowFocusRect);
198     Q_D(QListView);               // We rely on a qobject_cast for PM_DefaultFrameWidth to change
199     d->updateStyledFrameWidths(); // hence we have to force an update now that the object has been constructed
200 }
201 
202 /*!
203   Destroys the view.
204 */
~QListView()205 QListView::~QListView()
206 {
207 }
208 
209 /*!
210     \property QListView::movement
211     \brief whether the items can be moved freely, are snapped to a
212     grid, or cannot be moved at all.
213 
214     This property determines how the user can move the items in the
215     view. \l Static means that the items can't be moved the user. \l
216     Free means that the user can drag and drop the items to any
217     position in the view. \l Snap means that the user can drag and
218     drop the items, but only to the positions in a notional grid
219     signified by the gridSize property.
220 
221     Setting this property when the view is visible will cause the
222     items to be laid out again.
223 
224     By default, this property is set to \l Static.
225 
226     \sa gridSize, resizeMode, viewMode
227 */
setMovement(Movement movement)228 void QListView::setMovement(Movement movement)
229 {
230     Q_D(QListView);
231     d->modeProperties |= uint(QListViewPrivate::Movement);
232     d->movement = movement;
233 
234 #ifndef QT_NO_DRAGANDDROP
235     bool movable = (movement != Static);
236     setDragEnabled(movable);
237     d->viewport->setAcceptDrops(movable);
238 #endif
239     d->doDelayedItemsLayout();
240 }
241 
movement() const242 QListView::Movement QListView::movement() const
243 {
244     Q_D(const QListView);
245     return d->movement;
246 }
247 
248 /*!
249     \property QListView::flow
250     \brief which direction the items layout should flow.
251 
252     If this property is \l LeftToRight, the items will be laid out left
253     to right. If the \l isWrapping property is true, the layout will wrap
254     when it reaches the right side of the visible area. If this
255     property is \l TopToBottom, the items will be laid out from the top
256     of the visible area, wrapping when it reaches the bottom.
257 
258     Setting this property when the view is visible will cause the
259     items to be laid out again.
260 
261     By default, this property is set to \l TopToBottom.
262 
263     \sa viewMode
264 */
setFlow(Flow flow)265 void QListView::setFlow(Flow flow)
266 {
267     Q_D(QListView);
268     d->modeProperties |= uint(QListViewPrivate::Flow);
269     d->flow = flow;
270     d->doDelayedItemsLayout();
271 }
272 
flow() const273 QListView::Flow QListView::flow() const
274 {
275     Q_D(const QListView);
276     return d->flow;
277 }
278 
279 /*!
280     \property QListView::isWrapping
281     \brief whether the items layout should wrap.
282 
283     This property holds whether the layout should wrap when there is
284     no more space in the visible area. The point at which the layout wraps
285     depends on the \l flow property.
286 
287     Setting this property when the view is visible will cause the
288     items to be laid out again.
289 
290     By default, this property is false.
291 
292     \sa viewMode
293 */
setWrapping(bool enable)294 void QListView::setWrapping(bool enable)
295 {
296     Q_D(QListView);
297     d->modeProperties |= uint(QListViewPrivate::Wrap);
298     d->setWrapping(enable);
299     d->doDelayedItemsLayout();
300 }
301 
isWrapping() const302 bool QListView::isWrapping() const
303 {
304     Q_D(const QListView);
305     return d->isWrapping();
306 }
307 
308 /*!
309     \property QListView::resizeMode
310     \brief whether the items are laid out again when the view is resized.
311 
312     If this property is \l Adjust, the items will be laid out again
313     when the view is resized. If the value is \l Fixed, the items will
314     not be laid out when the view is resized.
315 
316     By default, this property is set to \l Fixed.
317 
318     \sa movement, gridSize, viewMode
319 */
setResizeMode(ResizeMode mode)320 void QListView::setResizeMode(ResizeMode mode)
321 {
322     Q_D(QListView);
323     d->modeProperties |= uint(QListViewPrivate::ResizeMode);
324     d->resizeMode = mode;
325 }
326 
resizeMode() const327 QListView::ResizeMode QListView::resizeMode() const
328 {
329     Q_D(const QListView);
330     return d->resizeMode;
331 }
332 
333 /*!
334     \property QListView::layoutMode
335     \brief determines whether the layout of items should happen immediately or be delayed.
336 
337     This property holds the layout mode for the items. When the mode
338     is \l SinglePass (the default), the items are laid out all in one go.
339     When the mode is \l Batched, the items are laid out in batches of \l batchSize
340     items, while processing events. This makes it possible to
341     instantly view and interact with the visible items while the rest
342     are being laid out.
343 
344     \sa viewMode
345 */
setLayoutMode(LayoutMode mode)346 void QListView::setLayoutMode(LayoutMode mode)
347 {
348     Q_D(QListView);
349     d->layoutMode = mode;
350 }
351 
layoutMode() const352 QListView::LayoutMode QListView::layoutMode() const
353 {
354     Q_D(const QListView);
355     return d->layoutMode;
356 }
357 
358 /*!
359     \property QListView::spacing
360     \brief the space around the items in the layout
361 
362     This property is the size of the empty space that is padded around
363     an item in the layout.
364 
365     Setting this property when the view is visible will cause the
366     items to be laid out again.
367 
368     By default, this property contains a value of 0.
369 
370     \sa viewMode
371 */
372 // ### Qt5: Use same semantic as layouts (spacing is the size of space
373 // *between* items)
setSpacing(int space)374 void QListView::setSpacing(int space)
375 {
376     Q_D(QListView);
377     d->modeProperties |= uint(QListViewPrivate::Spacing);
378     d->setSpacing(space);
379     d->doDelayedItemsLayout();
380 }
381 
spacing() const382 int QListView::spacing() const
383 {
384     Q_D(const QListView);
385     return d->spacing();
386 }
387 
388 /*!
389     \property QListView::batchSize
390     \brief the number of items laid out in each batch if \l layoutMode is
391     set to \l Batched
392 
393     The default value is 100.
394 
395     \since 4.2
396 */
397 
setBatchSize(int batchSize)398 void QListView::setBatchSize(int batchSize)
399 {
400     Q_D(QListView);
401     if (batchSize <= 0) {
402         qWarning("Invalid batchSize (%d)", batchSize);
403         return;
404     }
405     d->batchSize = batchSize;
406 }
407 
batchSize() const408 int QListView::batchSize() const
409 {
410     Q_D(const QListView);
411     return d->batchSize;
412 }
413 
414 /*!
415     \property QListView::gridSize
416     \brief the size of the layout grid
417 
418     This property is the size of the grid in which the items are laid
419     out. The default is an empty size which means that there is no
420     grid and the layout is not done in a grid. Setting this property
421     to a non-empty size switches on the grid layout. (When a grid
422     layout is in force the \l spacing property is ignored.)
423 
424     Setting this property when the view is visible will cause the
425     items to be laid out again.
426 
427     \sa viewMode
428 */
setGridSize(const QSize & size)429 void QListView::setGridSize(const QSize &size)
430 {
431     Q_D(QListView);
432     d->modeProperties |= uint(QListViewPrivate::GridSize);
433     d->setGridSize(size);
434     d->doDelayedItemsLayout();
435 }
436 
gridSize() const437 QSize QListView::gridSize() const
438 {
439     Q_D(const QListView);
440     return d->gridSize();
441 }
442 
443 /*!
444     \property QListView::viewMode
445     \brief the view mode of the QListView.
446 
447     This property will change the other unset properties to conform
448     with the set view mode. QListView-specific properties that have already been set
449     will not be changed, unless clearPropertyFlags() has been called.
450 
451     Setting the view mode will enable or disable drag and drop based on the
452     selected movement. For ListMode, the default movement is \l Static
453     (drag and drop disabled); for IconMode, the default movement is
454     \l Free (drag and drop enabled).
455 
456     \sa isWrapping, spacing, gridSize, flow, movement, resizeMode
457 */
setViewMode(ViewMode mode)458 void QListView::setViewMode(ViewMode mode)
459 {
460     Q_D(QListView);
461     if (d->commonListView && d->viewMode == mode)
462         return;
463     d->viewMode = mode;
464 
465     delete d->commonListView;
466     if (mode == ListMode) {
467         d->commonListView = new QListModeViewBase(this, d);
468         if (!(d->modeProperties & QListViewPrivate::Wrap))
469             d->setWrapping(false);
470         if (!(d->modeProperties & QListViewPrivate::Spacing))
471             d->setSpacing(0);
472         if (!(d->modeProperties & QListViewPrivate::GridSize))
473             d->setGridSize(QSize());
474         if (!(d->modeProperties & QListViewPrivate::Flow))
475             d->flow = TopToBottom;
476         if (!(d->modeProperties & QListViewPrivate::Movement))
477             d->movement = Static;
478         if (!(d->modeProperties & QListViewPrivate::ResizeMode))
479             d->resizeMode = Fixed;
480         if (!(d->modeProperties & QListViewPrivate::SelectionRectVisible))
481             d->showElasticBand = false;
482     } else {
483         d->commonListView = new QIconModeViewBase(this, d);
484         if (!(d->modeProperties & QListViewPrivate::Wrap))
485             d->setWrapping(true);
486         if (!(d->modeProperties & QListViewPrivate::Spacing))
487             d->setSpacing(0);
488         if (!(d->modeProperties & QListViewPrivate::GridSize))
489             d->setGridSize(QSize());
490         if (!(d->modeProperties & QListViewPrivate::Flow))
491             d->flow = LeftToRight;
492         if (!(d->modeProperties & QListViewPrivate::Movement))
493             d->movement = Free;
494         if (!(d->modeProperties & QListViewPrivate::ResizeMode))
495             d->resizeMode = Fixed;
496         if (!(d->modeProperties & QListViewPrivate::SelectionRectVisible))
497             d->showElasticBand = true;
498     }
499 
500 #ifndef QT_NO_DRAGANDDROP
501     bool movable = (d->movement != Static);
502     setDragEnabled(movable);
503     setAcceptDrops(movable);
504 #endif
505     d->clear();
506     d->doDelayedItemsLayout();
507 }
508 
viewMode() const509 QListView::ViewMode QListView::viewMode() const
510 {
511     Q_D(const QListView);
512     return d->viewMode;
513 }
514 
515 /*!
516     Clears the QListView-specific property flags. See \l{viewMode}.
517 
518     Properties inherited from QAbstractItemView are not covered by the
519     property flags. Specifically, \l{QAbstractItemView::dragEnabled}
520     {dragEnabled} and \l{QAbstractItemView::acceptDrops}
521     {acceptsDrops} are computed by QListView when calling
522     setMovement() or setViewMode().
523 */
clearPropertyFlags()524 void QListView::clearPropertyFlags()
525 {
526     Q_D(QListView);
527     d->modeProperties = 0;
528 }
529 
530 /*!
531     Returns true if the \a row is hidden; otherwise returns false.
532 */
isRowHidden(int row) const533 bool QListView::isRowHidden(int row) const
534 {
535     Q_D(const QListView);
536     return d->isHidden(row);
537 }
538 
539 /*!
540     If \a hide is true, the given \a row will be hidden; otherwise
541     the \a row will be shown.
542 */
setRowHidden(int row,bool hide)543 void QListView::setRowHidden(int row, bool hide)
544 {
545     Q_D(QListView);
546     const bool hidden = d->isHidden(row);
547     if (hide && !hidden)
548         d->commonListView->appendHiddenRow(row);
549     else if (!hide && hidden)
550         d->commonListView->removeHiddenRow(row);
551     d->doDelayedItemsLayout();
552     d->viewport->update();
553 }
554 
555 /*!
556   \reimp
557 */
visualRect(const QModelIndex & index) const558 QRect QListView::visualRect(const QModelIndex &index) const
559 {
560     Q_D(const QListView);
561     return d->mapToViewport(rectForIndex(index));
562 }
563 
564 /*!
565   \reimp
566 */
scrollTo(const QModelIndex & index,ScrollHint hint)567 void QListView::scrollTo(const QModelIndex &index, ScrollHint hint)
568 {
569     Q_D(QListView);
570 
571     if (index.parent() != d->root || index.column() != d->column)
572         return;
573 
574     const QRect rect = visualRect(index);
575     if (hint == EnsureVisible && d->viewport->rect().contains(rect)) {
576         d->viewport->update(rect);
577         return;
578     }
579 
580     if (d->flow == QListView::TopToBottom || d->isWrapping()) // vertical
581         verticalScrollBar()->setValue(d->verticalScrollToValue(index, rect, hint));
582 
583     if (d->flow == QListView::LeftToRight || d->isWrapping()) // horizontal
584         horizontalScrollBar()->setValue(d->horizontalScrollToValue(index, rect, hint));
585 }
586 
horizontalScrollToValue(const QModelIndex & index,const QRect & rect,QListView::ScrollHint hint) const587 int QListViewPrivate::horizontalScrollToValue(const QModelIndex &index, const QRect &rect,
588                                               QListView::ScrollHint hint) const
589 {
590     Q_Q(const QListView);
591     const QRect area = viewport->rect();
592     const bool leftOf = q->isRightToLeft()
593                         ? (rect.left() < area.left()) && (rect.right() < area.right())
594                         : rect.left() < area.left();
595     const bool rightOf = q->isRightToLeft()
596                          ? rect.right() > area.right()
597                          : (rect.right() > area.right()) && (rect.left() > area.left());
598     return commonListView->horizontalScrollToValue(q->visualIndex(index), hint, leftOf, rightOf, area, rect);
599 }
600 
verticalScrollToValue(const QModelIndex & index,const QRect & rect,QListView::ScrollHint hint) const601 int QListViewPrivate::verticalScrollToValue(const QModelIndex &index, const QRect &rect,
602                                             QListView::ScrollHint hint) const
603 {
604     Q_Q(const QListView);
605     const QRect area = viewport->rect();
606     const bool above = (hint == QListView::EnsureVisible && rect.top() < area.top());
607     const bool below = (hint == QListView::EnsureVisible && rect.bottom() > area.bottom());
608     return commonListView->verticalScrollToValue(q->visualIndex(index), hint, above, below, area, rect);
609 }
610 
selectAll(QItemSelectionModel::SelectionFlags command)611 void QListViewPrivate::selectAll(QItemSelectionModel::SelectionFlags command)
612 {
613     if (!selectionModel)
614         return;
615 
616     QItemSelection selection;
617     QModelIndex topLeft;
618     int row = 0;
619     const int colCount = model->columnCount(root);
620     for(; row < model->rowCount(root); ++row) {
621         if (isHidden(row)) {
622             //it might be the end of a selection range
623             if (topLeft.isValid()) {
624                 QModelIndex bottomRight = model->index(row - 1, colCount - 1, root);
625                 selection.append(QItemSelectionRange(topLeft, bottomRight));
626                 topLeft = QModelIndex();
627             }
628             continue;
629         }
630 
631         if (!topLeft.isValid()) //start of a new selection range
632             topLeft = model->index(row, 0, root);
633     }
634 
635     if (topLeft.isValid()) {
636         //last selected range
637         QModelIndex bottomRight = model->index(row - 1, colCount - 1, root);
638         selection.append(QItemSelectionRange(topLeft, bottomRight));
639     }
640 
641     if (!selection.isEmpty())
642         selectionModel->select(selection, command);
643 }
644 
645 /*!
646   \reimp
647 
648   We have a QListView way of knowing what elements are on the viewport
649   through the intersectingSet function
650 */
draggablePaintPairs(const QModelIndexList & indexes,QRect * r) const651 QItemViewPaintPairs QListViewPrivate::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const
652 {
653     Q_ASSERT(r);
654     Q_Q(const QListView);
655     QRect &rect = *r;
656     const QRect viewportRect = viewport->rect();
657     QItemViewPaintPairs ret;
658     const QSet<QModelIndex> visibleIndexes = intersectingSet(viewportRect).toList().toSet();
659     for (int i = 0; i < indexes.count(); ++i) {
660         const QModelIndex &index = indexes.at(i);
661         if (visibleIndexes.contains(index)) {
662             const QRect current = q->visualRect(index);
663             ret += qMakePair(current, index);
664             rect |= current;
665         }
666     }
667     rect &= viewportRect;
668     return ret;
669 }
670 
671 /*!
672   \internal
673 */
reset()674 void QListView::reset()
675 {
676     Q_D(QListView);
677     d->clear();
678     d->hiddenRows.clear();
679     QAbstractItemView::reset();
680 }
681 
682 /*!
683   \internal
684 */
setRootIndex(const QModelIndex & index)685 void QListView::setRootIndex(const QModelIndex &index)
686 {
687     Q_D(QListView);
688     d->column = qBound(0, d->column, d->model->columnCount(index) - 1);
689     QAbstractItemView::setRootIndex(index);
690     // sometimes we get an update before reset() is called
691     d->clear();
692     d->hiddenRows.clear();
693 }
694 
695 /*!
696     \internal
697 
698     Scroll the view contents by \a dx and \a dy.
699 */
700 
scrollContentsBy(int dx,int dy)701 void QListView::scrollContentsBy(int dx, int dy)
702 {
703     Q_D(QListView);
704     d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling
705     d->commonListView->scrollContentsBy(dx, dy, d->state == QListView::DragSelectingState);
706 }
707 
708 /*!
709     \internal
710 
711     Resize the internal contents to \a width and \a height and set the
712     scroll bar ranges accordingly.
713 */
resizeContents(int width,int height)714 void QListView::resizeContents(int width, int height)
715 {
716     Q_D(QListView);
717     d->setContentsSize(width, height);
718 }
719 
720 /*!
721     \internal
722 */
contentsSize() const723 QSize QListView::contentsSize() const
724 {
725     Q_D(const QListView);
726     return d->contentsSize();
727 }
728 
729 /*!
730   \reimp
731 */
dataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight)732 void QListView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
733 {
734     d_func()->commonListView->dataChanged(topLeft, bottomRight);
735     QAbstractItemView::dataChanged(topLeft, bottomRight);
736 }
737 
738 /*!
739   \reimp
740 */
rowsInserted(const QModelIndex & parent,int start,int end)741 void QListView::rowsInserted(const QModelIndex &parent, int start, int end)
742 {
743     Q_D(QListView);
744     // ### be smarter about inserted items
745     d->clear();
746     d->doDelayedItemsLayout();
747     QAbstractItemView::rowsInserted(parent, start, end);
748 }
749 
750 /*!
751   \reimp
752 */
rowsAboutToBeRemoved(const QModelIndex & parent,int start,int end)753 void QListView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
754 {
755     Q_D(QListView);
756     // if the parent is above d->root in the tree, nothing will happen
757     QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
758     if (parent == d->root) {
759         QSet<QPersistentModelIndex>::iterator it = d->hiddenRows.begin();
760         while (it != d->hiddenRows.end()) {
761             int hiddenRow = it->row();
762             if (hiddenRow >= start && hiddenRow <= end) {
763                 it = d->hiddenRows.erase(it);
764             } else {
765                 ++it;
766             }
767         }
768     }
769     d->clear();
770     d->doDelayedItemsLayout();
771 }
772 
773 /*!
774   \reimp
775 */
mouseMoveEvent(QMouseEvent * e)776 void QListView::mouseMoveEvent(QMouseEvent *e)
777 {
778     if (!isVisible())
779         return;
780     Q_D(QListView);
781     QAbstractItemView::mouseMoveEvent(e);
782     if (state() == DragSelectingState
783         && d->showElasticBand
784         && d->selectionMode != SingleSelection
785         && d->selectionMode != NoSelection) {
786         QRect rect(d->pressedPosition, e->pos() + QPoint(horizontalOffset(), verticalOffset()));
787         rect = rect.normalized();
788         d->viewport->update(d->mapToViewport(rect.united(d->elasticBand)));
789         d->elasticBand = rect;
790     }
791 }
792 
793 /*!
794   \reimp
795 */
mouseReleaseEvent(QMouseEvent * e)796 void QListView::mouseReleaseEvent(QMouseEvent *e)
797 {
798     Q_D(QListView);
799     QAbstractItemView::mouseReleaseEvent(e);
800     // #### move this implementation into a dynamic class
801     if (d->showElasticBand && d->elasticBand.isValid()) {
802         d->viewport->update(d->mapToViewport(d->elasticBand));
803         d->elasticBand = QRect();
804     }
805 }
806 
807 /*!
808   \reimp
809 */
timerEvent(QTimerEvent * e)810 void QListView::timerEvent(QTimerEvent *e)
811 {
812     Q_D(QListView);
813     if (e->timerId() == d->batchLayoutTimer.timerId()) {
814         if (d->doItemsLayout(d->batchSize)) { // layout is done
815             d->batchLayoutTimer.stop();
816             updateGeometries();
817             d->viewport->update();
818         }
819     }
820     QAbstractItemView::timerEvent(e);
821 }
822 
823 /*!
824   \reimp
825 */
resizeEvent(QResizeEvent * e)826 void QListView::resizeEvent(QResizeEvent *e)
827 {
828     Q_D(QListView);
829     if (d->delayedPendingLayout)
830         return;
831 
832     QSize delta = e->size() - e->oldSize();
833 
834     if (delta.isNull())
835       return;
836 
837     bool listWrap = (d->viewMode == ListMode) && d->wrapItemText;
838     bool flowDimensionChanged = (d->flow == LeftToRight && delta.width() != 0)
839                                 || (d->flow == TopToBottom && delta.height() != 0);
840 
841     // We post a delayed relayout in the following cases :
842     // - we're wrapping
843     // - the state is NoState, we're adjusting and the size has changed in the flowing direction
844     if (listWrap
845         || (state() == NoState && d->resizeMode == Adjust && flowDimensionChanged)) {
846         d->doDelayedItemsLayout(100); // wait 1/10 sec before starting the layout
847     } else {
848         QAbstractItemView::resizeEvent(e);
849     }
850 }
851 
852 #ifndef QT_NO_DRAGANDDROP
853 
854 /*!
855   \reimp
856 */
dragMoveEvent(QDragMoveEvent * e)857 void QListView::dragMoveEvent(QDragMoveEvent *e)
858 {
859     Q_D(QListView);
860     if (!d->commonListView->filterDragMoveEvent(e)) {
861         if (viewMode() == QListView::ListMode && flow() == QListView::LeftToRight)
862             static_cast<QListModeViewBase *>(d->commonListView)->dragMoveEvent(e);
863         else
864             QAbstractItemView::dragMoveEvent(e);
865     }
866 }
867 
868 
869 /*!
870   \reimp
871 */
dragLeaveEvent(QDragLeaveEvent * e)872 void QListView::dragLeaveEvent(QDragLeaveEvent *e)
873 {
874     if (!d_func()->commonListView->filterDragLeaveEvent(e))
875         QAbstractItemView::dragLeaveEvent(e);
876 }
877 
878 /*!
879   \reimp
880 */
dropEvent(QDropEvent * e)881 void QListView::dropEvent(QDropEvent *e)
882 {
883     if (!d_func()->commonListView->filterDropEvent(e))
884         QAbstractItemView::dropEvent(e);
885 }
886 
887 /*!
888   \reimp
889 */
startDrag(Qt::DropActions supportedActions)890 void QListView::startDrag(Qt::DropActions supportedActions)
891 {
892     if (!d_func()->commonListView->filterStartDrag(supportedActions))
893         QAbstractItemView::startDrag(supportedActions);
894 }
895 
896 /*!
897     \internal
898 
899     Called whenever items from the view is dropped on the viewport.
900     The \a event provides additional information.
901 */
internalDrop(QDropEvent * event)902 void QListView::internalDrop(QDropEvent *event)
903 {
904     // ### Qt5: remove that function
905     Q_UNUSED(event);
906 }
907 
908 /*!
909     \internal
910 
911     Called whenever the user starts dragging items and the items are movable,
912     enabling internal dragging and dropping of items.
913 */
internalDrag(Qt::DropActions supportedActions)914 void QListView::internalDrag(Qt::DropActions supportedActions)
915 {
916     // ### Qt5: remove that function
917     Q_UNUSED(supportedActions);
918 }
919 
920 #endif // QT_NO_DRAGANDDROP
921 
922 /*!
923   \reimp
924 */
viewOptions() const925 QStyleOptionViewItem QListView::viewOptions() const
926 {
927     Q_D(const QListView);
928     QStyleOptionViewItem option = QAbstractItemView::viewOptions();
929     if (!d->iconSize.isValid()) { // otherwise it was already set in abstractitemview
930         int pm = (d->viewMode == ListMode
931                   ? style()->pixelMetric(QStyle::PM_ListViewIconSize, 0, this)
932                   : style()->pixelMetric(QStyle::PM_IconViewIconSize, 0, this));
933         option.decorationSize = QSize(pm, pm);
934     }
935     if (d->viewMode == IconMode) {
936         option.showDecorationSelected = false;
937         option.decorationPosition = QStyleOptionViewItem::Top;
938         option.displayAlignment = Qt::AlignCenter;
939     } else {
940         option.decorationPosition = QStyleOptionViewItem::Left;
941     }
942 
943     if (d->gridSize().isValid()) {
944         option.rect.setSize(d->gridSize());
945     }
946 
947     return option;
948 }
949 
950 
951 /*!
952   \reimp
953 */
paintEvent(QPaintEvent * e)954 void QListView::paintEvent(QPaintEvent *e)
955 {
956     Q_D(QListView);
957     if (!d->itemDelegate)
958         return;
959     QStyleOptionViewItemV4 option = d->viewOptionsV4();
960     QPainter painter(d->viewport);
961 
962     const QVector<QModelIndex> toBeRendered = d->intersectingSet(e->rect().translated(horizontalOffset(), verticalOffset()), false);
963 
964     const QModelIndex current = currentIndex();
965     const QModelIndex hover = d->hover;
966     const QAbstractItemModel *itemModel = d->model;
967     const QItemSelectionModel *selections = d->selectionModel;
968     const bool focus = (hasFocus() || d->viewport->hasFocus()) && current.isValid();
969     const bool alternate = d->alternatingColors;
970     const QStyle::State state = option.state;
971     const QAbstractItemView::State viewState = this->state();
972     const bool enabled = (state & QStyle::State_Enabled) != 0;
973 
974     bool alternateBase = false;
975     int previousRow = -2; // trigger the alternateBase adjustment on first pass
976 
977     int maxSize = (flow() == TopToBottom)
978         ? qMax(viewport()->size().width(), d->contentsSize().width()) - 2 * d->spacing()
979         : qMax(viewport()->size().height(), d->contentsSize().height()) - 2 * d->spacing();
980 
981     QVector<QModelIndex>::const_iterator end = toBeRendered.constEnd();
982     for (QVector<QModelIndex>::const_iterator it = toBeRendered.constBegin(); it != end; ++it) {
983         Q_ASSERT((*it).isValid());
984         option.rect = visualRect(*it);
985 
986         if (flow() == TopToBottom)
987             option.rect.setWidth(qMin(maxSize, option.rect.width()));
988         else
989             option.rect.setHeight(qMin(maxSize, option.rect.height()));
990 
991         option.state = state;
992         if (selections && selections->isSelected(*it))
993             option.state |= QStyle::State_Selected;
994         if (enabled) {
995             QPalette::ColorGroup cg;
996             if ((itemModel->flags(*it) & Qt::ItemIsEnabled) == 0) {
997                 option.state &= ~QStyle::State_Enabled;
998                 cg = QPalette::Disabled;
999             } else {
1000                 cg = QPalette::Normal;
1001             }
1002             option.palette.setCurrentColorGroup(cg);
1003         }
1004         if (focus && current == *it) {
1005             option.state |= QStyle::State_HasFocus;
1006             if (viewState == EditingState)
1007                 option.state |= QStyle::State_Editing;
1008         }
1009         if (*it == hover)
1010             option.state |= QStyle::State_MouseOver;
1011         else
1012             option.state &= ~QStyle::State_MouseOver;
1013 
1014         if (alternate) {
1015             int row = (*it).row();
1016             if (row != previousRow + 1) {
1017                 // adjust alternateBase according to rows in the "gap"
1018                 if (!d->hiddenRows.isEmpty()) {
1019                     for (int r = qMax(previousRow + 1, 0); r < row; ++r) {
1020                         if (!d->isHidden(r))
1021                             alternateBase = !alternateBase;
1022                     }
1023                 } else {
1024                     alternateBase = (row & 1) != 0;
1025                 }
1026             }
1027             if (alternateBase) {
1028                 option.features |= QStyleOptionViewItemV2::Alternate;
1029             } else {
1030                 option.features &= ~QStyleOptionViewItemV2::Alternate;
1031             }
1032 
1033             // draw background of the item (only alternate row). rest of the background
1034             // is provided by the delegate
1035             QStyle::State oldState = option.state;
1036             option.state &= ~QStyle::State_Selected;
1037             style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &option, &painter, this);
1038             option.state = oldState;
1039 
1040             alternateBase = !alternateBase;
1041             previousRow = row;
1042         }
1043 
1044         d->delegateForIndex(*it)->paint(&painter, option, *it);
1045     }
1046 
1047 #ifndef QT_NO_DRAGANDDROP
1048     d->commonListView->paintDragDrop(&painter);
1049 #endif
1050 
1051 #ifndef QT_NO_RUBBERBAND
1052     // #### move this implementation into a dynamic class
1053     if (d->showElasticBand && d->elasticBand.isValid()) {
1054         QStyleOptionRubberBand opt;
1055         opt.initFrom(this);
1056         opt.shape = QRubberBand::Rectangle;
1057         opt.opaque = false;
1058         opt.rect = d->mapToViewport(d->elasticBand, false).intersected(
1059             d->viewport->rect().adjusted(-16, -16, 16, 16));
1060         painter.save();
1061         style()->drawControl(QStyle::CE_RubberBand, &opt, &painter);
1062         painter.restore();
1063     }
1064 #endif
1065 }
1066 
1067 /*!
1068   \reimp
1069 */
indexAt(const QPoint & p) const1070 QModelIndex QListView::indexAt(const QPoint &p) const
1071 {
1072     Q_D(const QListView);
1073     QRect rect(p.x() + horizontalOffset(), p.y() + verticalOffset(), 1, 1);
1074     const QVector<QModelIndex> intersectVector = d->intersectingSet(rect);
1075     QModelIndex index = intersectVector.count() > 0
1076                         ? intersectVector.last() : QModelIndex();
1077     if (index.isValid() && visualRect(index).contains(p))
1078         return index;
1079     return QModelIndex();
1080 }
1081 
1082 /*!
1083   \reimp
1084 */
horizontalOffset() const1085 int QListView::horizontalOffset() const
1086 {
1087     return d_func()->commonListView->horizontalOffset();
1088 }
1089 
1090 /*!
1091   \reimp
1092 */
verticalOffset() const1093 int QListView::verticalOffset() const
1094 {
1095     return d_func()->commonListView->verticalOffset();
1096 }
1097 
1098 /*!
1099   \reimp
1100 */
moveCursor(CursorAction cursorAction,Qt::KeyboardModifiers modifiers)1101 QModelIndex QListView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
1102 {
1103     Q_D(QListView);
1104     Q_UNUSED(modifiers);
1105 
1106     QModelIndex current = currentIndex();
1107     if (!current.isValid()) {
1108         int rowCount = d->model->rowCount(d->root);
1109         if (!rowCount)
1110             return QModelIndex();
1111         int row = 0;
1112         while (row < rowCount && d->isHiddenOrDisabled(row))
1113             ++row;
1114         if (row >= rowCount)
1115             return QModelIndex();
1116         return d->model->index(row, d->column, d->root);
1117     }
1118 
1119     const QRect initialRect = rectForIndex(current);
1120     QRect rect = initialRect;
1121     if (rect.isEmpty()) {
1122         return d->model->index(0, d->column, d->root);
1123     }
1124     if (d->gridSize().isValid()) rect.setSize(d->gridSize());
1125 
1126     QSize contents = d->contentsSize();
1127     QVector<QModelIndex> intersectVector;
1128 
1129     switch (cursorAction) {
1130     case MoveLeft:
1131         while (intersectVector.isEmpty()) {
1132             rect.translate(-rect.width(), 0);
1133             if (rect.right() <= 0)
1134                 return current;
1135             if (rect.left() < 0)
1136                 rect.setLeft(0);
1137             intersectVector = d->intersectingSet(rect);
1138             d->removeCurrentAndDisabled(&intersectVector, current);
1139         }
1140         return d->closestIndex(initialRect, intersectVector);
1141     case MoveRight:
1142         while (intersectVector.isEmpty()) {
1143             rect.translate(rect.width(), 0);
1144             if (rect.left() >= contents.width())
1145                 return current;
1146             if (rect.right() > contents.width())
1147                 rect.setRight(contents.width());
1148             intersectVector = d->intersectingSet(rect);
1149             d->removeCurrentAndDisabled(&intersectVector, current);
1150         }
1151         return d->closestIndex(initialRect, intersectVector);
1152     case MovePageUp:
1153         // move current by (visibileRowCount - 1) items.
1154         // rect.translate(0, -rect.height()); will happen in the switch fallthrough for MoveUp.
1155         rect.moveTop(rect.top() - d->viewport->height() + 2 * rect.height());
1156         if (rect.top() < rect.height())
1157             rect.moveTop(rect.height());
1158     case MovePrevious:
1159     case MoveUp:
1160         while (intersectVector.isEmpty()) {
1161             rect.translate(0, -rect.height());
1162             if (rect.bottom() <= 0) {
1163 #ifdef QT_KEYPAD_NAVIGATION
1164                 if (QApplication::keypadNavigationEnabled()) {
1165                     int row = d->batchStartRow() - 1;
1166                     while (row >= 0 && d->isHiddenOrDisabled(row))
1167                         --row;
1168                     if (row >= 0)
1169                         return d->model->index(row, d->column, d->root);
1170                 }
1171 #endif
1172                 return current;
1173             }
1174             if (rect.top() < 0)
1175                 rect.setTop(0);
1176             intersectVector = d->intersectingSet(rect);
1177             d->removeCurrentAndDisabled(&intersectVector, current);
1178         }
1179         return d->closestIndex(initialRect, intersectVector);
1180     case MovePageDown:
1181         // move current by (visibileRowCount - 1) items.
1182         // rect.translate(0, rect.height()); will happen in the switch fallthrough for MoveDown.
1183         rect.moveTop(rect.top() + d->viewport->height() - 2 * rect.height());
1184         if (rect.bottom() > contents.height() - rect.height())
1185             rect.moveBottom(contents.height() - rect.height());
1186     case MoveNext:
1187     case MoveDown:
1188         while (intersectVector.isEmpty()) {
1189             rect.translate(0, rect.height());
1190             if (rect.top() >= contents.height()) {
1191 #ifdef QT_KEYPAD_NAVIGATION
1192                 if (QApplication::keypadNavigationEnabled()) {
1193                     int rowCount = d->model->rowCount(d->root);
1194                     int row = 0;
1195                     while (row < rowCount && d->isHiddenOrDisabled(row))
1196                         ++row;
1197                     if (row < rowCount)
1198                         return d->model->index(row, d->column, d->root);
1199                 }
1200 #endif
1201                 return current;
1202             }
1203             if (rect.bottom() > contents.height())
1204                 rect.setBottom(contents.height());
1205             intersectVector = d->intersectingSet(rect);
1206             d->removeCurrentAndDisabled(&intersectVector, current);
1207         }
1208         return d->closestIndex(initialRect, intersectVector);
1209     case MoveHome:
1210         return d->model->index(0, d->column, d->root);
1211     case MoveEnd:
1212         return d->model->index(d->batchStartRow() - 1, d->column, d->root);}
1213 
1214     return current;
1215 }
1216 
1217 /*!
1218     Returns the rectangle of the item at position \a index in the
1219     model. The rectangle is in contents coordinates.
1220 
1221     \sa visualRect()
1222 */
rectForIndex(const QModelIndex & index) const1223 QRect QListView::rectForIndex(const QModelIndex &index) const
1224 {
1225     return d_func()->rectForIndex(index);
1226 }
1227 
1228 /*!
1229     \since 4.1
1230 
1231     Sets the contents position of the item at \a index in the model to the given
1232     \a position.
1233     If the list view's movement mode is Static or its view mode is ListView,
1234     this function will have no effect.
1235 */
setPositionForIndex(const QPoint & position,const QModelIndex & index)1236 void QListView::setPositionForIndex(const QPoint &position, const QModelIndex &index)
1237 {
1238     Q_D(QListView);
1239     if (d->movement == Static
1240         || !d->isIndexValid(index)
1241         || index.parent() != d->root
1242         || index.column() != d->column)
1243         return;
1244 
1245     d->executePostedLayout();
1246     d->commonListView->setPositionForIndex(position, index);
1247 }
1248 
1249 /*!
1250   \reimp
1251 */
setSelection(const QRect & rect,QItemSelectionModel::SelectionFlags command)1252 void QListView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
1253 {
1254     Q_D(QListView);
1255     if (!d->selectionModel)
1256         return;
1257 
1258     // if we are wrapping, we can only selecte inside the contents rectangle
1259     int w = qMax(d->contentsSize().width(), d->viewport->width());
1260     int h = qMax(d->contentsSize().height(), d->viewport->height());
1261     if (d->wrap && !QRect(0, 0, w, h).intersects(rect))
1262         return;
1263 
1264     QItemSelection selection;
1265 
1266     if (rect.width() == 1 && rect.height() == 1) {
1267         const QVector<QModelIndex> intersectVector = d->intersectingSet(rect.translated(horizontalOffset(), verticalOffset()));
1268         QModelIndex tl;
1269         if (!intersectVector.isEmpty())
1270             tl = intersectVector.last(); // special case for mouse press; only select the top item
1271         if (tl.isValid() && d->isIndexEnabled(tl))
1272             selection.select(tl, tl);
1273     } else {
1274         if (state() == DragSelectingState) { // visual selection mode (rubberband selection)
1275             selection = d->selection(rect.translated(horizontalOffset(), verticalOffset()));
1276         } else { // logical selection mode (key and mouse click selection)
1277             QModelIndex tl, br;
1278             // get the first item
1279             const QRect topLeft(rect.left() + horizontalOffset(), rect.top() + verticalOffset(), 1, 1);
1280             QVector<QModelIndex> intersectVector = d->intersectingSet(topLeft);
1281             if (!intersectVector.isEmpty())
1282                 tl = intersectVector.last();
1283             // get the last item
1284             const QRect bottomRight(rect.right() + horizontalOffset(), rect.bottom() + verticalOffset(), 1, 1);
1285             intersectVector = d->intersectingSet(bottomRight);
1286             if (!intersectVector.isEmpty())
1287                 br = intersectVector.last();
1288 
1289             // get the ranges
1290             if (tl.isValid() && br.isValid()
1291                 && d->isIndexEnabled(tl)
1292                 && d->isIndexEnabled(br)) {
1293                 QRect first = rectForIndex(tl);
1294                 QRect last = rectForIndex(br);
1295                 QRect middle;
1296                 if (d->flow == LeftToRight) {
1297                     QRect &top = first;
1298                     QRect &bottom = last;
1299                     // if bottom is above top, swap them
1300                     if (top.center().y() > bottom.center().y()) {
1301                         QRect tmp = top;
1302                         top = bottom;
1303                         bottom = tmp;
1304                     }
1305                     // if the rect are on differnet lines, expand
1306                     if (top.top() != bottom.top()) {
1307                         // top rectangle
1308                         if (isRightToLeft())
1309                             top.setLeft(0);
1310                         else
1311                             top.setRight(contentsSize().width());
1312                         // bottom rectangle
1313                         if (isRightToLeft())
1314                             bottom.setRight(contentsSize().width());
1315                         else
1316                             bottom.setLeft(0);
1317                     } else if (top.left() > bottom.right()) {
1318                         if (isRightToLeft())
1319                             bottom.setLeft(top.right());
1320                         else
1321                             bottom.setRight(top.left());
1322                     } else {
1323                         if (isRightToLeft())
1324                             top.setLeft(bottom.right());
1325                         else
1326                             top.setRight(bottom.left());
1327                     }
1328                     // middle rectangle
1329                     if (top.bottom() < bottom.top()) {
1330                         if (gridSize().isValid() && !gridSize().isNull())
1331                             middle.setTop(top.top() + gridSize().height());
1332                         else
1333                             middle.setTop(top.bottom() + 1);
1334                         middle.setLeft(qMin(top.left(), bottom.left()));
1335                         middle.setBottom(bottom.top() - 1);
1336                         middle.setRight(qMax(top.right(), bottom.right()));
1337                     }
1338                 } else {    // TopToBottom
1339                     QRect &left = first;
1340                     QRect &right = last;
1341                     if (left.center().x() > right.center().x())
1342                         qSwap(left, right);
1343 
1344                     int ch = contentsSize().height();
1345                     if (left.left() != right.left()) {
1346                         // left rectangle
1347                         if (isRightToLeft())
1348                             left.setTop(0);
1349                         else
1350                             left.setBottom(ch);
1351 
1352                         // top rectangle
1353                         if (isRightToLeft())
1354                             right.setBottom(ch);
1355                         else
1356                             right.setTop(0);
1357                         // only set middle if the
1358                         middle.setTop(0);
1359                         middle.setBottom(ch);
1360                         if (gridSize().isValid() && !gridSize().isNull())
1361                             middle.setLeft(left.left() + gridSize().width());
1362                         else
1363                             middle.setLeft(left.right() + 1);
1364                         middle.setRight(right.left() - 1);
1365                     } else if (left.bottom() < right.top()) {
1366                         left.setBottom(right.top() - 1);
1367                     } else {
1368                         right.setBottom(left.top() - 1);
1369                     }
1370                 }
1371 
1372                 // do the selections
1373                 QItemSelection topSelection = d->selection(first);
1374                 QItemSelection middleSelection = d->selection(middle);
1375                 QItemSelection bottomSelection = d->selection(last);
1376                 // merge
1377                 selection.merge(topSelection, QItemSelectionModel::Select);
1378                 selection.merge(middleSelection, QItemSelectionModel::Select);
1379                 selection.merge(bottomSelection, QItemSelectionModel::Select);
1380             }
1381         }
1382     }
1383 
1384     d->selectionModel->select(selection, command);
1385 }
1386 
1387 /*!
1388   \reimp
1389 
1390   Since 4.7, the returned region only contains rectangles intersecting
1391   (or included in) the viewport.
1392 */
visualRegionForSelection(const QItemSelection & selection) const1393 QRegion QListView::visualRegionForSelection(const QItemSelection &selection) const
1394 {
1395     Q_D(const QListView);
1396     // ### NOTE: this is a potential bottleneck in non-static mode
1397     int c = d->column;
1398     QRegion selectionRegion;
1399     const QRect &viewportRect = d->viewport->rect();
1400     for (int i = 0; i < selection.count(); ++i) {
1401         if (!selection.at(i).isValid())
1402             continue;
1403         QModelIndex parent = selection.at(i).topLeft().parent();
1404         //we only display the children of the root in a listview
1405         //we're not interested in the other model indexes
1406         if (parent != d->root)
1407             continue;
1408         int t = selection.at(i).topLeft().row();
1409         int b = selection.at(i).bottomRight().row();
1410         if (d->viewMode == IconMode || d->isWrapping()) { // in non-static mode, we have to go through all selected items
1411             for (int r = t; r <= b; ++r) {
1412                 const QRect &rect = visualRect(d->model->index(r, c, parent));
1413                 if (viewportRect.intersects(rect))
1414                     selectionRegion += rect;
1415             }
1416         } else { // in static mode, we can optimize a bit
1417             while (t <= b && d->isHidden(t)) ++t;
1418             while (b >= t && d->isHidden(b)) --b;
1419             const QModelIndex top = d->model->index(t, c, parent);
1420             const QModelIndex bottom = d->model->index(b, c, parent);
1421             QRect rect(visualRect(top).topLeft(),
1422                        visualRect(bottom).bottomRight());
1423             if (viewportRect.intersects(rect))
1424                 selectionRegion += rect;
1425         }
1426     }
1427 
1428     return selectionRegion;
1429 }
1430 
1431 /*!
1432   \reimp
1433 */
selectedIndexes() const1434 QModelIndexList QListView::selectedIndexes() const
1435 {
1436     Q_D(const QListView);
1437     if (!d->selectionModel)
1438         return QModelIndexList();
1439 
1440     QModelIndexList viewSelected = d->selectionModel->selectedIndexes();
1441     for (int i = 0; i < viewSelected.count(); ++i) {
1442         const QModelIndex &index = viewSelected.at(i);
1443         if (!isIndexHidden(index) && index.parent() == d->root && index.column() == d->column)
1444             ++i;
1445         else
1446             viewSelected.removeAt(i);
1447     }
1448     return viewSelected;
1449 }
1450 
1451 /*!
1452     \internal
1453 
1454     Layout the items according to the flow and wrapping properties.
1455 */
doItemsLayout()1456 void QListView::doItemsLayout()
1457 {
1458     Q_D(QListView);
1459     // showing the scroll bars will trigger a resize event,
1460     // so we set the state to expanding to avoid
1461     // triggering another layout
1462     QAbstractItemView::State oldState = state();
1463     setState(ExpandingState);
1464     if (d->model->columnCount(d->root) > 0) { // no columns means no contents
1465         d->resetBatchStartRow();
1466         if (layoutMode() == SinglePass)
1467             d->doItemsLayout(d->model->rowCount(d->root)); // layout everything
1468         else if (!d->batchLayoutTimer.isActive()) {
1469             if (!d->doItemsLayout(d->batchSize)) // layout is done
1470                 d->batchLayoutTimer.start(0, this); // do a new batch as fast as possible
1471         }
1472     }
1473     QAbstractItemView::doItemsLayout();
1474     setState(oldState);        // restoring the oldState
1475 }
1476 
1477 /*!
1478   \reimp
1479 */
updateGeometries()1480 void QListView::updateGeometries()
1481 {
1482     Q_D(QListView);
1483     if (geometry().isEmpty() || d->model->rowCount(d->root) <= 0 || d->model->columnCount(d->root) <= 0) {
1484         horizontalScrollBar()->setRange(0, 0);
1485         verticalScrollBar()->setRange(0, 0);
1486     } else {
1487         QModelIndex index = d->model->index(0, d->column, d->root);
1488         QStyleOptionViewItemV4 option = d->viewOptionsV4();
1489         QSize step = d->itemSize(option, index);
1490         d->commonListView->updateHorizontalScrollBar(step);
1491         d->commonListView->updateVerticalScrollBar(step);
1492     }
1493 
1494     QAbstractItemView::updateGeometries();
1495 
1496     // if the scroll bars are turned off, we resize the contents to the viewport
1497     if (d->movement == Static && !d->isWrapping()) {
1498         d->layoutChildren(); // we need the viewport size to be updated
1499         if (d->flow == TopToBottom) {
1500             if (horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) {
1501                 d->setContentsSize(viewport()->width(), contentsSize().height());
1502                 horizontalScrollBar()->setRange(0, 0); // we see all the contents anyway
1503             }
1504         } else { // LeftToRight
1505             if (verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) {
1506                 d->setContentsSize(contentsSize().width(), viewport()->height());
1507                 verticalScrollBar()->setRange(0, 0); // we see all the contents anyway
1508             }
1509         }
1510     }
1511 
1512 }
1513 
1514 /*!
1515   \reimp
1516 */
isIndexHidden(const QModelIndex & index) const1517 bool QListView::isIndexHidden(const QModelIndex &index) const
1518 {
1519     Q_D(const QListView);
1520     return (d->isHidden(index.row())
1521             && (index.parent() == d->root)
1522             && index.column() == d->column);
1523 }
1524 
1525 /*!
1526     \property QListView::modelColumn
1527     \brief the column in the model that is visible
1528 
1529     By default, this property contains 0, indicating that the first
1530     column in the model will be shown.
1531 */
setModelColumn(int column)1532 void QListView::setModelColumn(int column)
1533 {
1534     Q_D(QListView);
1535     if (column < 0 || column >= d->model->columnCount(d->root))
1536         return;
1537     d->column = column;
1538     d->doDelayedItemsLayout();
1539 }
1540 
modelColumn() const1541 int QListView::modelColumn() const
1542 {
1543     Q_D(const QListView);
1544     return d->column;
1545 }
1546 
1547 /*!
1548     \property QListView::uniformItemSizes
1549     \brief whether all items in the listview have the same size
1550     \since 4.1
1551 
1552     This property should only be set to true if it is guaranteed that all items
1553     in the view have the same size. This enables the view to do some
1554     optimizations for performance purposes.
1555 
1556     By default, this property is false.
1557 */
setUniformItemSizes(bool enable)1558 void QListView::setUniformItemSizes(bool enable)
1559 {
1560     Q_D(QListView);
1561     d->uniformItemSizes = enable;
1562 }
1563 
uniformItemSizes() const1564 bool QListView::uniformItemSizes() const
1565 {
1566     Q_D(const QListView);
1567     return d->uniformItemSizes;
1568 }
1569 
1570 /*!
1571     \property QListView::wordWrap
1572     \brief the item text word-wrapping policy
1573     \since 4.2
1574 
1575     If this property is true then the item text is wrapped where
1576     necessary at word-breaks; otherwise it is not wrapped at all.
1577     This property is false by default.
1578 
1579     Please note that even if wrapping is enabled, the cell will not be
1580     expanded to make room for the text. It will print ellipsis for
1581     text that cannot be shown, according to the view's
1582     \l{QAbstractItemView::}{textElideMode}.
1583 */
setWordWrap(bool on)1584 void QListView::setWordWrap(bool on)
1585 {
1586     Q_D(QListView);
1587     if (d->wrapItemText == on)
1588         return;
1589     d->wrapItemText = on;
1590     d->doDelayedItemsLayout();
1591 }
1592 
wordWrap() const1593 bool QListView::wordWrap() const
1594 {
1595     Q_D(const QListView);
1596     return d->wrapItemText;
1597 }
1598 
1599 /*!
1600     \property QListView::selectionRectVisible
1601     \brief if the selection rectangle should be visible
1602     \since 4.3
1603 
1604     If this property is true then the selection rectangle is visible;
1605     otherwise it will be hidden.
1606 
1607     \note The selection rectangle will only be visible if the selection mode
1608     is in a mode where more than one item can be selected; i.e., it will not
1609     draw a selection rectangle if the selection mode is
1610     QAbstractItemView::SingleSelection.
1611 
1612     By default, this property is false.
1613 */
setSelectionRectVisible(bool show)1614 void QListView::setSelectionRectVisible(bool show)
1615 {
1616     Q_D(QListView);
1617     d->modeProperties |= uint(QListViewPrivate::SelectionRectVisible);
1618     d->setSelectionRectVisible(show);
1619 }
1620 
isSelectionRectVisible() const1621 bool QListView::isSelectionRectVisible() const
1622 {
1623     Q_D(const QListView);
1624     return d->isSelectionRectVisible();
1625 }
1626 
1627 /*!
1628     \reimp
1629 */
event(QEvent * e)1630 bool QListView::event(QEvent *e)
1631 {
1632     return QAbstractItemView::event(e);
1633 }
1634 
1635 /*
1636  * private object implementation
1637  */
1638 
QListViewPrivate()1639 QListViewPrivate::QListViewPrivate()
1640     : QAbstractItemViewPrivate(),
1641       commonListView(0),
1642       wrap(false),
1643       space(0),
1644       flow(QListView::TopToBottom),
1645       movement(QListView::Static),
1646       resizeMode(QListView::Fixed),
1647       layoutMode(QListView::SinglePass),
1648       viewMode(QListView::ListMode),
1649       modeProperties(0),
1650       column(0),
1651       uniformItemSizes(false),
1652       batchSize(100),
1653       showElasticBand(false)
1654 {
1655 }
1656 
~QListViewPrivate()1657 QListViewPrivate::~QListViewPrivate()
1658 {
1659     delete commonListView;
1660 }
1661 
clear()1662 void QListViewPrivate::clear()
1663 {
1664     // initialization of data structs
1665     cachedItemSize = QSize();
1666     commonListView->clear();
1667 }
1668 
prepareItemsLayout()1669 void QListViewPrivate::prepareItemsLayout()
1670 {
1671     Q_Q(QListView);
1672     clear();
1673 
1674     //take the size as if there were scrollbar in order to prevent scrollbar to blink
1675     layoutBounds = QRect(QPoint(), q->maximumViewportSize());
1676 
1677     int frameAroundContents = 0;
1678     if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents))
1679         frameAroundContents = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2;
1680 
1681     // maximumViewportSize() already takes scrollbar into account if policy is
1682     // Qt::ScrollBarAlwaysOn but scrollbar extent must be deduced if policy
1683     // is Qt::ScrollBarAsNeeded
1684     int verticalMargin = vbarpolicy==Qt::ScrollBarAsNeeded
1685         ? q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, vbar) + frameAroundContents
1686         : 0;
1687     int horizontalMargin =  hbarpolicy==Qt::ScrollBarAsNeeded
1688         ? q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, hbar) + frameAroundContents
1689         : 0;
1690 
1691     layoutBounds.adjust(0, 0, -verticalMargin, -horizontalMargin);
1692 
1693     int rowCount = model->columnCount(root) <= 0 ? 0 : model->rowCount(root);
1694     commonListView->setRowCount(rowCount);
1695 }
1696 
1697 /*!
1698   \internal
1699 */
doItemsLayout(int delta)1700 bool QListViewPrivate::doItemsLayout(int delta)
1701 {
1702     int max = model->rowCount(root) - 1;
1703     int first = batchStartRow();
1704     int last = qMin(first + delta - 1, max);
1705 
1706     if (first == 0) {
1707         layoutChildren(); // make sure the viewport has the right size
1708         prepareItemsLayout();
1709     }
1710 
1711     if (max < 0 || last < first) {
1712         return true; // nothing to do
1713     }
1714 
1715     QListViewLayoutInfo info;
1716     info.bounds = layoutBounds;
1717     info.grid = gridSize();
1718     info.spacing = (info.grid.isValid() ? 0 : spacing());
1719     info.first = first;
1720     info.last = last;
1721     info.wrap = isWrapping();
1722     info.flow = flow;
1723     info.max = max;
1724 
1725     return commonListView->doBatchedItemLayout(info, max);
1726 }
1727 
indexToListViewItem(const QModelIndex & index) const1728 QListViewItem QListViewPrivate::indexToListViewItem(const QModelIndex &index) const
1729 {
1730     if (!index.isValid() || isHidden(index.row()))
1731         return QListViewItem();
1732 
1733     return commonListView->indexToListViewItem(index);
1734 }
1735 
mapToViewport(const QRect & rect,bool extend) const1736 QRect QListViewPrivate::mapToViewport(const QRect &rect, bool extend) const
1737 {
1738     Q_Q(const QListView);
1739     if (!rect.isValid())
1740         return rect;
1741 
1742     QRect result = extend ? commonListView->mapToViewport(rect) : rect;
1743     int dx = -q->horizontalOffset();
1744     int dy = -q->verticalOffset();
1745     return result.adjusted(dx, dy, dx, dy);
1746 }
1747 
closestIndex(const QRect & target,const QVector<QModelIndex> & candidates) const1748 QModelIndex QListViewPrivate::closestIndex(const QRect &target,
1749                                            const QVector<QModelIndex> &candidates) const
1750 {
1751     int distance = 0;
1752     int shortest = INT_MAX;
1753     QModelIndex closest;
1754     QVector<QModelIndex>::const_iterator it = candidates.begin();
1755 
1756     for (; it != candidates.end(); ++it) {
1757         if (!(*it).isValid())
1758             continue;
1759 
1760         const QRect indexRect = indexToListViewItem(*it).rect();
1761 
1762         //if the center x (or y) position of an item is included in the rect of the other item,
1763         //we define the distance between them as the difference in x (or y) of their respective center.
1764         // Otherwise, we use the nahattan  length between the 2 items
1765         if ((target.center().x() >= indexRect.x() && target.center().x() < indexRect.right())
1766             || (indexRect.center().x() >= target.x() && indexRect.center().x() < target.right())) {
1767                 //one item's center is at the vertical of the other
1768                 distance = qAbs(indexRect.center().y() - target.center().y());
1769         } else if ((target.center().y() >= indexRect.y() && target.center().y() < indexRect.bottom())
1770             || (indexRect.center().y() >= target.y() && indexRect.center().y() < target.bottom())) {
1771                 //one item's center is at the vertical of the other
1772                 distance = qAbs(indexRect.center().x() - target.center().x());
1773         } else {
1774             distance = (indexRect.center() - target.center()).manhattanLength();
1775         }
1776         if (distance < shortest) {
1777             shortest = distance;
1778             closest = *it;
1779         }
1780     }
1781     return closest;
1782 }
1783 
itemSize(const QStyleOptionViewItem & option,const QModelIndex & index) const1784 QSize QListViewPrivate::itemSize(const QStyleOptionViewItem &option, const QModelIndex &index) const
1785 {
1786     if (!uniformItemSizes) {
1787         const QAbstractItemDelegate *delegate = delegateForIndex(index);
1788         return delegate ? delegate->sizeHint(option, index) : QSize();
1789     }
1790     if (!cachedItemSize.isValid()) { // the last item is probaly the largest, so we use its size
1791         int row = model->rowCount(root) - 1;
1792         QModelIndex sample = model->index(row, column, root);
1793         const QAbstractItemDelegate *delegate = delegateForIndex(sample);
1794         cachedItemSize = delegate ? delegate->sizeHint(option, sample) : QSize();
1795     }
1796     return cachedItemSize;
1797 }
1798 
selection(const QRect & rect) const1799 QItemSelection QListViewPrivate::selection(const QRect &rect) const
1800 {
1801     QItemSelection selection;
1802     QModelIndex tl, br;
1803     const QVector<QModelIndex> intersectVector = intersectingSet(rect);
1804     QVector<QModelIndex>::const_iterator it = intersectVector.begin();
1805     for (; it != intersectVector.end(); ++it) {
1806         if (!tl.isValid() && !br.isValid()) {
1807             tl = br = *it;
1808         } else if ((*it).row() == (tl.row() - 1)) {
1809             tl = *it; // expand current range
1810         } else if ((*it).row() == (br.row() + 1)) {
1811             br = (*it); // expand current range
1812         } else {
1813             selection.select(tl, br); // select current range
1814             tl = br = *it; // start new range
1815         }
1816     }
1817 
1818     if (tl.isValid() && br.isValid())
1819         selection.select(tl, br);
1820     else if (tl.isValid())
1821         selection.select(tl, tl);
1822     else if (br.isValid())
1823         selection.select(br, br);
1824 
1825     return selection;
1826 }
1827 
1828 #ifndef QT_NO_DRAGANDDROP
position(const QPoint & pos,const QRect & rect,const QModelIndex & idx) const1829 QAbstractItemView::DropIndicatorPosition QListViewPrivate::position(const QPoint &pos, const QRect &rect, const QModelIndex &idx) const
1830 {
1831     if (viewMode == QListView::ListMode && flow == QListView::LeftToRight)
1832         return static_cast<QListModeViewBase *>(commonListView)->position(pos, rect, idx);
1833     else
1834         return QAbstractItemViewPrivate::position(pos, rect, idx);
1835 }
1836 
dropOn(QDropEvent * event,int * dropRow,int * dropCol,QModelIndex * dropIndex)1837 bool QListViewPrivate::dropOn(QDropEvent *event, int *dropRow, int *dropCol, QModelIndex *dropIndex)
1838 {
1839     if (viewMode == QListView::ListMode && flow == QListView::LeftToRight)
1840         return static_cast<QListModeViewBase *>(commonListView)->dropOn(event, dropRow, dropCol, dropIndex);
1841     else
1842         return QAbstractItemViewPrivate::dropOn(event, dropRow, dropCol, dropIndex);
1843 }
1844 #endif
1845 
1846 /*
1847  * Common ListView Implementation
1848 */
1849 
appendHiddenRow(int row)1850 void QCommonListViewBase::appendHiddenRow(int row)
1851 {
1852     dd->hiddenRows.insert(dd->model->index(row, 0, qq->rootIndex()));
1853 }
1854 
removeHiddenRow(int row)1855 void QCommonListViewBase::removeHiddenRow(int row)
1856 {
1857     dd->hiddenRows.remove(dd->model->index(row, 0, qq->rootIndex()));
1858 }
1859 
1860 #ifndef QT_NO_DRAGANDDROP
paintDragDrop(QPainter * painter)1861 void QCommonListViewBase::paintDragDrop(QPainter *painter)
1862 {
1863     // FIXME: Until the we can provide a proper drop indicator
1864     // in IconMode, it makes no sense to show it
1865     dd->paintDropIndicator(painter);
1866 }
1867 #endif
1868 
updateHorizontalScrollBar(const QSize & step)1869 void QCommonListViewBase::updateHorizontalScrollBar(const QSize &step)
1870 {
1871     horizontalScrollBar()->setSingleStep(step.width() + spacing());
1872     horizontalScrollBar()->setPageStep(viewport()->width());
1873 
1874     // If both scroll bars are set to auto, we might end up in a situation with enough space
1875     // for the actual content. But still one of the scroll bars will become enabled due to
1876     // the other one using the space. The other one will become invisible in the same cycle.
1877     // -> Infinite loop, QTBUG-39902
1878     const bool bothScrollBarsAuto = qq->verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded &&
1879                                     qq->horizontalScrollBarPolicy() == Qt::ScrollBarAsNeeded;
1880 
1881     if (bothScrollBarsAuto && contentsSize.width() - qq->verticalScrollBar()->width() <= viewport()->width()
1882                            && contentsSize.height() - qq->horizontalScrollBar()->height() <= viewport()->height()) {
1883         // break the infinite loop described above by setting the range to 0, 0.
1884         // QAbstractScrollArea will then hide the scroll bar for us
1885         horizontalScrollBar()->setRange(0, 0);
1886     } else {
1887         horizontalScrollBar()->setRange(0, contentsSize.width() - viewport()->width());
1888     }
1889 }
1890 
updateVerticalScrollBar(const QSize & step)1891 void QCommonListViewBase::updateVerticalScrollBar(const QSize &step)
1892 {
1893     verticalScrollBar()->setSingleStep(step.height() + spacing());
1894     verticalScrollBar()->setPageStep(viewport()->height());
1895 
1896     // If both scroll bars are set to auto, we might end up in a situation with enough space
1897     // for the actual content. But still one of the scroll bars will become enabled due to
1898     // the other one using the space. The other one will become invisible in the same cycle.
1899     // -> Infinite loop, QTBUG-39902
1900     const bool bothScrollBarsAuto = qq->verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded &&
1901                                     qq->horizontalScrollBarPolicy() == Qt::ScrollBarAsNeeded;
1902 
1903     if (bothScrollBarsAuto && contentsSize.width() - qq->verticalScrollBar()->width() <= viewport()->width()
1904                            && contentsSize.height() - qq->horizontalScrollBar()->height() <= viewport()->height()) {
1905         // break the infinite loop described above by setting the range to 0, 0.
1906         // QAbstractScrollArea will then hide the scroll bar for us
1907         verticalScrollBar()->setRange(0, 0);
1908     } else {
1909         verticalScrollBar()->setRange(0, contentsSize.height() - viewport()->height());
1910     }
1911 }
1912 
scrollContentsBy(int dx,int dy,bool)1913 void QCommonListViewBase::scrollContentsBy(int dx, int dy, bool /*scrollElasticBand*/)
1914 {
1915     dd->scrollContentsBy(isRightToLeft() ? -dx : dx, dy);
1916 }
1917 
verticalScrollToValue(int,QListView::ScrollHint hint,bool above,bool below,const QRect & area,const QRect & rect) const1918 int QCommonListViewBase::verticalScrollToValue(int /*index*/, QListView::ScrollHint hint,
1919                                           bool above, bool below, const QRect &area, const QRect &rect) const
1920 {
1921     int verticalValue = verticalScrollBar()->value();
1922     QRect adjusted = rect.adjusted(-spacing(), -spacing(), spacing(), spacing());
1923     if (hint == QListView::PositionAtTop || above)
1924         verticalValue += adjusted.top();
1925     else if (hint == QListView::PositionAtBottom || below)
1926         verticalValue += qMin(adjusted.top(), adjusted.bottom() - area.height() + 1);
1927     else if (hint == QListView::PositionAtCenter)
1928         verticalValue += adjusted.top() - ((area.height() - adjusted.height()) / 2);
1929     return verticalValue;
1930 }
1931 
horizontalOffset() const1932 int QCommonListViewBase::horizontalOffset() const
1933 {
1934     return (isRightToLeft() ? horizontalScrollBar()->maximum() - horizontalScrollBar()->value() : horizontalScrollBar()->value());
1935 }
1936 
horizontalScrollToValue(const int,QListView::ScrollHint hint,bool leftOf,bool rightOf,const QRect & area,const QRect & rect) const1937 int QCommonListViewBase::horizontalScrollToValue(const int /*index*/, QListView::ScrollHint hint,
1938                                             bool leftOf, bool rightOf, const QRect &area, const QRect &rect) const
1939 {
1940     int horizontalValue = horizontalScrollBar()->value();
1941     if (isRightToLeft()) {
1942         if (hint == QListView::PositionAtCenter) {
1943             horizontalValue += ((area.width() - rect.width()) / 2) - rect.left();
1944         } else {
1945             if (leftOf)
1946                 horizontalValue -= rect.left();
1947             else if (rightOf)
1948                 horizontalValue += qMin(rect.left(), area.width() - rect.right());
1949         }
1950     } else {
1951         if (hint == QListView::PositionAtCenter) {
1952             horizontalValue += rect.left() - ((area.width()- rect.width()) / 2);
1953         } else {
1954             if (leftOf)
1955                 horizontalValue += rect.left();
1956             else if (rightOf)
1957                 horizontalValue += qMin(rect.left(), rect.right() - area.width());
1958         }
1959     }
1960     return horizontalValue;
1961 }
1962 
1963 /*
1964  * ListMode ListView Implementation
1965 */
1966 
1967 #ifndef QT_NO_DRAGANDDROP
position(const QPoint & pos,const QRect & rect,const QModelIndex & index) const1968 QAbstractItemView::DropIndicatorPosition QListModeViewBase::position(const QPoint &pos, const QRect &rect, const QModelIndex &index) const
1969 {
1970     QAbstractItemView::DropIndicatorPosition r = QAbstractItemView::OnViewport;
1971     if (!dd->overwrite) {
1972         const int margin = 2;
1973         if (pos.x() - rect.left() < margin) {
1974             r = QAbstractItemView::AboveItem;   // Visually, on the left
1975         } else if (rect.right() - pos.x() < margin) {
1976             r = QAbstractItemView::BelowItem;   // Visually, on the right
1977         } else if (rect.contains(pos, true)) {
1978             r = QAbstractItemView::OnItem;
1979         }
1980     } else {
1981         QRect touchingRect = rect;
1982         touchingRect.adjust(-1, -1, 1, 1);
1983         if (touchingRect.contains(pos, false)) {
1984             r = QAbstractItemView::OnItem;
1985         }
1986     }
1987 
1988     if (r == QAbstractItemView::OnItem && (!(dd->model->flags(index) & Qt::ItemIsDropEnabled)))
1989         r = pos.x() < rect.center().x() ? QAbstractItemView::AboveItem : QAbstractItemView::BelowItem;
1990 
1991     return r;
1992 }
1993 
dragMoveEvent(QDragMoveEvent * event)1994 void QListModeViewBase::dragMoveEvent(QDragMoveEvent *event)
1995 {
1996     if (qq->dragDropMode() == QAbstractItemView::InternalMove
1997         && (event->source() != qq || !(event->possibleActions() & Qt::MoveAction)))
1998         return;
1999 
2000     // ignore by default
2001     event->ignore();
2002 
2003     // can't use indexAt, doesn't account for spacing.
2004     QPoint p = event->pos();
2005     QRect rect(p.x() + horizontalOffset(), p.y() + verticalOffset(), 1, 1);
2006     rect.adjust(-dd->spacing(), -dd->spacing(), dd->spacing(), dd->spacing());
2007     const QVector<QModelIndex> intersectVector = dd->intersectingSet(rect);
2008     QModelIndex index = intersectVector.count() > 0
2009                         ? intersectVector.last() : QModelIndex();
2010     dd->hover = index;
2011     if (!dd->droppingOnItself(event, index)
2012         && dd->canDecode(event)) {
2013 
2014         if (index.isValid() && dd->showDropIndicator) {
2015             QRect rect = qq->visualRect(index);
2016             dd->dropIndicatorPosition = position(event->pos(), rect, index);
2017             // if spacing, should try to draw between items, not just next to item.
2018             switch (dd->dropIndicatorPosition) {
2019             case QAbstractItemView::AboveItem:
2020                 if (dd->isIndexDropEnabled(index.parent())) {
2021                     dd->dropIndicatorRect = QRect(rect.left()-dd->spacing(), rect.top(), 0, rect.height());
2022                     event->accept();
2023                 } else {
2024                     dd->dropIndicatorRect = QRect();
2025                 }
2026                 break;
2027             case QAbstractItemView::BelowItem:
2028                 if (dd->isIndexDropEnabled(index.parent())) {
2029                     dd->dropIndicatorRect = QRect(rect.right()+dd->spacing(), rect.top(), 0, rect.height());
2030                     event->accept();
2031                 } else {
2032                     dd->dropIndicatorRect = QRect();
2033                 }
2034                 break;
2035             case QAbstractItemView::OnItem:
2036                 if (dd->isIndexDropEnabled(index)) {
2037                     dd->dropIndicatorRect = rect;
2038                     event->accept();
2039                 } else {
2040                     dd->dropIndicatorRect = QRect();
2041                 }
2042                 break;
2043             case QAbstractItemView::OnViewport:
2044                 dd->dropIndicatorRect = QRect();
2045                 if (dd->isIndexDropEnabled(qq->rootIndex())) {
2046                     event->accept(); // allow dropping in empty areas
2047                 }
2048                 break;
2049             }
2050         } else {
2051             dd->dropIndicatorRect = QRect();
2052             dd->dropIndicatorPosition = QAbstractItemView::OnViewport;
2053             if (dd->isIndexDropEnabled(qq->rootIndex())) {
2054                 event->accept(); // allow dropping in empty areas
2055             }
2056         }
2057         dd->viewport->update();
2058     } // can decode
2059 
2060     if (dd->shouldAutoScroll(event->pos()))
2061         qq->startAutoScroll();
2062 }
2063 
2064 /*!
2065     If the event hasn't already been accepted, determines the index to drop on.
2066 
2067     if (row == -1 && col == -1)
2068         // append to this drop index
2069     else
2070         // place at row, col in drop index
2071 
2072     If it returns true a drop can be done, and dropRow, dropCol and dropIndex reflects the position of the drop.
2073     \internal
2074   */
dropOn(QDropEvent * event,int * dropRow,int * dropCol,QModelIndex * dropIndex)2075 bool QListModeViewBase::dropOn(QDropEvent *event, int *dropRow, int *dropCol, QModelIndex *dropIndex)
2076 {
2077     if (event->isAccepted())
2078         return false;
2079 
2080     QModelIndex index;
2081     if (dd->viewport->rect().contains(event->pos())) {
2082         // can't use indexAt, doesn't account for spacing.
2083         QPoint p = event->pos();
2084         QRect rect(p.x() + horizontalOffset(), p.y() + verticalOffset(), 1, 1);
2085         rect.adjust(-dd->spacing(), -dd->spacing(), dd->spacing(), dd->spacing());
2086         const QVector<QModelIndex> intersectVector = dd->intersectingSet(rect);
2087         index = intersectVector.count() > 0
2088             ? intersectVector.last() : QModelIndex();
2089         if (!index.isValid())
2090             index = dd->root;
2091     }
2092 
2093     // If we are allowed to do the drop
2094     if (dd->model->supportedDropActions() & event->dropAction()) {
2095         int row = -1;
2096         int col = -1;
2097         if (index != dd->root) {
2098             dd->dropIndicatorPosition = position(event->pos(), qq->visualRect(index), index);
2099             switch (dd->dropIndicatorPosition) {
2100             case QAbstractItemView::AboveItem:
2101                 row = index.row();
2102                 col = index.column();
2103                 index = index.parent();
2104                 break;
2105             case QAbstractItemView::BelowItem:
2106                 row = index.row() + 1;
2107                 col = index.column();
2108                 index = index.parent();
2109                 break;
2110             case QAbstractItemView::OnItem:
2111             case QAbstractItemView::OnViewport:
2112                 break;
2113             }
2114         } else {
2115             dd->dropIndicatorPosition = QAbstractItemView::OnViewport;
2116         }
2117         *dropIndex = index;
2118         *dropRow = row;
2119         *dropCol = col;
2120         if (!dd->droppingOnItself(event, index))
2121             return true;
2122     }
2123     return false;
2124 }
2125 
2126 #endif //QT_NO_DRAGANDDROP
2127 
updateVerticalScrollBar(const QSize & step)2128 void QListModeViewBase::updateVerticalScrollBar(const QSize &step)
2129 {
2130     if (verticalScrollMode() == QAbstractItemView::ScrollPerItem
2131         && ((flow() == QListView::TopToBottom && !isWrapping())
2132         || (flow() == QListView::LeftToRight && isWrapping()))) {
2133             const int steps = (flow() == QListView::TopToBottom ? scrollValueMap : segmentPositions).count() - 1;
2134             if (steps > 0) {
2135                 const int pageSteps = perItemScrollingPageSteps(viewport()->height(), contentsSize.height(), isWrapping());
2136                 verticalScrollBar()->setSingleStep(1);
2137                 verticalScrollBar()->setPageStep(pageSteps);
2138                 verticalScrollBar()->setRange(0, steps - pageSteps);
2139             } else {
2140                 verticalScrollBar()->setRange(0, 0);
2141             }
2142             // } else if (vertical && d->isWrapping() && d->movement == Static) {
2143             // ### wrapped scrolling in flow direction
2144     } else {
2145         QCommonListViewBase::updateVerticalScrollBar(step);
2146     }
2147 }
2148 
updateHorizontalScrollBar(const QSize & step)2149 void QListModeViewBase::updateHorizontalScrollBar(const QSize &step)
2150 {
2151     if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem
2152         && ((flow() == QListView::TopToBottom && isWrapping())
2153         || (flow() == QListView::LeftToRight && !isWrapping()))) {
2154             int steps = (flow() == QListView::TopToBottom ? segmentPositions : scrollValueMap).count() - 1;
2155             if (steps > 0) {
2156                 const int pageSteps = perItemScrollingPageSteps(viewport()->width(), contentsSize.width(), isWrapping());
2157                 horizontalScrollBar()->setSingleStep(1);
2158                 horizontalScrollBar()->setPageStep(pageSteps);
2159                 horizontalScrollBar()->setRange(0, steps - pageSteps);
2160             } else {
2161                 horizontalScrollBar()->setRange(0, 0);
2162             }
2163     } else {
2164         QCommonListViewBase::updateHorizontalScrollBar(step);
2165     }
2166 }
2167 
verticalScrollToValue(int index,QListView::ScrollHint hint,bool above,bool below,const QRect & area,const QRect & rect) const2168 int QListModeViewBase::verticalScrollToValue(int index, QListView::ScrollHint hint,
2169                                           bool above, bool below, const QRect &area, const QRect &rect) const
2170 {
2171     if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
2172         int value;
2173         if (scrollValueMap.isEmpty()) {
2174             value = 0;
2175         } else {
2176             int scrollBarValue = verticalScrollBar()->value();
2177             int numHidden = 0;
2178             for (int i = 0; i < flowPositions.count() - 1 && i <= scrollBarValue; ++i)
2179                 if (isHidden(i))
2180                     ++numHidden;
2181             value = qBound(0, scrollValueMap.at(verticalScrollBar()->value()) - numHidden, flowPositions.count() - 1);
2182         }
2183         if (above)
2184             hint = QListView::PositionAtTop;
2185         else if (below)
2186             hint = QListView::PositionAtBottom;
2187         if (hint == QListView::EnsureVisible)
2188             return value;
2189 
2190         return perItemScrollToValue(index, value, area.height(), hint, Qt::Vertical, isWrapping(), rect.height());
2191     }
2192 
2193     return QCommonListViewBase::verticalScrollToValue(index, hint, above, below, area, rect);
2194 }
2195 
horizontalOffset() const2196 int QListModeViewBase::horizontalOffset() const
2197 {
2198     if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
2199         if (isWrapping()) {
2200             if (flow() == QListView::TopToBottom && !segmentPositions.isEmpty()) {
2201                 const int max = segmentPositions.count() - 1;
2202                 int currentValue = qBound(0, horizontalScrollBar()->value(), max);
2203                 int position = segmentPositions.at(currentValue);
2204                 int maximumValue = qBound(0, horizontalScrollBar()->maximum(), max);
2205                 int maximum = segmentPositions.at(maximumValue);
2206                 return (isRightToLeft() ? maximum - position : position);
2207             }
2208         } else if (flow() == QListView::LeftToRight && !flowPositions.isEmpty()) {
2209             int position = flowPositions.at(scrollValueMap.at(horizontalScrollBar()->value()));
2210             int maximum = flowPositions.at(scrollValueMap.at(horizontalScrollBar()->maximum()));
2211             return (isRightToLeft() ? maximum - position : position);
2212         }
2213     }
2214     return QCommonListViewBase::horizontalOffset();
2215 }
2216 
verticalOffset() const2217 int QListModeViewBase::verticalOffset() const
2218 {
2219     if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
2220         if (isWrapping()) {
2221             if (flow() == QListView::LeftToRight && !segmentPositions.isEmpty()) {
2222                 int value = verticalScrollBar()->value();
2223                 if (value >= segmentPositions.count())
2224                     return 0;
2225                 return segmentPositions.at(value) - spacing();
2226             }
2227         } else if (flow() == QListView::TopToBottom && !flowPositions.isEmpty()) {
2228             int value = verticalScrollBar()->value();
2229             if (value > scrollValueMap.count())
2230                 return 0;
2231             return flowPositions.at(scrollValueMap.at(value)) - spacing();
2232         }
2233     }
2234     return QCommonListViewBase::verticalOffset();
2235 }
2236 
horizontalScrollToValue(int index,QListView::ScrollHint hint,bool leftOf,bool rightOf,const QRect & area,const QRect & rect) const2237 int QListModeViewBase::horizontalScrollToValue(int index, QListView::ScrollHint hint,
2238                                             bool leftOf, bool rightOf, const QRect &area, const QRect &rect) const
2239 {
2240     if (horizontalScrollMode() != QAbstractItemView::ScrollPerItem)
2241         return QCommonListViewBase::horizontalScrollToValue(index, hint, leftOf, rightOf, area, rect);
2242 
2243     int value;
2244     if (scrollValueMap.isEmpty())
2245         value = 0;
2246     else
2247         value = qBound(0, scrollValueMap.at(horizontalScrollBar()->value()), flowPositions.count() - 1);
2248     if (leftOf)
2249         hint = QListView::PositionAtTop;
2250     else if (rightOf)
2251         hint = QListView::PositionAtBottom;
2252     if (hint == QListView::EnsureVisible)
2253         return value;
2254 
2255     return perItemScrollToValue(index, value, area.width(), hint, Qt::Horizontal, isWrapping(), rect.width());
2256 }
2257 
scrollContentsBy(int dx,int dy,bool scrollElasticBand)2258 void QListModeViewBase::scrollContentsBy(int dx, int dy, bool scrollElasticBand)
2259 {
2260     // ### reorder this logic
2261     const int verticalValue = verticalScrollBar()->value();
2262     const int horizontalValue = horizontalScrollBar()->value();
2263     const bool vertical = (verticalScrollMode() == QAbstractItemView::ScrollPerItem);
2264     const bool horizontal = (horizontalScrollMode() == QAbstractItemView::ScrollPerItem);
2265 
2266     if (isWrapping()) {
2267         if (segmentPositions.isEmpty())
2268             return;
2269         const int max = segmentPositions.count() - 1;
2270         if (horizontal && flow() == QListView::TopToBottom && dx != 0) {
2271             int currentValue = qBound(0, horizontalValue, max);
2272             int previousValue = qBound(0, currentValue + dx, max);
2273             int currentCoordinate = segmentPositions.at(currentValue) - spacing();
2274             int previousCoordinate = segmentPositions.at(previousValue) - spacing();
2275             dx = previousCoordinate - currentCoordinate;
2276         } else if (vertical && flow() == QListView::LeftToRight && dy != 0) {
2277             int currentValue = qBound(0, verticalValue, max);
2278             int previousValue = qBound(0, currentValue + dy, max);
2279             int currentCoordinate = segmentPositions.at(currentValue) - spacing();
2280             int previousCoordinate = segmentPositions.at(previousValue) - spacing();
2281             dy = previousCoordinate - currentCoordinate;
2282         }
2283     } else {
2284         if (flowPositions.isEmpty())
2285             return;
2286         const int max = scrollValueMap.count() - 1;
2287         if (vertical && flow() == QListView::TopToBottom && dy != 0) {
2288             int currentValue = qBound(0, verticalValue, max);
2289             int previousValue = qBound(0, currentValue + dy, max);
2290             int currentCoordinate = flowPositions.at(scrollValueMap.at(currentValue));
2291             int previousCoordinate = flowPositions.at(scrollValueMap.at(previousValue));
2292             dy = previousCoordinate - currentCoordinate;
2293         } else if (horizontal && flow() == QListView::LeftToRight && dx != 0) {
2294             int currentValue = qBound(0, horizontalValue, max);
2295             int previousValue = qBound(0, currentValue + dx, max);
2296             int currentCoordinate = flowPositions.at(scrollValueMap.at(currentValue));
2297             int previousCoordinate = flowPositions.at(scrollValueMap.at(previousValue));
2298             dx = previousCoordinate - currentCoordinate;
2299         }
2300     }
2301     QCommonListViewBase::scrollContentsBy(dx, dy, scrollElasticBand);
2302 }
2303 
doBatchedItemLayout(const QListViewLayoutInfo & info,int max)2304 bool QListModeViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max)
2305 {
2306     doStaticLayout(info);
2307     if (batchStartRow > max) { // stop items layout
2308         flowPositions.resize(flowPositions.count());
2309         segmentPositions.resize(segmentPositions.count());
2310         segmentStartRows.resize(segmentStartRows.count());
2311         return true; // done
2312     }
2313     return false; // not done
2314 }
2315 
indexToListViewItem(const QModelIndex & index) const2316 QListViewItem QListModeViewBase::indexToListViewItem(const QModelIndex &index) const
2317 {
2318     if (flowPositions.isEmpty()
2319         || segmentPositions.isEmpty()
2320         || index.row() >= flowPositions.count())
2321         return QListViewItem();
2322 
2323     const int segment = qBinarySearch<int>(segmentStartRows, index.row(),
2324                                            0, segmentStartRows.count() - 1);
2325 
2326 
2327     QStyleOptionViewItemV4 options = viewOptions();
2328     options.rect.setSize(contentsSize);
2329     QSize size = (uniformItemSizes() && cachedItemSize().isValid())
2330                  ? cachedItemSize() : itemSize(options, index);
2331 
2332     QPoint pos;
2333     if (flow() == QListView::LeftToRight) {
2334         pos.setX(flowPositions.at(index.row()));
2335         pos.setY(segmentPositions.at(segment));
2336     } else { // TopToBottom
2337         pos.setY(flowPositions.at(index.row()));
2338         pos.setX(segmentPositions.at(segment));
2339         if (isWrapping()) { // make the items as wide as the segment
2340             int right = (segment + 1 >= segmentPositions.count()
2341                      ? contentsSize.width()
2342                      : segmentPositions.at(segment + 1));
2343             size.setWidth(right - pos.x());
2344         } else { // make the items as wide as the viewport
2345             size.setWidth(qMax(size.width(), viewport()->width() - 2 * spacing()));
2346         }
2347     }
2348 
2349     return QListViewItem(QRect(pos, size), index.row());
2350 }
2351 
initStaticLayout(const QListViewLayoutInfo & info)2352 QPoint QListModeViewBase::initStaticLayout(const QListViewLayoutInfo &info)
2353 {
2354     int x, y;
2355     if (info.first == 0) {
2356         flowPositions.clear();
2357         segmentPositions.clear();
2358         segmentStartRows.clear();
2359         segmentExtents.clear();
2360         scrollValueMap.clear();
2361         x = info.bounds.left() + info.spacing;
2362         y = info.bounds.top() + info.spacing;
2363         segmentPositions.append(info.flow == QListView::LeftToRight ? y : x);
2364         segmentStartRows.append(0);
2365     } else if (info.wrap) {
2366         if (info.flow == QListView::LeftToRight) {
2367             x = batchSavedPosition;
2368             y = segmentPositions.last();
2369         } else { // flow == QListView::TopToBottom
2370             x = segmentPositions.last();
2371             y = batchSavedPosition;
2372         }
2373     } else { // not first and not wrap
2374         if (info.flow == QListView::LeftToRight) {
2375             x = batchSavedPosition;
2376             y = info.bounds.top() + info.spacing;
2377         } else { // flow == QListView::TopToBottom
2378             x = info.bounds.left() + info.spacing;
2379             y = batchSavedPosition;
2380         }
2381     }
2382     return QPoint(x, y);
2383 }
2384 
2385 /*!
2386   \internal
2387 */
doStaticLayout(const QListViewLayoutInfo & info)2388 void QListModeViewBase::doStaticLayout(const QListViewLayoutInfo &info)
2389 {
2390     const bool useItemSize = !info.grid.isValid();
2391     const QPoint topLeft = initStaticLayout(info);
2392     QStyleOptionViewItemV4 option = viewOptions();
2393     option.rect = info.bounds;
2394     option.rect.adjust(info.spacing, info.spacing, -info.spacing, -info.spacing);
2395 
2396     // The static layout data structures are as follows:
2397     // One vector contains the coordinate in the direction of layout flow.
2398     // Another vector contains the coordinates of the segments.
2399     // A third vector contains the index (model row) of the first item
2400     // of each segment.
2401 
2402     int segStartPosition;
2403     int segEndPosition;
2404     int deltaFlowPosition;
2405     int deltaSegPosition;
2406     int deltaSegHint;
2407     int flowPosition;
2408     int segPosition;
2409 
2410     if (info.flow == QListView::LeftToRight) {
2411         segStartPosition = info.bounds.left();
2412         segEndPosition = info.bounds.width();
2413         flowPosition = topLeft.x();
2414         segPosition = topLeft.y();
2415         deltaFlowPosition = info.grid.width(); // dx
2416         deltaSegPosition = useItemSize ? batchSavedDeltaSeg : info.grid.height(); // dy
2417         deltaSegHint = info.grid.height();
2418     } else { // flow == QListView::TopToBottom
2419         segStartPosition = info.bounds.top();
2420         segEndPosition = info.bounds.height();
2421         flowPosition = topLeft.y();
2422         segPosition = topLeft.x();
2423         deltaFlowPosition = info.grid.height(); // dy
2424         deltaSegPosition = useItemSize ? batchSavedDeltaSeg : info.grid.width(); // dx
2425         deltaSegHint = info.grid.width();
2426     }
2427 
2428     for (int row = info.first; row <= info.last; ++row) {
2429         if (isHidden(row)) { // ###
2430             flowPositions.append(flowPosition);
2431         } else {
2432             // if we are not using a grid, we need to find the deltas
2433             if (useItemSize) {
2434                 QSize hint = itemSize(option, modelIndex(row));
2435                 if (info.flow == QListView::LeftToRight) {
2436                     deltaFlowPosition = hint.width() + info.spacing;
2437                     deltaSegHint = hint.height() + info.spacing;
2438                 } else { // TopToBottom
2439                     deltaFlowPosition = hint.height() + info.spacing;
2440                     deltaSegHint = hint.width() + info.spacing;
2441                 }
2442             }
2443             // create new segment
2444             if (info.wrap && (flowPosition + deltaFlowPosition >= segEndPosition)) {
2445                 segmentExtents.append(flowPosition);
2446                 flowPosition = info.spacing + segStartPosition;
2447                 segPosition += deltaSegPosition;
2448                 if (info.wrap)
2449                     segPosition += info.spacing;
2450                 segmentPositions.append(segPosition);
2451                 segmentStartRows.append(row);
2452                 deltaSegPosition = 0;
2453             }
2454             // save the flow position of this item
2455             scrollValueMap.append(flowPositions.count());
2456             flowPositions.append(flowPosition);
2457             // prepare for the next item
2458             deltaSegPosition = qMax(deltaSegHint, deltaSegPosition);
2459             flowPosition += info.spacing + deltaFlowPosition;
2460         }
2461     }
2462     // used when laying out next batch
2463     batchSavedPosition = flowPosition;
2464     batchSavedDeltaSeg = deltaSegPosition;
2465     batchStartRow = info.last + 1;
2466     if (info.last == info.max)
2467         flowPosition -= info.spacing; // remove extra spacing
2468     // set the contents size
2469     QRect rect = info.bounds;
2470     if (info.flow == QListView::LeftToRight) {
2471         rect.setRight(segmentPositions.count() == 1 ? flowPosition : info.bounds.right());
2472         rect.setBottom(segPosition + deltaSegPosition);
2473     } else { // TopToBottom
2474         rect.setRight(segPosition + deltaSegPosition);
2475         rect.setBottom(segmentPositions.count() == 1 ? flowPosition : info.bounds.bottom());
2476     }
2477     contentsSize = QSize(rect.right(), rect.bottom());
2478     // if it is the last batch, save the end of the segments
2479     if (info.last == info.max) {
2480         segmentExtents.append(flowPosition);
2481         scrollValueMap.append(flowPositions.count());
2482         flowPositions.append(flowPosition);
2483         segmentPositions.append(info.wrap ? segPosition + deltaSegPosition : INT_MAX);
2484     }
2485     // if the new items are visble, update the viewport
2486     QRect changedRect(topLeft, rect.bottomRight());
2487     if (clipRect().intersects(changedRect))
2488         viewport()->update();
2489 }
2490 
2491 /*!
2492   \internal
2493   Finds the set of items intersecting with \a area.
2494   In this function, itemsize is counted from topleft to the start of the next item.
2495 */
intersectingSet(const QRect & area) const2496 QVector<QModelIndex> QListModeViewBase::intersectingSet(const QRect &area) const
2497 {
2498     QVector<QModelIndex> ret;
2499     int segStartPosition;
2500     int segEndPosition;
2501     int flowStartPosition;
2502     int flowEndPosition;
2503     if (flow() == QListView::LeftToRight) {
2504         segStartPosition = area.top();
2505         segEndPosition = area.bottom();
2506         flowStartPosition = area.left();
2507         flowEndPosition = area.right();
2508     } else {
2509         segStartPosition = area.left();
2510         segEndPosition = area.right();
2511         flowStartPosition = area.top();
2512         flowEndPosition = area.bottom();
2513     }
2514     if (segmentPositions.count() < 2 || flowPositions.isEmpty())
2515         return ret;
2516     // the last segment position is actually the edge of the last segment
2517     const int segLast = segmentPositions.count() - 2;
2518     int seg = qBinarySearch<int>(segmentPositions, segStartPosition, 0, segLast + 1);
2519     for (; seg <= segLast && segmentPositions.at(seg) <= segEndPosition; ++seg) {
2520         int first = segmentStartRows.at(seg);
2521         int last = (seg < segLast ? segmentStartRows.at(seg + 1) : batchStartRow) - 1;
2522         if (segmentExtents.at(seg) < flowStartPosition)
2523             continue;
2524         int row = qBinarySearch<int>(flowPositions, flowStartPosition, first, last);
2525         for (; row <= last && flowPositions.at(row) <= flowEndPosition; ++row) {
2526             if (isHidden(row))
2527                 continue;
2528             QModelIndex index = modelIndex(row);
2529             if (index.isValid())
2530                 ret += index;
2531 #if 0 // for debugging
2532             else
2533                 qWarning("intersectingSet: row %d was invalid", row);
2534 #endif
2535         }
2536     }
2537     return ret;
2538 }
2539 
dataChanged(const QModelIndex &,const QModelIndex &)2540 void QListModeViewBase::dataChanged(const QModelIndex &, const QModelIndex &)
2541 {
2542     dd->doDelayedItemsLayout();
2543 }
2544 
2545 
mapToViewport(const QRect & rect) const2546 QRect QListModeViewBase::mapToViewport(const QRect &rect) const
2547 {
2548     if (isWrapping())
2549         return rect;
2550     // If the listview is in "listbox-mode", the items are as wide as the view.
2551     // But we don't shrink the items.
2552     QRect result = rect;
2553     if (flow() == QListView::TopToBottom) {
2554         result.setLeft(spacing());
2555         result.setWidth(qMax(rect.width(), qMax(contentsSize.width(), viewport()->width()) - 2 * spacing()));
2556     } else { // LeftToRight
2557         result.setTop(spacing());
2558         result.setHeight(qMax(rect.height(), qMax(contentsSize.height(), viewport()->height()) - 2 * spacing()));
2559     }
2560     return result;
2561 }
2562 
perItemScrollingPageSteps(int length,int bounds,bool wrap) const2563 int QListModeViewBase::perItemScrollingPageSteps(int length, int bounds, bool wrap) const
2564 {
2565     QVector<int> positions;
2566     if (wrap)
2567         positions = segmentPositions;
2568     else if (!flowPositions.isEmpty()) {
2569         positions.reserve(scrollValueMap.size());
2570         foreach (int itemShown, scrollValueMap)
2571             positions.append(flowPositions.at(itemShown));
2572     }
2573     if (positions.isEmpty() || bounds <= length)
2574         return positions.count();
2575     if (uniformItemSizes()) {
2576         for (int i = 1; i < positions.count(); ++i)
2577             if (positions.at(i) > 0)
2578                 return length / positions.at(i);
2579         return 0; // all items had height 0
2580     }
2581     int pageSteps = 0;
2582     int steps = positions.count() - 1;
2583     int max = qMax(length, bounds);
2584     int min = qMin(length, bounds);
2585     int pos = min - (max - positions.last());
2586 
2587     while (pos >= 0 && steps > 0) {
2588         pos -= (positions.at(steps) - positions.at(steps - 1));
2589         if (pos >= 0) //this item should be visible
2590             ++pageSteps;
2591         --steps;
2592     }
2593 
2594     // at this point we know that positions has at least one entry
2595     return qMax(pageSteps, 1);
2596 }
2597 
perItemScrollToValue(int index,int scrollValue,int viewportSize,QAbstractItemView::ScrollHint hint,Qt::Orientation orientation,bool wrap,int itemExtent) const2598 int QListModeViewBase::perItemScrollToValue(int index, int scrollValue, int viewportSize,
2599                                                  QAbstractItemView::ScrollHint hint,
2600                                                  Qt::Orientation orientation, bool wrap, int itemExtent) const
2601 {
2602     if (index < 0)
2603         return scrollValue;
2604 
2605     QVector<int> visibleFlowPositions;
2606     visibleFlowPositions.reserve(flowPositions.count() - 1);
2607     for (int i = 0; i < flowPositions.count() - 1; i++) { // flowPositions count is +1 larger than actual row count
2608         if (!isHidden(i))
2609             visibleFlowPositions.append(flowPositions.at(i));
2610     }
2611 
2612     if (!wrap) {
2613         int topIndex = index;
2614         const int bottomIndex = topIndex;
2615         const int bottomCoordinate = visibleFlowPositions.at(index);
2616 
2617         while (topIndex > 0 &&
2618                (bottomCoordinate - visibleFlowPositions.at(topIndex - 1) + itemExtent) <= (viewportSize)) {
2619             topIndex--;
2620         }
2621 
2622         const int itemCount = bottomIndex - topIndex + 1;
2623         switch (hint) {
2624         case QAbstractItemView::PositionAtTop:
2625             return index;
2626         case QAbstractItemView::PositionAtBottom:
2627             return index - itemCount + 1;
2628         case QAbstractItemView::PositionAtCenter:
2629             return index - (itemCount / 2);
2630         default:
2631             break;
2632         }
2633     } else { // wrapping
2634         Qt::Orientation flowOrientation = (flow() == QListView::LeftToRight
2635                                            ? Qt::Horizontal : Qt::Vertical);
2636         if (flowOrientation == orientation) { // scrolling in the "flow" direction
2637             // ### wrapped scrolling in the flow direction
2638             return visibleFlowPositions.at(index); // ### always pixel based for now
2639         } else if (!segmentStartRows.isEmpty()) { // we are scrolling in the "segment" direction
2640             int segment = qBinarySearch<int>(segmentStartRows, index, 0, segmentStartRows.count() - 1);
2641             int leftSegment = segment;
2642             const int rightSegment = leftSegment;
2643             const int bottomCoordinate = segmentPositions.at(segment);
2644 
2645             while (leftSegment > scrollValue &&
2646                 (bottomCoordinate - segmentPositions.at(leftSegment-1) + itemExtent) <= (viewportSize)) {
2647                     leftSegment--;
2648             }
2649 
2650             const int segmentCount = rightSegment - leftSegment + 1;
2651             switch (hint) {
2652             case QAbstractItemView::PositionAtTop:
2653                 return segment;
2654             case QAbstractItemView::PositionAtBottom:
2655                 return segment - segmentCount + 1;
2656             case QAbstractItemView::PositionAtCenter:
2657                 return segment - (segmentCount / 2);
2658             default:
2659                 break;
2660             }
2661         }
2662     }
2663     return scrollValue;
2664 }
2665 
clear()2666 void QListModeViewBase::clear()
2667 {
2668     flowPositions.clear();
2669     segmentPositions.clear();
2670     segmentStartRows.clear();
2671     segmentExtents.clear();
2672     batchSavedPosition = 0;
2673     batchStartRow = 0;
2674     batchSavedDeltaSeg = 0;
2675 }
2676 
2677 /*
2678  * IconMode ListView Implementation
2679 */
2680 
setPositionForIndex(const QPoint & position,const QModelIndex & index)2681 void QIconModeViewBase::setPositionForIndex(const QPoint &position, const QModelIndex &index)
2682 {
2683     if (index.row() >= items.count())
2684         return;
2685     const QSize oldContents = contentsSize;
2686     qq->update(index); // update old position
2687     moveItem(index.row(), position);
2688     qq->update(index); // update new position
2689 
2690     if (contentsSize != oldContents)
2691         dd->viewUpdateGeometries(); // update the scroll bars
2692 }
2693 
appendHiddenRow(int row)2694 void QIconModeViewBase::appendHiddenRow(int row)
2695 {
2696     if (row >= 0 && row < items.count()) //remove item
2697         tree.removeLeaf(items.at(row).rect(), row);
2698     QCommonListViewBase::appendHiddenRow(row);
2699 }
2700 
removeHiddenRow(int row)2701 void QIconModeViewBase::removeHiddenRow(int row)
2702 {
2703     QCommonListViewBase::removeHiddenRow(row);
2704     if (row >= 0 && row < items.count()) //insert item
2705         tree.insertLeaf(items.at(row).rect(), row);
2706 }
2707 
2708 #ifndef QT_NO_DRAGANDDROP
filterStartDrag(Qt::DropActions supportedActions)2709 bool QIconModeViewBase::filterStartDrag(Qt::DropActions supportedActions)
2710 {
2711     // This function does the same thing as in QAbstractItemView::startDrag(),
2712     // plus adding viewitems to the draggedItems list.
2713     // We need these items to draw the drag items
2714     QModelIndexList indexes = dd->selectionModel->selectedIndexes();
2715     if (indexes.count() > 0 ) {
2716         if (viewport()->acceptDrops()) {
2717             QModelIndexList::ConstIterator it = indexes.constBegin();
2718             for (; it != indexes.constEnd(); ++it)
2719                 if (dd->model->flags(*it) & Qt::ItemIsDragEnabled
2720                     && (*it).column() == dd->column)
2721                     draggedItems.push_back(*it);
2722         }
2723 
2724         QRect rect;
2725         QPixmap pixmap = dd->renderToPixmap(indexes, &rect);
2726         rect.adjust(horizontalOffset(), verticalOffset(), 0, 0);
2727 
2728         QDrag *drag = new QDrag(qq);
2729         drag->setPixmap(pixmap);
2730         drag->setHotSpot(dd->pressedPosition - rect.topLeft());
2731         drag->setMimeData(dd->model->mimeData(indexes));
2732         Qt::DropAction action = drag->exec(supportedActions, Qt::CopyAction);
2733         draggedItems.clear();
2734         if (action == Qt::MoveAction)
2735             dd->clearOrRemove();
2736     }
2737     return true;
2738 }
2739 
filterDropEvent(QDropEvent * e)2740 bool QIconModeViewBase::filterDropEvent(QDropEvent *e)
2741 {
2742     if (e->source() != qq)
2743         return false;
2744 
2745     const QSize contents = contentsSize;
2746     QPoint offset(horizontalOffset(), verticalOffset());
2747     QPoint end = e->pos() + offset;
2748     if (qq->acceptDrops()) {
2749         const Qt::ItemFlags dropableFlags = Qt::ItemIsDropEnabled|Qt::ItemIsEnabled;
2750         const QVector<QModelIndex> &dropIndices = intersectingSet(QRect(end, QSize(1, 1)));
2751         foreach (const QModelIndex &index, dropIndices)
2752             if ((index.flags() & dropableFlags) == dropableFlags)
2753                 return false;
2754     }
2755     QPoint start = dd->pressedPosition;
2756     QPoint delta = (dd->movement == QListView::Snap ? snapToGrid(end) - snapToGrid(start) : end - start);
2757     QList<QModelIndex> indexes = dd->selectionModel->selectedIndexes();
2758     for (int i = 0; i < indexes.count(); ++i) {
2759         QModelIndex index = indexes.at(i);
2760         QRect rect = dd->rectForIndex(index);
2761         viewport()->update(dd->mapToViewport(rect, false));
2762         QPoint dest = rect.topLeft() + delta;
2763         if (qq->isRightToLeft())
2764             dest.setX(dd->flipX(dest.x()) - rect.width());
2765         moveItem(index.row(), dest);
2766         qq->update(index);
2767     }
2768     dd->stopAutoScroll();
2769     draggedItems.clear();
2770     dd->emitIndexesMoved(indexes);
2771     e->accept(); // we have handled the event
2772     // if the size has not grown, we need to check if it has shrinked
2773     if (contentsSize != contents) {
2774         if ((contentsSize.width() <= contents.width()
2775             || contentsSize.height() <= contents.height())) {
2776                 updateContentsSize();
2777         }
2778         dd->viewUpdateGeometries();
2779     }
2780     return true;
2781 }
2782 
filterDragLeaveEvent(QDragLeaveEvent * e)2783 bool QIconModeViewBase::filterDragLeaveEvent(QDragLeaveEvent *e)
2784 {
2785     viewport()->update(draggedItemsRect()); // erase the area
2786     draggedItemsPos = QPoint(-1, -1); // don't draw the dragged items
2787     return QCommonListViewBase::filterDragLeaveEvent(e);
2788 }
2789 
filterDragMoveEvent(QDragMoveEvent * e)2790 bool QIconModeViewBase::filterDragMoveEvent(QDragMoveEvent *e)
2791 {
2792     if (e->source() != qq || !dd->canDecode(e))
2793         return false;
2794 
2795     // ignore by default
2796     e->ignore();
2797     // get old dragged items rect
2798     QRect itemsRect = this->itemsRect(draggedItems);
2799     viewport()->update(itemsRect.translated(draggedItemsDelta()));
2800     // update position
2801     draggedItemsPos = e->pos();
2802     // get new items rect
2803     viewport()->update(itemsRect.translated(draggedItemsDelta()));
2804     // set the item under the cursor to current
2805     QModelIndex index;
2806     if (movement() == QListView::Snap) {
2807         QRect rect(snapToGrid(e->pos() + offset()), gridSize());
2808         const QVector<QModelIndex> intersectVector = intersectingSet(rect);
2809         index = intersectVector.count() > 0 ? intersectVector.last() : QModelIndex();
2810     } else {
2811         index = qq->indexAt(e->pos());
2812     }
2813     // check if we allow drops here
2814     if (draggedItems.contains(index))
2815         e->accept(); // allow changing item position
2816     else if (dd->model->flags(index) & Qt::ItemIsDropEnabled)
2817         e->accept(); // allow dropping on dropenabled items
2818     else if (!index.isValid())
2819         e->accept(); // allow dropping in empty areas
2820 
2821     // the event was treated. do autoscrolling
2822     if (dd->shouldAutoScroll(e->pos()))
2823         dd->startAutoScroll();
2824     return true;
2825 }
2826 #endif // QT_NO_DRAGANDDROP
2827 
setRowCount(int rowCount)2828 void QIconModeViewBase::setRowCount(int rowCount)
2829 {
2830     tree.create(qMax(rowCount - hiddenCount(), 0));
2831 }
2832 
scrollContentsBy(int dx,int dy,bool scrollElasticBand)2833 void QIconModeViewBase::scrollContentsBy(int dx, int dy, bool scrollElasticBand)
2834 {
2835     if (scrollElasticBand)
2836         dd->scrollElasticBandBy(isRightToLeft() ? -dx : dx, dy);
2837 
2838     QCommonListViewBase::scrollContentsBy(dx, dy, scrollElasticBand);
2839     if (!draggedItems.isEmpty())
2840         viewport()->update(draggedItemsRect().translated(dx, dy));
2841 }
2842 
dataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight)2843 void QIconModeViewBase::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
2844 {
2845     if (column() >= topLeft.column() && column() <= bottomRight.column())  {
2846         QStyleOptionViewItemV4 option = viewOptions();
2847         int bottom = qMin(items.count(), bottomRight.row() + 1);
2848         for (int row = topLeft.row(); row < bottom; ++row)
2849             items[row].resize(itemSize(option, modelIndex(row)));
2850     }
2851 }
2852 
doBatchedItemLayout(const QListViewLayoutInfo & info,int max)2853 bool QIconModeViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max)
2854 {
2855     if (info.last >= items.count()) {
2856         //first we create the items
2857         QStyleOptionViewItemV4 option = viewOptions();
2858         for (int row = items.count(); row <= info.last; ++row) {
2859             QSize size = itemSize(option, modelIndex(row));
2860             QListViewItem item(QRect(0, 0, size.width(), size.height()), row); // default pos
2861             items.append(item);
2862         }
2863         doDynamicLayout(info);
2864     }
2865     return (batchStartRow > max); // done
2866 }
2867 
indexToListViewItem(const QModelIndex & index) const2868 QListViewItem QIconModeViewBase::indexToListViewItem(const QModelIndex &index) const
2869 {
2870     if (index.isValid() && index.row() < items.count())
2871         return items.at(index.row());
2872     return QListViewItem();
2873 }
2874 
initBspTree(const QSize & contents)2875 void QIconModeViewBase::initBspTree(const QSize &contents)
2876 {
2877     // remove all items from the tree
2878     int leafCount = tree.leafCount();
2879     for (int l = 0; l < leafCount; ++l)
2880         tree.leaf(l).clear();
2881     // we have to get the bounding rect of the items before we can initialize the tree
2882     QBspTree::Node::Type type = QBspTree::Node::Both; // 2D
2883     // simple heuristics to get better bsp
2884     if (contents.height() / contents.width() >= 3)
2885         type = QBspTree::Node::HorizontalPlane;
2886     else if (contents.width() / contents.height() >= 3)
2887         type = QBspTree::Node::VerticalPlane;
2888     // build tree for the bounding rect (not just the contents rect)
2889     tree.init(QRect(0, 0, contents.width(), contents.height()), type);
2890 }
2891 
initDynamicLayout(const QListViewLayoutInfo & info)2892 QPoint QIconModeViewBase::initDynamicLayout(const QListViewLayoutInfo &info)
2893 {
2894     int x, y;
2895     if (info.first == 0) {
2896         x = info.bounds.x() + info.spacing;
2897         y = info.bounds.y() + info.spacing;
2898         items.reserve(rowCount() - hiddenCount());
2899     } else {
2900         int idx = info.first - 1;
2901         while (idx > 0 && !items.at(idx).isValid())
2902             --idx;
2903         const QListViewItem &item = items.at(idx);
2904         x = item.x;
2905         y = item.y;
2906         if (info.flow == QListView::LeftToRight)
2907             x += (info.grid.isValid() ? info.grid.width() : item.w) + info.spacing;
2908         else
2909             y += (info.grid.isValid() ? info.grid.height() : item.h) + info.spacing;
2910     }
2911     return QPoint(x, y);
2912 }
2913 
2914 /*!
2915   \internal
2916 */
doDynamicLayout(const QListViewLayoutInfo & info)2917 void QIconModeViewBase::doDynamicLayout(const QListViewLayoutInfo &info)
2918 {
2919     const bool useItemSize = !info.grid.isValid();
2920     const QPoint topLeft = initDynamicLayout(info);
2921 
2922     int segStartPosition;
2923     int segEndPosition;
2924     int deltaFlowPosition;
2925     int deltaSegPosition;
2926     int deltaSegHint;
2927     int flowPosition;
2928     int segPosition;
2929 
2930     if (info.flow == QListView::LeftToRight) {
2931         segStartPosition = info.bounds.left() + info.spacing;
2932         segEndPosition = info.bounds.right();
2933         deltaFlowPosition = info.grid.width(); // dx
2934         deltaSegPosition = (useItemSize ? batchSavedDeltaSeg : info.grid.height()); // dy
2935         deltaSegHint = info.grid.height();
2936         flowPosition = topLeft.x();
2937         segPosition = topLeft.y();
2938     } else { // flow == QListView::TopToBottom
2939         segStartPosition = info.bounds.top() + info.spacing;
2940         segEndPosition = info.bounds.bottom();
2941         deltaFlowPosition = info.grid.height(); // dy
2942         deltaSegPosition = (useItemSize ? batchSavedDeltaSeg : info.grid.width()); // dx
2943         deltaSegHint = info.grid.width();
2944         flowPosition = topLeft.y();
2945         segPosition = topLeft.x();
2946     }
2947 
2948     if (moved.count() != items.count())
2949         moved.resize(items.count());
2950 
2951     QRect rect(QPoint(), topLeft);
2952     QListViewItem *item = 0;
2953     for (int row = info.first; row <= info.last; ++row) {
2954         item = &items[row];
2955         if (isHidden(row)) {
2956             item->invalidate();
2957         } else {
2958             // if we are not using a grid, we need to find the deltas
2959             if (useItemSize) {
2960                 if (info.flow == QListView::LeftToRight)
2961                     deltaFlowPosition = item->w + info.spacing;
2962                 else
2963                     deltaFlowPosition = item->h + info.spacing;
2964             } else {
2965                 item->w = qMin<int>(info.grid.width(), item->w);
2966                 item->h = qMin<int>(info.grid.height(), item->h);
2967             }
2968 
2969             // create new segment
2970             if (info.wrap
2971                 && flowPosition + deltaFlowPosition > segEndPosition
2972                 && flowPosition > segStartPosition) {
2973                 flowPosition = segStartPosition;
2974                 segPosition += deltaSegPosition;
2975                 if (useItemSize)
2976                     deltaSegPosition = 0;
2977             }
2978             // We must delay calculation of the seg adjustment, as this item
2979             // may have caused a wrap to occur
2980             if (useItemSize) {
2981                 if (info.flow == QListView::LeftToRight)
2982                     deltaSegHint = item->h + info.spacing;
2983                 else
2984                     deltaSegHint = item->w + info.spacing;
2985                 deltaSegPosition = qMax(deltaSegPosition, deltaSegHint);
2986             }
2987 
2988             // set the position of the item
2989             // ### idealy we should have some sort of alignment hint for the item
2990             // ### (normally that would be a point between the icon and the text)
2991             if (!moved.testBit(row)) {
2992                 if (info.flow == QListView::LeftToRight) {
2993                     if (useItemSize) {
2994                         item->x = flowPosition;
2995                         item->y = segPosition;
2996                     } else { // use grid
2997                         item->x = flowPosition + ((deltaFlowPosition - item->w) / 2);
2998                         item->y = segPosition;
2999                     }
3000                 } else { // TopToBottom
3001                     if (useItemSize) {
3002                         item->y = flowPosition;
3003                         item->x = segPosition;
3004                     } else { // use grid
3005                         item->y = flowPosition + ((deltaFlowPosition - item->h) / 2);
3006                         item->x = segPosition;
3007                     }
3008                 }
3009             }
3010 
3011             // let the contents contain the new item
3012             if (useItemSize)
3013                 rect |= item->rect();
3014             else if (info.flow == QListView::LeftToRight)
3015                 rect |= QRect(flowPosition, segPosition, deltaFlowPosition, deltaSegPosition);
3016             else // flow == TopToBottom
3017                 rect |= QRect(segPosition, flowPosition, deltaSegPosition, deltaFlowPosition);
3018 
3019             // prepare for next item
3020             flowPosition += deltaFlowPosition; // current position + item width + gap
3021         }
3022     }
3023     batchSavedDeltaSeg = deltaSegPosition;
3024     batchStartRow = info.last + 1;
3025     bool done = (info.last >= rowCount() - 1);
3026     // resize the content area
3027     if (done || !info.bounds.contains(item->rect())) {
3028         contentsSize = rect.size();
3029         if (info.flow == QListView::LeftToRight)
3030             contentsSize.rheight() += info.spacing;
3031         else
3032             contentsSize.rwidth() += info.spacing;
3033     }
3034     if (rect.size().isEmpty())
3035         return;
3036     // resize tree
3037     int insertFrom = info.first;
3038     if (done || info.first == 0) {
3039         initBspTree(rect.size());
3040         insertFrom = 0;
3041     }
3042     // insert items in tree
3043     for (int row = insertFrom; row <= info.last; ++row)
3044         tree.insertLeaf(items.at(row).rect(), row);
3045     // if the new items are visble, update the viewport
3046     QRect changedRect(topLeft, rect.bottomRight());
3047     if (clipRect().intersects(changedRect))
3048         viewport()->update();
3049 }
3050 
intersectingSet(const QRect & area) const3051 QVector<QModelIndex> QIconModeViewBase::intersectingSet(const QRect &area) const
3052 {
3053     QIconModeViewBase *that = const_cast<QIconModeViewBase*>(this);
3054     QBspTree::Data data(static_cast<void*>(that));
3055     QVector<QModelIndex> res;
3056     that->interSectingVector = &res;
3057     that->tree.climbTree(area, &QIconModeViewBase::addLeaf, data);
3058     that->interSectingVector = 0;
3059     return res;
3060 }
3061 
itemsRect(const QVector<QModelIndex> & indexes) const3062 QRect QIconModeViewBase::itemsRect(const QVector<QModelIndex> &indexes) const
3063 {
3064     QVector<QModelIndex>::const_iterator it = indexes.begin();
3065     QListViewItem item = indexToListViewItem(*it);
3066     QRect rect(item.x, item.y, item.w, item.h);
3067     for (; it != indexes.end(); ++it) {
3068         item = indexToListViewItem(*it);
3069         rect |= viewItemRect(item);
3070     }
3071     return rect;
3072 }
3073 
itemIndex(const QListViewItem & item) const3074 int QIconModeViewBase::itemIndex(const QListViewItem &item) const
3075 {
3076     if (!item.isValid())
3077         return -1;
3078     int i = item.indexHint;
3079     if (i < items.count()) {
3080         if (items.at(i) == item)
3081             return i;
3082     } else {
3083         i = items.count() - 1;
3084     }
3085 
3086     int j = i;
3087     int c = items.count();
3088     bool a = true;
3089     bool b = true;
3090 
3091     while (a || b) {
3092         if (a) {
3093             if (items.at(i) == item) {
3094                 items.at(i).indexHint = i;
3095                 return i;
3096             }
3097             a = ++i < c;
3098         }
3099         if (b) {
3100             if (items.at(j) == item) {
3101                 items.at(j).indexHint = j;
3102                 return j;
3103             }
3104             b = --j > -1;
3105         }
3106     }
3107     return -1;
3108 }
3109 
addLeaf(QVector<int> & leaf,const QRect & area,uint visited,QBspTree::Data data)3110 void QIconModeViewBase::addLeaf(QVector<int> &leaf, const QRect &area,
3111                                    uint visited, QBspTree::Data data)
3112 {
3113     QListViewItem *vi;
3114     QIconModeViewBase *_this = static_cast<QIconModeViewBase *>(data.ptr);
3115     for (int i = 0; i < leaf.count(); ++i) {
3116         int idx = leaf.at(i);
3117         if (idx < 0 || idx >= _this->items.count())
3118             continue;
3119         vi = &_this->items[idx];
3120         Q_ASSERT(vi);
3121         if (vi->isValid() && vi->rect().intersects(area) && vi->visited != visited) {
3122             QModelIndex index  = _this->dd->listViewItemToIndex(*vi);
3123             Q_ASSERT(index.isValid());
3124             _this->interSectingVector->append(index);
3125             vi->visited = visited;
3126         }
3127     }
3128 }
3129 
moveItem(int index,const QPoint & dest)3130 void QIconModeViewBase::moveItem(int index, const QPoint &dest)
3131 {
3132     // does not impact on the bintree itself or the contents rect
3133     QListViewItem *item = &items[index];
3134     QRect rect = item->rect();
3135 
3136     // move the item without removing it from the tree
3137     tree.removeLeaf(rect, index);
3138     item->move(dest);
3139     tree.insertLeaf(QRect(dest, rect.size()), index);
3140 
3141     // resize the contents area
3142     contentsSize = (QRect(QPoint(0, 0), contentsSize)|QRect(dest, rect.size())).size();
3143 
3144     // mark the item as moved
3145     if (moved.count() != items.count())
3146         moved.resize(items.count());
3147     moved.setBit(index, true);
3148 }
3149 
snapToGrid(const QPoint & pos) const3150 QPoint QIconModeViewBase::snapToGrid(const QPoint &pos) const
3151 {
3152     int x = pos.x() - (pos.x() % gridSize().width());
3153     int y = pos.y() - (pos.y() % gridSize().height());
3154     return QPoint(x, y);
3155 }
3156 
draggedItemsDelta() const3157 QPoint QIconModeViewBase::draggedItemsDelta() const
3158 {
3159     if (movement() == QListView::Snap) {
3160         QPoint snapdelta = QPoint((offset().x() % gridSize().width()),
3161                                   (offset().y() % gridSize().height()));
3162         return snapToGrid(draggedItemsPos + snapdelta) - snapToGrid(pressedPosition()) - snapdelta;
3163     }
3164     return draggedItemsPos - pressedPosition();
3165 }
3166 
draggedItemsRect() const3167 QRect QIconModeViewBase::draggedItemsRect() const
3168 {
3169     QRect rect = itemsRect(draggedItems);
3170     rect.translate(draggedItemsDelta());
3171     return rect;
3172 }
3173 
scrollElasticBandBy(int dx,int dy)3174 void QListViewPrivate::scrollElasticBandBy(int dx, int dy)
3175 {
3176     if (dx > 0) // right
3177         elasticBand.moveRight(elasticBand.right() + dx);
3178     else if (dx < 0) // left
3179         elasticBand.moveLeft(elasticBand.left() - dx);
3180     if (dy > 0) // down
3181         elasticBand.moveBottom(elasticBand.bottom() + dy);
3182     else if (dy < 0) // up
3183         elasticBand.moveTop(elasticBand.top() - dy);
3184 }
3185 
clear()3186 void QIconModeViewBase::clear()
3187 {
3188     tree.destroy();
3189     items.clear();
3190     moved.clear();
3191     batchStartRow = 0;
3192     batchSavedDeltaSeg = 0;
3193 }
3194 
updateContentsSize()3195 void QIconModeViewBase::updateContentsSize()
3196 {
3197     QRect bounding;
3198     for (int i = 0; i < items.count(); ++i)
3199         bounding |= items.at(i).rect();
3200     contentsSize = bounding.size();
3201 }
3202 
3203 /*!
3204   \reimp
3205 */
currentChanged(const QModelIndex & current,const QModelIndex & previous)3206 void QListView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
3207 {
3208 #ifndef QT_NO_ACCESSIBILITY
3209     if (QAccessible::isActive()) {
3210         if (current.isValid()) {
3211             int entry = visualIndex(current) + 1;
3212 #ifdef Q_WS_X11
3213             QAccessible::updateAccessibility(this, entry, QAccessible::Focus);
3214 #else
3215             QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus);
3216 #endif
3217         }
3218     }
3219 #endif
3220     QAbstractItemView::currentChanged(current, previous);
3221 }
3222 
3223 /*!
3224   \reimp
3225 */
selectionChanged(const QItemSelection & selected,const QItemSelection & deselected)3226 void QListView::selectionChanged(const QItemSelection &selected,
3227                                  const QItemSelection &deselected)
3228 {
3229 #ifndef QT_NO_ACCESSIBILITY
3230     if (QAccessible::isActive()) {
3231         // ### does not work properly for selection ranges.
3232         QModelIndex sel = selected.indexes().value(0);
3233         if (sel.isValid()) {
3234             int entry = visualIndex(sel) + 1;
3235 #ifdef Q_WS_X11
3236             QAccessible::updateAccessibility(this, entry, QAccessible::Selection);
3237 #else
3238             QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection);
3239 #endif
3240         }
3241         QModelIndex desel = deselected.indexes().value(0);
3242         if (desel.isValid()) {
3243             int entry = visualIndex(desel) + 1;
3244 #ifdef Q_WS_X11
3245             QAccessible::updateAccessibility(this, entry, QAccessible::SelectionRemove);
3246 #else
3247             QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove);
3248 #endif
3249         }
3250     }
3251 #endif
3252     QAbstractItemView::selectionChanged(selected, deselected);
3253 }
3254 
visualIndex(const QModelIndex & index) const3255 int QListView::visualIndex(const QModelIndex &index) const
3256 {
3257     Q_D(const QListView);
3258     d->executePostedLayout();
3259     QListViewItem itm = d->indexToListViewItem(index);
3260     int visualIndex = d->commonListView->itemIndex(itm);
3261     for (int row = 0; row <= index.row() && visualIndex >= 0; row++) {
3262         if (d->isHidden(row))
3263             visualIndex--;
3264     }
3265     return visualIndex;
3266 }
3267 
3268 QT_END_NAMESPACE
3269 
3270 #endif // QT_NO_LISTVIEW
3271