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 ¤t, 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