source: trunk/src/gui/itemviews/qlistview.cpp@ 5

Last change on this file since 5 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 103.7 KB
Line 
1/***************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
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
60QT_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 \mainclass
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*/
179QListView::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*/
192QListView::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*/
205QListView::~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*/
228void 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
242QListView::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*/
265void QListView::setFlow(Flow flow)
266{
267 Q_D(QListView);
268 d->modeProperties |= uint(QListViewPrivate::Flow);
269 d->flow = flow;
270 d->doDelayedItemsLayout();
271}
272
273QListView::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*/
294void QListView::setWrapping(bool enable)
295{
296 Q_D(QListView);
297 d->modeProperties |= uint(QListViewPrivate::Wrap);
298 d->setWrapping(enable);
299 d->doDelayedItemsLayout();
300}
301
302bool 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*/
320void QListView::setResizeMode(ResizeMode mode)
321{
322 Q_D(QListView);
323 d->modeProperties |= uint(QListViewPrivate::ResizeMode);
324 d->resizeMode = mode;
325}
326
327QListView::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*/
346void QListView::setLayoutMode(LayoutMode mode)
347{
348 Q_D(QListView);
349 d->layoutMode = mode;
350}
351
352QListView::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 between 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)
374void QListView::setSpacing(int space)
375{
376 Q_D(QListView);
377 d->modeProperties |= uint(QListViewPrivate::Spacing);
378 d->setSpacing(space);
379 d->doDelayedItemsLayout();
380}
381
382int 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
398void 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
408int 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*/
429void 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
437QSize 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*/
458void QListView::setViewMode(ViewMode mode)
459{
460 Q_D(QListView);
461 d->viewMode = mode;
462
463 if (mode == ListMode) {
464 delete d->dynamicListView;
465 d->dynamicListView = 0;
466 if (!d->staticListView)
467 d->staticListView = new QStaticListViewBase(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 delete d->staticListView;
484 d->staticListView = 0;
485 if (!d->dynamicListView)
486 d->dynamicListView = new QDynamicListViewBase(this, d);
487 if (!(d->modeProperties & QListViewPrivate::Wrap))
488 d->setWrapping(true);
489 if (!(d->modeProperties & QListViewPrivate::Spacing))
490 d->setSpacing(0);
491 if (!(d->modeProperties & QListViewPrivate::GridSize))
492 d->setGridSize(QSize());
493 if (!(d->modeProperties & QListViewPrivate::Flow))
494 d->flow = LeftToRight;
495 if (!(d->modeProperties & QListViewPrivate::Movement))
496 d->movement = Free;
497 if (!(d->modeProperties & QListViewPrivate::ResizeMode))
498 d->resizeMode = Fixed;
499 if (!(d->modeProperties & QListViewPrivate::SelectionRectVisible))
500 d->showElasticBand = true;
501 }
502
503#ifndef QT_NO_DRAGANDDROP
504 bool movable = (d->movement != Static);
505 setDragEnabled(movable);
506 setAcceptDrops(movable);
507#endif
508 d->clear();
509 d->doDelayedItemsLayout();
510}
511
512QListView::ViewMode QListView::viewMode() const
513{
514 Q_D(const QListView);
515 return d->viewMode;
516}
517
518/*!
519 Clears the QListView-specific property flags. See \l{viewMode}.
520
521 Properties inherited from QAbstractItemView are not covered by the
522 property flags. Specifically, \l{QAbstractItemView::dragEnabled}
523 {dragEnabled} and \l{QAbstractItemView::acceptDrops}
524 {acceptsDrops} are computed by QListView when calling
525 setMovement() or setViewMode().
526*/
527void QListView::clearPropertyFlags()
528{
529 Q_D(QListView);
530 d->modeProperties = 0;
531}
532
533/*!
534 Returns true if the \a row is hidden; otherwise returns false.
535*/
536bool QListView::isRowHidden(int row) const
537{
538 Q_D(const QListView);
539 return d->isHidden(row);
540}
541
542/*!
543 If \a hide is true, the given \a row will be hidden; otherwise
544 the \a row will be shown.
545*/
546void QListView::setRowHidden(int row, bool hide)
547{
548 Q_D(QListView);
549 const bool hidden = d->isHidden(row);
550 if (d->viewMode == ListMode) {
551 if (hide && !hidden)
552 d->hiddenRows.append(d->model->index(row, 0));
553 else if (!hide && hidden)
554 d->hiddenRows.remove(d->hiddenRows.indexOf(d->model->index(row, 0)));
555 d->doDelayedItemsLayout();
556 } else {
557 if (hide && !hidden) {
558 d->dynamicListView->removeItem(row);
559 d->hiddenRows.append(d->model->index(row, 0));
560 } else if (!hide && hidden) {
561 d->hiddenRows.remove(d->hiddenRows.indexOf(d->model->index(row, 0)));
562 d->dynamicListView->insertItem(row);
563 }
564 if (d->resizeMode == Adjust)
565 d->doDelayedItemsLayout();
566 d->viewport->update();
567 }
568}
569
570/*!
571 \reimp
572*/
573QRect QListView::visualRect(const QModelIndex &index) const
574{
575 Q_D(const QListView);
576 return d->mapToViewport(rectForIndex(index), d->viewMode == QListView::ListMode);
577}
578
579/*!
580 \reimp
581*/
582void QListView::scrollTo(const QModelIndex &index, ScrollHint hint)
583{
584 Q_D(QListView);
585
586 if (index.parent() != d->root || index.column() != d->column)
587 return;
588
589 const QRect rect = visualRect(index);
590 if (hint == EnsureVisible && d->viewport->rect().contains(rect)) {
591 d->setDirtyRegion(rect);
592 return;
593 }
594
595 if (d->flow == QListView::TopToBottom || d->isWrapping()) // vertical
596 verticalScrollBar()->setValue(d->verticalScrollToValue(index, rect, hint));
597
598 if (d->flow == QListView::LeftToRight || d->isWrapping()) // horizontal
599 horizontalScrollBar()->setValue(d->horizontalScrollToValue(index, rect, hint));
600}
601
602int QListViewPrivate::horizontalScrollToValue(const QModelIndex &index, const QRect &rect,
603 QListView::ScrollHint hint) const
604{
605 Q_Q(const QListView);
606 const QRect area = viewport->rect();
607 const bool leftOf = q->isRightToLeft()
608 ? (rect.left() < area.left()) && (rect.right() < area.right())
609 : rect.left() < area.left();
610 const bool rightOf = q->isRightToLeft()
611 ? rect.right() > area.right()
612 : (rect.right() > area.right()) && (rect.left() > area.left());
613 int horizontalValue = q->horizontalScrollBar()->value();
614
615 // ScrollPerItem
616 if (q->horizontalScrollMode() == QAbstractItemView::ScrollPerItem && viewMode == QListView::ListMode) {
617 const QListViewItem item = indexToListViewItem(index);
618 const QRect rect = q->visualRect(index);
619 horizontalValue = staticListView->horizontalPerItemValue(itemIndex(item),
620 horizontalValue, area.width(),
621 leftOf, rightOf, isWrapping(), hint, rect.width());
622 } else { // ScrollPerPixel
623 if (q->isRightToLeft()) {
624 if (hint == QListView::PositionAtCenter) {
625 horizontalValue += ((area.width() - rect.width()) / 2) - rect.left();
626 } else {
627 if (leftOf)
628 horizontalValue -= rect.left();
629 else if (rightOf)
630 horizontalValue += qMin(rect.left(), area.width() - rect.right());
631 }
632 } else {
633 if (hint == QListView::PositionAtCenter) {
634 horizontalValue += rect.left() - ((area.width()- rect.width()) / 2);
635 } else {
636 if (leftOf)
637 horizontalValue += rect.left();
638 else if (rightOf)
639 horizontalValue += qMin(rect.left(), rect.right() - area.width());
640 }
641 }
642 }
643 return horizontalValue;
644}
645
646int QListViewPrivate::verticalScrollToValue(const QModelIndex &index, const QRect &rect,
647 QListView::ScrollHint hint) const
648{
649 Q_Q(const QListView);
650
651 const QRect area = viewport->rect();
652 const bool above = (hint == QListView::EnsureVisible && rect.top() < area.top());
653 const bool below = (hint == QListView::EnsureVisible && rect.bottom() > area.bottom());
654
655 int verticalValue = q->verticalScrollBar()->value();
656
657 // ScrollPerItem
658 if (q->verticalScrollMode() == QAbstractItemView::ScrollPerItem && viewMode == QListView::ListMode) {
659 const QListViewItem item = indexToListViewItem(index);
660 const QRect rect = q->visualRect(index);
661 verticalValue = staticListView->verticalPerItemValue(itemIndex(item),
662 verticalValue, area.height(),
663 above, below, isWrapping(), hint, rect.height());
664
665 } else { // ScrollPerPixel
666 QRect adjusted = rect.adjusted(-spacing(), -spacing(), spacing(), spacing());
667 if (hint == QListView::PositionAtTop || above)
668 verticalValue += adjusted.top();
669 else if (hint == QListView::PositionAtBottom || below)
670 verticalValue += qMin(adjusted.top(), adjusted.bottom() - area.height() + 1);
671 else if (hint == QListView::PositionAtCenter)
672 verticalValue += adjusted.top() - ((area.height() - adjusted.height()) / 2);
673 }
674
675 return verticalValue;
676}
677
678void QListViewPrivate::selectAll(QItemSelectionModel::SelectionFlags command)
679{
680 if (!selectionModel)
681 return;
682
683 QItemSelection selection;
684 QModelIndex topLeft;
685 int row = 0;
686 const int colCount = model->columnCount(root);
687 for(; row < model->rowCount(root); ++row) {
688 if (isHidden(row)) {
689 //it might be the end of a selection range
690 if (topLeft.isValid()) {
691 QModelIndex bottomRight = model->index(row - 1, colCount - 1, root);
692 selection.append(QItemSelectionRange(topLeft, bottomRight));
693 topLeft = QModelIndex();
694 }
695 continue;
696 }
697
698 if (!topLeft.isValid()) //start of a new selection range
699 topLeft = model->index(row, 0, root);
700 }
701
702 if (topLeft.isValid()) {
703 //last selected range
704 QModelIndex bottomRight = model->index(row - 1, colCount - 1, root);
705 selection.append(QItemSelectionRange(topLeft, bottomRight));
706 }
707
708 if (!selection.isEmpty())
709 selectionModel->select(selection, command);
710}
711
712
713/*!
714 \internal
715*/
716void QListView::reset()
717{
718 Q_D(QListView);
719 d->clear();
720 d->hiddenRows.clear();
721 QAbstractItemView::reset();
722}
723
724/*!
725 \internal
726*/
727void QListView::setRootIndex(const QModelIndex &index)
728{
729 Q_D(QListView);
730 d->column = qBound(0, d->column, d->model->columnCount(index) - 1);
731 QAbstractItemView::setRootIndex(index);
732 // sometimes we get an update before reset() is called
733 d->clear();
734 d->hiddenRows.clear();
735}
736
737/*!
738 \internal
739
740 Scroll the view contents by \a dx and \a dy.
741*/
742void QListView::scrollContentsBy(int dx, int dy)
743{
744 Q_D(QListView);
745
746 d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling
747
748 if (d->viewMode == ListMode)
749 d->staticListView->scrollContentsBy(dx, dy);
750 else if (state() == DragSelectingState)
751 d->scrollElasticBandBy(isRightToLeft() ? -dx : dx, dy);
752
753 d->scrollContentsBy(isRightToLeft() ? -dx : dx, dy);
754
755 // update the dragged items
756 if (d->viewMode == IconMode) // ### move to dynamic class
757 if (!d->dynamicListView->draggedItems.isEmpty())
758 d->setDirtyRegion(d->dynamicListView->draggedItemsRect().translated(dx, dy));
759}
760
761/*!
762 \internal
763
764 Resize the internal contents to \a width and \a height and set the
765 scroll bar ranges accordingly.
766*/
767void QListView::resizeContents(int width, int height)
768{
769 Q_D(QListView);
770 d->setContentsSize(width, height);
771}
772
773/*!
774 \internal
775*/
776QSize QListView::contentsSize() const
777{
778 Q_D(const QListView);
779 return d->contentsSize();
780}
781
782/*!
783 \reimp
784*/
785void QListView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
786{
787 Q_D(QListView);
788 if (d->viewMode == IconMode)
789 d->dynamicListView->dataChanged(topLeft, bottomRight);
790 QAbstractItemView::dataChanged(topLeft, bottomRight);
791}
792
793/*!
794 \reimp
795*/
796void QListView::rowsInserted(const QModelIndex &parent, int start, int end)
797{
798 Q_D(QListView);
799 // ### be smarter about inserted items
800 d->clear();
801 d->doDelayedItemsLayout();
802 QAbstractItemView::rowsInserted(parent, start, end);
803}
804
805/*!
806 \reimp
807*/
808void QListView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
809{
810 Q_D(QListView);
811 // if the parent is above d->root in the tree, nothing will happen
812 QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
813 if (parent == d->root) {
814 for (int i = d->hiddenRows.count() - 1; i >= 0; --i) {
815 int hiddenRow = d->hiddenRows.at(i).row();
816 if (hiddenRow >= start && hiddenRow <= end) {
817 d->hiddenRows.remove(i);
818 }
819 }
820 }
821 d->clear();
822 d->doDelayedItemsLayout();
823}
824
825/*!
826 \reimp
827*/
828void QListView::mouseMoveEvent(QMouseEvent *e)
829{
830 Q_D(QListView);
831 QAbstractItemView::mouseMoveEvent(e);
832 if (state() == DragSelectingState
833 && d->showElasticBand
834 && d->selectionMode != SingleSelection
835 && d->selectionMode != NoSelection) {
836 QRect rect(d->pressedPosition, e->pos() + QPoint(horizontalOffset(), verticalOffset()));
837 rect = rect.normalized();
838 d->setDirtyRegion(d->mapToViewport(rect.united(d->elasticBand), d->viewMode == QListView::ListMode));
839 d->elasticBand = rect;
840 }
841}
842
843/*!
844 \reimp
845*/
846void QListView::mouseReleaseEvent(QMouseEvent *e)
847{
848 Q_D(QListView);
849 QAbstractItemView::mouseReleaseEvent(e);
850 // #### move this implementation into a dynamic class
851 if (d->showElasticBand && d->elasticBand.isValid()) {
852 d->setDirtyRegion(d->mapToViewport(d->elasticBand, d->viewMode == QListView::ListMode));
853 d->elasticBand = QRect();
854 }
855}
856
857/*!
858 \reimp
859*/
860void QListView::timerEvent(QTimerEvent *e)
861{
862 Q_D(QListView);
863 if (e->timerId() == d->batchLayoutTimer.timerId()) {
864 if (d->doItemsLayout(d->batchSize)) { // layout is done
865 d->batchLayoutTimer.stop();
866 updateGeometries();
867 d->viewport->update();
868 }
869 }
870 QAbstractItemView::timerEvent(e);
871}
872
873/*!
874 \reimp
875*/
876void QListView::resizeEvent(QResizeEvent *e)
877{
878 Q_D(QListView);
879 if (d->delayedPendingLayout)
880 return;
881
882 QSize delta = e->size() - e->oldSize();
883
884 if (delta.isNull())
885 return;
886
887 bool listWrap = (d->viewMode == ListMode) && d->wrapItemText;
888 bool flowDimensionChanged = (d->flow == LeftToRight && delta.width() != 0)
889 || (d->flow == TopToBottom && delta.height() != 0);
890
891 // We post a delayed relayout in the following cases :
892 // - we're wrapping
893 // - the state is NoState, we're adjusting and the size has changed in the flowing direction
894 if (listWrap
895 || (state() == NoState && d->resizeMode == Adjust && flowDimensionChanged)) {
896 d->doDelayedItemsLayout(100); // wait 1/10 sec before starting the layout
897 } else {
898 QAbstractItemView::resizeEvent(e);
899 }
900}
901
902#ifndef QT_NO_DRAGANDDROP
903
904/*!
905 \reimp
906*/
907void QListView::dragMoveEvent(QDragMoveEvent *e)
908{
909 // ### move implementation to dynamic
910 Q_D(QListView);
911 if (e->source() == this && d->viewMode == IconMode) {
912 // the ignore by default
913 e->ignore();
914 if (d->canDecode(e)) {
915 // get old dragged items rect
916 QRect itemsRect = d->dynamicListView->itemsRect(d->dynamicListView->draggedItems);
917 d->setDirtyRegion(itemsRect.translated(d->dynamicListView->draggedItemsDelta()));
918 // update position
919 d->dynamicListView->draggedItemsPos = e->pos();
920 // get new items rect
921 d->setDirtyRegion(itemsRect.translated(d->dynamicListView->draggedItemsDelta()));
922 // set the item under the cursor to current
923 QModelIndex index;
924 if (d->movement == Snap) {
925 QRect rect(d->dynamicListView->snapToGrid(e->pos() + d->offset()), d->gridSize());
926 d->intersectingSet(rect);
927 index = d->intersectVector.count() > 0
928 ? d->intersectVector.last() : QModelIndex();
929 } else {
930 index = indexAt(e->pos());
931 }
932 // check if we allow drops here
933 if (e->source() == this && d->dynamicListView->draggedItems.contains(index))
934 e->accept(); // allow changing item position
935 else if (d->model->flags(index) & Qt::ItemIsDropEnabled)
936 e->accept(); // allow dropping on dropenabled items
937 else if (!index.isValid())
938 e->accept(); // allow dropping in empty areas
939 }
940 // do autoscrolling
941 if (d->shouldAutoScroll(e->pos()))
942 startAutoScroll();
943 } else { // not internal
944 QAbstractItemView::dragMoveEvent(e);
945 }
946}
947
948/*!
949 \reimp
950*/
951void QListView::dragLeaveEvent(QDragLeaveEvent *e)
952{
953 // ### move implementation to dynamic
954 Q_D(QListView);
955 if (d->viewMode == IconMode) {
956 d->viewport->update(d->dynamicListView->draggedItemsRect()); // erase the area
957 d->dynamicListView->draggedItemsPos = QPoint(-1, -1); // don't draw the dragged items
958 }
959 QAbstractItemView::dragLeaveEvent(e);
960}
961
962/*!
963 \reimp
964*/
965void QListView::dropEvent(QDropEvent *event)
966{
967 Q_D(QListView);
968 if (event->source() == this && d->viewMode == IconMode)
969 internalDrop(event); // ### move to dynamic
970 else
971 QAbstractItemView::dropEvent(event);
972}
973
974/*!
975 \reimp
976*/
977void QListView::startDrag(Qt::DropActions supportedActions)
978{
979 Q_D(QListView);
980 if (d->viewMode == IconMode) // ### move to dynamic
981 internalDrag(supportedActions);
982 else
983 QAbstractItemView::startDrag(supportedActions);
984}
985
986/*!
987 \internal
988
989 Called whenever items from the view is dropped on the viewport.
990 The \a event provides additional information.
991*/
992void QListView::internalDrop(QDropEvent *event)
993{
994 Q_D(QListView);
995 if (d->viewMode == QListView::ListMode)
996 return;
997
998 // ### move to dynamic class
999 QPoint offset(horizontalOffset(), verticalOffset());
1000 QPoint end = event->pos() + offset;
1001 QPoint start = d->pressedPosition;
1002 QPoint delta = (d->movement == Snap ?
1003 d->dynamicListView->snapToGrid(end)
1004 - d->dynamicListView->snapToGrid(start) : end - start);
1005 QSize contents = d->contentsSize();
1006 QList<QModelIndex> indexes = d->selectionModel->selectedIndexes();
1007 for (int i = 0; i < indexes.count(); ++i) {
1008 QModelIndex index = indexes.at(i);
1009 QRect rect = rectForIndex(index);
1010 d->setDirtyRegion(d->mapToViewport(rect, d->viewMode == QListView::ListMode));
1011 QPoint dest = rect.topLeft() + delta;
1012 if (isRightToLeft())
1013 dest.setX(d->flipX(dest.x()) - rect.width());
1014 d->dynamicListView->moveItem(index.row(), dest);
1015 d->setDirtyRegion(visualRect(index));
1016 }
1017 stopAutoScroll();
1018 d->dynamicListView->draggedItems.clear();
1019 emit indexesMoved(indexes);
1020 event->accept(); // we have handled the event
1021 // if the size has not grown, we need to check if it has shrinked
1022 if (d->dynamicListView
1023 && (d->contentsSize().width() <= contents.width()
1024 || d->contentsSize().height() <= contents.height())) {
1025 d->dynamicListView->updateContentsSize();
1026 }
1027 if (d->contentsSize() != contents)
1028 updateGeometries();
1029}
1030
1031/*!
1032 \internal
1033
1034 Called whenever the user starts dragging items and the items are movable,
1035 enabling internal dragging and dropping of items.
1036*/
1037void QListView::internalDrag(Qt::DropActions supportedActions)
1038{
1039 Q_D(QListView);
1040 if (d->viewMode == QListView::ListMode)
1041 return;
1042
1043 // #### move to dynamic class
1044
1045 // This function does the same thing as in QAbstractItemView::startDrag(),
1046 // plus adding viewitems to the draggedItems list.
1047 // We need these items to draw the drag items
1048 QModelIndexList indexes = d->selectionModel->selectedIndexes();
1049 if (indexes.count() > 0 ) {
1050 if (d->viewport->acceptDrops()) {
1051 QModelIndexList::ConstIterator it = indexes.constBegin();
1052 for (; it != indexes.constEnd(); ++it)
1053 if (d->model->flags(*it) & Qt::ItemIsDragEnabled
1054 && (*it).column() == d->column)
1055 d->dynamicListView->draggedItems.push_back(*it);
1056 }
1057 QDrag *drag = new QDrag(this);
1058 drag->setMimeData(d->model->mimeData(indexes));
1059 Qt::DropAction action = drag->exec(supportedActions, Qt::CopyAction);
1060 d->dynamicListView->draggedItems.clear();
1061 if (action == Qt::MoveAction)
1062 d->clearOrRemove();
1063 }
1064}
1065
1066#endif // QT_NO_DRAGANDDROP
1067
1068/*!
1069 \reimp
1070*/
1071QStyleOptionViewItem QListView::viewOptions() const
1072{
1073 Q_D(const QListView);
1074 QStyleOptionViewItem option = QAbstractItemView::viewOptions();
1075 if (!d->iconSize.isValid()) { // otherwise it was already set in abstractitemview
1076 int pm = (d->viewMode == ListMode
1077 ? style()->pixelMetric(QStyle::PM_ListViewIconSize, 0, this)
1078 : style()->pixelMetric(QStyle::PM_IconViewIconSize, 0, this));
1079 option.decorationSize = QSize(pm, pm);
1080 }
1081 if (d->viewMode == IconMode) {
1082 option.showDecorationSelected = false;
1083 option.decorationPosition = QStyleOptionViewItem::Top;
1084 option.displayAlignment = Qt::AlignCenter;
1085 } else {
1086 option.decorationPosition = QStyleOptionViewItem::Left;
1087 }
1088 return option;
1089}
1090
1091/*!
1092 \reimp
1093*/
1094void QListView::paintEvent(QPaintEvent *e)
1095{
1096 Q_D(QListView);
1097 if (!d->itemDelegate)
1098 return;
1099 QStyleOptionViewItemV4 option = d->viewOptionsV4();
1100 QPainter painter(d->viewport);
1101 QRect area = e->rect();
1102
1103 QVector<QModelIndex> toBeRendered;
1104// QVector<QRect> rects = e->region().rects();
1105// for (int i = 0; i < rects.size(); ++i) {
1106// d->intersectingSet(rects.at(i).translated(horizontalOffset(), verticalOffset()));
1107// toBeRendered += d->intersectVector;
1108// }
1109 d->intersectingSet(e->rect().translated(horizontalOffset(), verticalOffset()), false);
1110 toBeRendered = d->intersectVector;
1111
1112 const QModelIndex current = currentIndex();
1113 const QModelIndex hover = d->hover;
1114 const QAbstractItemModel *itemModel = d->model;
1115 const QItemSelectionModel *selections = d->selectionModel;
1116 const bool focus = (hasFocus() || d->viewport->hasFocus()) && current.isValid();
1117 const bool alternate = d->alternatingColors;
1118 const QStyle::State state = option.state;
1119 const QAbstractItemView::State viewState = this->state();
1120 const bool enabled = (state & QStyle::State_Enabled) != 0;
1121
1122 bool alternateBase = false;
1123 int previousRow = -2; // trigger the alternateBase adjustment on first pass
1124
1125 QVector<QModelIndex>::const_iterator end = toBeRendered.constEnd();
1126 for (QVector<QModelIndex>::const_iterator it = toBeRendered.constBegin(); it != end; ++it) {
1127 Q_ASSERT((*it).isValid());
1128 option.rect = visualRect(*it);
1129 option.state = state;
1130 if (selections && selections->isSelected(*it))
1131 option.state |= QStyle::State_Selected;
1132 if (enabled) {
1133 QPalette::ColorGroup cg;
1134 if ((itemModel->flags(*it) & Qt::ItemIsEnabled) == 0) {
1135 option.state &= ~QStyle::State_Enabled;
1136 cg = QPalette::Disabled;
1137 } else {
1138 cg = QPalette::Normal;
1139 }
1140 option.palette.setCurrentColorGroup(cg);
1141 }
1142 if (focus && current == *it) {
1143 option.state |= QStyle::State_HasFocus;
1144 if (viewState == EditingState)
1145 option.state |= QStyle::State_Editing;
1146 }
1147 if (*it == hover)
1148 option.state |= QStyle::State_MouseOver;
1149 else
1150 option.state &= ~QStyle::State_MouseOver;
1151
1152 if (alternate) {
1153 int row = (*it).row();
1154 if (row != previousRow + 1) {
1155 // adjust alternateBase according to rows in the "gap"
1156 if (!d->hiddenRows.isEmpty()) {
1157 for (int r = qMax(previousRow + 1, 0); r < row; ++r) {
1158 if (!d->isHidden(r))
1159 alternateBase = !alternateBase;
1160 }
1161 } else {
1162 alternateBase = (row & 1) != 0;
1163 }
1164 }
1165 if (alternateBase) {
1166 option.features |= QStyleOptionViewItemV2::Alternate;
1167 } else {
1168 option.features &= ~QStyleOptionViewItemV2::Alternate;
1169 }
1170
1171 // draw background of the item (only alternate row). rest of the background
1172 // is provided by the delegate
1173 QStyle::State oldState = option.state;
1174 option.state &= ~QStyle::State_Selected;
1175 style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &option, &painter, this);
1176 option.state = oldState;
1177
1178 alternateBase = !alternateBase;
1179 previousRow = row;
1180 }
1181
1182 if (const QWidget *widget = d->editorForIndex(*it).editor) {
1183 QRegion itemGeometry(option.rect);
1184 QRegion widgetGeometry(widget->geometry());
1185 painter.save();
1186 painter.setClipRegion(itemGeometry.subtracted(widgetGeometry));
1187 d->delegateForIndex(*it)->paint(&painter, option, *it);
1188 painter.restore();
1189 } else {
1190 d->delegateForIndex(*it)->paint(&painter, option, *it);
1191 }
1192 }
1193
1194#ifndef QT_NO_DRAGANDDROP
1195 // #### move this implementation into a dynamic class
1196 if (d->viewMode == IconMode)
1197 if (!d->dynamicListView->draggedItems.isEmpty()
1198 && d->viewport->rect().contains(d->dynamicListView->draggedItemsPos)) {
1199 QPoint delta = d->dynamicListView->draggedItemsDelta();
1200 painter.translate(delta.x(), delta.y());
1201 d->dynamicListView->drawItems(&painter, d->dynamicListView->draggedItems);
1202 }
1203 // FIXME: Until the we can provide a proper drop indicator
1204 // in IconMode, it makes no sense to show it
1205 if (d->viewMode == ListMode)
1206 d->paintDropIndicator(&painter);
1207#endif
1208
1209#ifndef QT_NO_RUBBERBAND
1210 // #### move this implementation into a dynamic class
1211 if (d->showElasticBand && d->elasticBand.isValid()) {
1212 QStyleOptionRubberBand opt;
1213 opt.initFrom(this);
1214 opt.shape = QRubberBand::Rectangle;
1215 opt.opaque = false;
1216 opt.rect = d->mapToViewport(d->elasticBand, false).intersected(
1217 d->viewport->rect().adjusted(-16, -16, 16, 16));
1218 painter.save();
1219 style()->drawControl(QStyle::CE_RubberBand, &opt, &painter);
1220 painter.restore();
1221 }
1222#endif
1223}
1224
1225/*!
1226 \reimp
1227*/
1228QModelIndex QListView::indexAt(const QPoint &p) const
1229{
1230 Q_D(const QListView);
1231 QRect rect(p.x() + horizontalOffset(), p.y() + verticalOffset(), 1, 1);
1232 d->intersectingSet(rect);
1233 QModelIndex index = d->intersectVector.count() > 0
1234 ? d->intersectVector.last() : QModelIndex();
1235 if (index.isValid() && visualRect(index).contains(p))
1236 return index;
1237 return QModelIndex();
1238}
1239
1240/*!
1241 \reimp
1242*/
1243int QListView::horizontalOffset() const
1244{
1245 Q_D(const QListView);
1246 // ### split into static and dynamic
1247 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem && d->viewMode == ListMode) {
1248 if (d->isWrapping()) {
1249 if (d->flow == TopToBottom && !d->staticListView->segmentPositions.isEmpty()) {
1250 const int max = d->staticListView->segmentPositions.count() - 1;
1251 int currentValue = qBound(0, horizontalScrollBar()->value(), max);
1252 int position = d->staticListView->segmentPositions.at(currentValue);
1253 int maximumValue = qBound(0, horizontalScrollBar()->maximum(), max);
1254 int maximum = d->staticListView->segmentPositions.at(maximumValue);
1255 return (isRightToLeft() ? maximum - position : position);
1256 }
1257 //return 0;
1258 } else {
1259 if (d->flow == LeftToRight && !d->staticListView->flowPositions.isEmpty()) {
1260 int position = d->staticListView->flowPositions.at(horizontalScrollBar()->value());
1261 int maximum = d->staticListView->flowPositions.at(horizontalScrollBar()->maximum());
1262 return (isRightToLeft() ? maximum - position : position);
1263 }
1264 //return 0;
1265 }
1266 }
1267 return (isRightToLeft()
1268 ? horizontalScrollBar()->maximum() - horizontalScrollBar()->value()
1269 : horizontalScrollBar()->value());
1270}
1271
1272/*!
1273 \reimp
1274*/
1275int QListView::verticalOffset() const
1276{
1277 // ## split into static and dynamic
1278 Q_D(const QListView);
1279 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem && d->viewMode == ListMode) {
1280 if (d->isWrapping()) {
1281 if (d->flow == LeftToRight && !d->staticListView->segmentPositions.isEmpty()) {
1282 int value = verticalScrollBar()->value();
1283 if (value >= d->staticListView->segmentPositions.count()) {
1284 //qWarning("QListView: Vertical scroll bar is out of bounds");
1285 return 0;
1286 }
1287 return d->staticListView->segmentPositions.at(value);
1288 }
1289 } else {
1290 if (d->flow == TopToBottom && !d->staticListView->flowPositions.isEmpty()) {
1291 int value = verticalScrollBar()->value();
1292 if (value > d->staticListView->flowPositions.count()) {
1293 //qWarning("QListView: Vertical scroll bar is out of bounds");
1294 return 0;
1295 }
1296 return d->staticListView->flowPositions.at(value) - d->spacing();
1297 }
1298 }
1299 }
1300 return verticalScrollBar()->value();
1301}
1302
1303/*!
1304 \reimp
1305*/
1306QModelIndex QListView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
1307{
1308 Q_D(QListView);
1309 Q_UNUSED(modifiers);
1310
1311 QModelIndex current = currentIndex();
1312 if (!current.isValid()) {
1313 int rowCount = d->model->rowCount(d->root);
1314 if (!rowCount)
1315 return QModelIndex();
1316 int row = 0;
1317 while (row < rowCount && d->isHiddenOrDisabled(row))
1318 ++row;
1319 if (row >= rowCount)
1320 return QModelIndex();
1321 return d->model->index(row, 0, d->root);
1322 }
1323
1324 const QRect initialRect = rectForIndex(current);
1325 QRect rect = initialRect;
1326 if (rect.isEmpty()) {
1327 return d->model->index(0, 0, d->root);
1328 }
1329 if (d->gridSize().isValid()) rect.setSize(d->gridSize());
1330
1331 QSize contents = d->contentsSize();
1332 d->intersectVector.clear();
1333
1334 switch (cursorAction) {
1335 case MoveLeft:
1336 while (d->intersectVector.isEmpty()) {
1337 rect.translate(-rect.width(), 0);
1338 if (rect.right() <= 0)
1339 return current;
1340 if (rect.left() < 0)
1341 rect.setLeft(0);
1342 d->intersectingSet(rect);
1343 d->removeCurrentAndDisabled(&d->intersectVector, current);
1344 }
1345 return d->closestIndex(initialRect, d->intersectVector);
1346 case MoveRight:
1347 while (d->intersectVector.isEmpty()) {
1348 rect.translate(rect.width(), 0);
1349 if (rect.left() >= contents.width())
1350 return current;
1351 if (rect.right() > contents.width())
1352 rect.setRight(contents.width());
1353 d->intersectingSet(rect);
1354 d->removeCurrentAndDisabled(&d->intersectVector, current);
1355 }
1356 return d->closestIndex(initialRect, d->intersectVector);
1357 case MovePageUp:
1358 rect.moveTop(rect.top() - d->viewport->height());
1359 if (rect.top() < rect.height())
1360 rect.moveTop(rect.height());
1361 case MovePrevious:
1362 case MoveUp:
1363 while (d->intersectVector.isEmpty()) {
1364 rect.translate(0, -rect.height());
1365 if (rect.bottom() <= 0) {
1366#ifdef QT_KEYPAD_NAVIGATION
1367 if (QApplication::keypadNavigationEnabled()) {
1368 int row = d->batchStartRow() - 1;
1369 while (row >= 0 && d->isHiddenOrDisabled(row))
1370 --row;
1371 if (row >= 0)
1372 return d->model->index(row, d->column, d->root);
1373 }
1374#endif
1375 return current;
1376 }
1377 if (rect.top() < 0)
1378 rect.setTop(0);
1379 d->intersectingSet(rect);
1380 d->removeCurrentAndDisabled(&d->intersectVector, current);
1381 }
1382 return d->closestIndex(initialRect, d->intersectVector);
1383 case MovePageDown:
1384 rect.moveTop(rect.top() + d->viewport->height());
1385 if (rect.bottom() > contents.height() - rect.height())
1386 rect.moveBottom(contents.height() - rect.height());
1387 case MoveNext:
1388 case MoveDown:
1389 while (d->intersectVector.isEmpty()) {
1390 rect.translate(0, rect.height());
1391 if (rect.top() >= contents.height()) {
1392#ifdef QT_KEYPAD_NAVIGATION
1393 if (QApplication::keypadNavigationEnabled()) {
1394 int rowCount = d->model->rowCount(d->root);
1395 int row = 0;
1396 while (row < rowCount && d->isHiddenOrDisabled(row))
1397 ++row;
1398 if (row < rowCount)
1399 return d->model->index(row, d->column, d->root);
1400 }
1401#endif
1402 return current;
1403 }
1404 if (rect.bottom() > contents.height())
1405 rect.setBottom(contents.height());
1406 d->intersectingSet(rect);
1407 d->removeCurrentAndDisabled(&d->intersectVector, current);
1408 }
1409 return d->closestIndex(initialRect, d->intersectVector);
1410 case MoveHome:
1411 return d->model->index(0, d->column, d->root);
1412 case MoveEnd:
1413 return d->model->index(d->batchStartRow() - 1, d->column, d->root);}
1414
1415 return current;
1416}
1417
1418/*!
1419 Returns the rectangle of the item at position \a index in the
1420 model. The rectangle is in contents coordinates.
1421
1422 \sa visualRect()
1423*/
1424QRect QListView::rectForIndex(const QModelIndex &index) const
1425{
1426 Q_D(const QListView);
1427 if (!d->isIndexValid(index)
1428 || index.parent() != d->root
1429 || index.column() != d->column
1430 || isIndexHidden(index))
1431 return QRect();
1432 d->executePostedLayout();
1433 QListViewItem item = d->indexToListViewItem(index);
1434 return d->viewItemRect(item);
1435}
1436
1437/*!
1438 \since 4.1
1439
1440 Sets the contents position of the item at \a index in the model to the given
1441 \a position.
1442 If the list view's movement mode is Static, this function will have no
1443 effect.
1444*/
1445void QListView::setPositionForIndex(const QPoint &position, const QModelIndex &index)
1446{
1447 Q_D(QListView);
1448 if (d->movement == Static
1449 || !d->isIndexValid(index)
1450 || index.parent() != d->root
1451 || index.column() != d->column)
1452 return;
1453
1454 d->executePostedLayout();
1455 if (index.row() >= d->dynamicListView->items.count())
1456 return;
1457 const QSize oldContents = d->contentsSize();
1458 d->setDirtyRegion(visualRect(index)); // update old position
1459 d->dynamicListView->moveItem(index.row(), position);
1460 d->setDirtyRegion(visualRect(index)); // update new position
1461
1462 if (d->contentsSize() != oldContents)
1463 updateGeometries(); // update the scroll bars
1464}
1465
1466/*!
1467 \reimp
1468*/
1469void QListView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
1470{
1471 Q_D(QListView);
1472 if (!d->selectionModel)
1473 return;
1474
1475 // if we are wrapping, we can only selecte inside the contents rectangle
1476 int w = qMax(d->contentsSize().width(), d->viewport->width());
1477 int h = qMax(d->contentsSize().height(), d->viewport->height());
1478 if (d->wrap && !QRect(0, 0, w, h).intersects(rect))
1479 return;
1480
1481 QItemSelection selection;
1482
1483 if (rect.width() == 1 && rect.height() == 1) {
1484 d->intersectingSet(rect.translated(horizontalOffset(), verticalOffset()));
1485 QModelIndex tl;
1486 if (!d->intersectVector.isEmpty())
1487 tl = d->intersectVector.last(); // special case for mouse press; only select the top item
1488 if (tl.isValid() && d->isIndexEnabled(tl))
1489 selection.select(tl, tl);
1490 } else {
1491 if (state() == DragSelectingState) { // visual selection mode (rubberband selection)
1492 selection = d->selection(rect.translated(horizontalOffset(), verticalOffset()));
1493 } else { // logical selection mode (key and mouse click selection)
1494 QModelIndex tl, br;
1495 // get the first item
1496 const QRect topLeft(rect.left() + horizontalOffset(), rect.top() + verticalOffset(), 1, 1);
1497 d->intersectingSet(topLeft);
1498 if (!d->intersectVector.isEmpty())
1499 tl = d->intersectVector.last();
1500 // get the last item
1501 const QRect bottomRight(rect.right() + horizontalOffset(), rect.bottom() + verticalOffset(), 1, 1);
1502 d->intersectingSet(bottomRight);
1503 if (!d->intersectVector.isEmpty())
1504 br = d->intersectVector.last();
1505
1506 // get the ranges
1507 if (tl.isValid() && br.isValid()
1508 && d->isIndexEnabled(tl)
1509 && d->isIndexEnabled(br)) {
1510 QRect first = rectForIndex(tl);
1511 QRect last = rectForIndex(br);
1512 QRect middle;
1513 if (d->flow == LeftToRight) {
1514 QRect &top = first;
1515 QRect &bottom = last;
1516 // if bottom is above top, swap them
1517 if (top.center().y() > bottom.center().y()) {
1518 QRect tmp = top;
1519 top = bottom;
1520 bottom = tmp;
1521 }
1522 // if the rect are on differnet lines, expand
1523 if (top.top() != bottom.top()) {
1524 // top rectangle
1525 if (isRightToLeft())
1526 top.setLeft(0);
1527 else
1528 top.setRight(contentsSize().width());
1529 // bottom rectangle
1530 if (isRightToLeft())
1531 bottom.setRight(contentsSize().width());
1532 else
1533 bottom.setLeft(0);
1534 } else if (top.left() > bottom.right()) {
1535 if (isRightToLeft())
1536 bottom.setLeft(top.right());
1537 else
1538 bottom.setRight(top.left());
1539 } else {
1540 if (isRightToLeft())
1541 top.setLeft(bottom.right());
1542 else
1543 top.setRight(bottom.left());
1544 }
1545 // middle rectangle
1546 if (top.bottom() < bottom.top()) {
1547 middle.setTop(top.bottom() + 1);
1548 middle.setLeft(qMin(top.left(), bottom.left()));
1549 middle.setBottom(bottom.top() - 1);
1550 middle.setRight(qMax(top.right(), bottom.right()));
1551 }
1552 } else { // TopToBottom
1553 QRect &left = first;
1554 QRect &right = last;
1555 if (left.center().x() > right.center().x())
1556 qSwap(left, right);
1557
1558 int ch = contentsSize().height();
1559 if (left.left() != right.left()) {
1560 // left rectangle
1561 if (isRightToLeft())
1562 left.setTop(0);
1563 else
1564 left.setBottom(ch);
1565
1566 // top rectangle
1567 if (isRightToLeft())
1568 right.setBottom(ch);
1569 else
1570 right.setTop(0);
1571 // only set middle if the
1572 middle.setTop(0);
1573 middle.setBottom(ch);
1574 middle.setLeft(left.right() + 1);
1575 middle.setRight(right.left() - 1);
1576 } else if (left.bottom() < right.top()) {
1577 left.setBottom(right.top() - 1);
1578 } else {
1579 right.setBottom(left.top() - 1);
1580 }
1581 }
1582
1583 // do the selections
1584 QItemSelection topSelection = d->selection(first);
1585 QItemSelection middleSelection = d->selection(middle);
1586 QItemSelection bottomSelection = d->selection(last);
1587 // merge
1588 selection.merge(topSelection, QItemSelectionModel::Select);
1589 selection.merge(middleSelection, QItemSelectionModel::Select);
1590 selection.merge(bottomSelection, QItemSelectionModel::Select);
1591 }
1592 }
1593 }
1594
1595 d->selectionModel->select(selection, command);
1596}
1597
1598/*!
1599 \reimp
1600*/
1601QRegion QListView::visualRegionForSelection(const QItemSelection &selection) const
1602{
1603 Q_D(const QListView);
1604 // ### NOTE: this is a potential bottleneck in non-static mode
1605 int c = d->column;
1606 QRegion selectionRegion;
1607 for (int i = 0; i < selection.count(); ++i) {
1608 if (!selection.at(i).isValid())
1609 continue;
1610 QModelIndex parent = selection.at(i).topLeft().parent();
1611 int t = selection.at(i).topLeft().row();
1612 int b = selection.at(i).bottomRight().row();
1613 if (d->viewMode == IconMode || d->isWrapping()) { // in non-static mode, we have to go through all selected items
1614 for (int r = t; r <= b; ++r)
1615 selectionRegion += QRegion(visualRect(d->model->index(r, c, parent)));
1616 } else { // in static mode, we can optimize a bit
1617 while (t <= b && d->isHidden(t)) ++t;
1618 while (b >= t && d->isHidden(b)) --b;
1619 const QModelIndex top = d->model->index(t, c, d->root);
1620 const QModelIndex bottom = d->model->index(b, c, d->root);
1621 QRect rect(visualRect(top).topLeft(),
1622 visualRect(bottom).bottomRight());
1623 selectionRegion += QRegion(rect);
1624 }
1625 }
1626
1627 return selectionRegion;
1628}
1629
1630/*!
1631 \reimp
1632*/
1633QModelIndexList QListView::selectedIndexes() const
1634{
1635 Q_D(const QListView);
1636 QModelIndexList viewSelected;
1637 QModelIndexList modelSelected;
1638 if (d->selectionModel)
1639 modelSelected = d->selectionModel->selectedIndexes();
1640 for (int i = 0; i < modelSelected.count(); ++i) {
1641 QModelIndex index = modelSelected.at(i);
1642 if (!isIndexHidden(index) && index.parent() == d->root && index.column() == d->column)
1643 viewSelected.append(index);
1644 }
1645 return viewSelected;
1646}
1647
1648/*!
1649 \internal
1650
1651 Layout the items according to the flow and wrapping properties.
1652*/
1653void QListView::doItemsLayout()
1654{
1655 Q_D(QListView);
1656 // showing the scroll bars will trigger a resize event,
1657 // so we set the state to expanding to avoid
1658 // triggering another layout
1659 QAbstractItemView::State oldState = state();
1660 setState(ExpandingState);
1661 if (d->model->columnCount(d->root) > 0) { // no columns means no contents
1662 d->resetBatchStartRow();
1663 if (layoutMode() == SinglePass)
1664 d->doItemsLayout(d->model->rowCount(d->root)); // layout everything
1665 else if (!d->batchLayoutTimer.isActive()) {
1666 if (!d->doItemsLayout(d->batchSize)) // layout is done
1667 d->batchLayoutTimer.start(0, this); // do a new batch as fast as possible
1668 }
1669 }
1670 QAbstractItemView::doItemsLayout();
1671 setState(oldState); // restoring the oldState
1672}
1673
1674/*!
1675 \reimp
1676*/
1677void QListView::updateGeometries()
1678{
1679 Q_D(QListView);
1680 if (d->model->rowCount(d->root) <= 0 || d->model->columnCount(d->root) <= 0) {
1681 horizontalScrollBar()->setRange(0, 0);
1682 verticalScrollBar()->setRange(0, 0);
1683 } else {
1684 QModelIndex index = d->model->index(0, d->column, d->root);
1685 QStyleOptionViewItemV4 option = d->viewOptionsV4();
1686 QSize step = d->itemSize(option, index);
1687
1688 QSize csize = d->contentsSize();
1689 QSize vsize = d->viewport->size();
1690 QSize max = maximumViewportSize();
1691 if (max.width() >= d->contentsSize().width() && max.height() >= d->contentsSize().height())
1692 vsize = max;
1693
1694 // ### reorder the logic
1695
1696 // ### split into static and dynamic
1697
1698 const bool vertical = verticalScrollMode() == QAbstractItemView::ScrollPerItem;
1699 const bool horizontal = horizontalScrollMode() == QAbstractItemView::ScrollPerItem;
1700
1701 if (d->flow == TopToBottom) {
1702 if (horizontal && d->isWrapping() && d->viewMode == ListMode) {
1703 const QVector<int> segmentPositions = d->staticListView->segmentPositions;
1704 const int steps = segmentPositions.count() - 1;
1705 if (steps > 0) {
1706 int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.width(),
1707 csize.width(),
1708 isWrapping());
1709 horizontalScrollBar()->setSingleStep(1);
1710 horizontalScrollBar()->setPageStep(pageSteps);
1711 horizontalScrollBar()->setRange(0, steps - pageSteps);
1712 } else {
1713 horizontalScrollBar()->setRange(0, 0);
1714 }
1715 } else {
1716 horizontalScrollBar()->setSingleStep(step.width() + d->spacing());
1717 horizontalScrollBar()->setPageStep(vsize.width());
1718 horizontalScrollBar()->setRange(0, d->contentsSize().width() - vsize.width());
1719 }
1720 if (vertical && !d->isWrapping() && d->viewMode == ListMode) {
1721 const QVector<int> flowPositions = d->staticListView->flowPositions;
1722 const int steps = flowPositions.count() - 1;
1723 if (steps > 0) {
1724 int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.height(),
1725 csize.height(),
1726 isWrapping());
1727 verticalScrollBar()->setSingleStep(1);
1728 verticalScrollBar()->setPageStep(pageSteps);
1729 verticalScrollBar()->setRange(0, steps - pageSteps);
1730 } else {
1731 verticalScrollBar()->setRange(0, 0);
1732 }
1733 // } else if (vertical && d->isWrapping() && d->movement == Static) {
1734 // ### wrapped scrolling in flow direction
1735 } else {
1736 verticalScrollBar()->setSingleStep(step.height() + d->spacing());
1737 verticalScrollBar()->setPageStep(vsize.height());
1738 verticalScrollBar()->setRange(0, d->contentsSize().height() - vsize.height());
1739 }
1740 } else { // LeftToRight
1741 if (horizontal && !d->isWrapping() && d->viewMode == ListMode) {
1742 const QVector<int> flowPositions = d->staticListView->flowPositions;
1743 int steps = flowPositions.count() - 1;
1744 if (steps > 0) {
1745 int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.width(),
1746 csize.width(),
1747 isWrapping());
1748 horizontalScrollBar()->setSingleStep(1);
1749 horizontalScrollBar()->setPageStep(pageSteps);
1750 horizontalScrollBar()->setRange(0, steps - pageSteps);
1751 } else {
1752 horizontalScrollBar()->setRange(0, 0);
1753 }
1754 // } else if (horizontal && d->isWrapping() && d->movement == Static) {
1755 // ### wrapped scrolling in flow direction
1756 } else {
1757 horizontalScrollBar()->setSingleStep(step.width() + d->spacing());
1758 horizontalScrollBar()->setPageStep(vsize.width());
1759 horizontalScrollBar()->setRange(0, d->contentsSize().width() - vsize.width());
1760 }
1761 if (vertical && d->isWrapping() && d->viewMode == ListMode) {
1762 const QVector<int> segmentPositions = d->staticListView->segmentPositions;
1763 int steps = segmentPositions.count() - 1;
1764 if (steps > 0) {
1765 int pageSteps = d->staticListView->perItemScrollingPageSteps(vsize.height(),
1766 csize.height(),
1767 isWrapping());
1768 verticalScrollBar()->setSingleStep(1);
1769 verticalScrollBar()->setPageStep(pageSteps);
1770 verticalScrollBar()->setRange(0, steps - pageSteps);
1771 } else {
1772 verticalScrollBar()->setRange(0, 0);
1773 }
1774 } else {
1775 verticalScrollBar()->setSingleStep(step.height() + d->spacing());
1776 verticalScrollBar()->setPageStep(vsize.height());
1777 verticalScrollBar()->setRange(0, d->contentsSize().height() - vsize.height());
1778 }
1779 }
1780 }
1781
1782 QAbstractItemView::updateGeometries();
1783
1784 // if the scroll bars are turned off, we resize the contents to the viewport
1785 if (d->movement == Static && !d->isWrapping()) {
1786 d->layoutChildren(); // we need the viewport size to be updated
1787 if (d->flow == TopToBottom) {
1788 if (horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) {
1789 d->setContentsSize(viewport()->width(), contentsSize().height());
1790 horizontalScrollBar()->setRange(0, 0); // we see all the contents anyway
1791 }
1792 } else { // LeftToRight
1793 if (verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) {
1794 d->setContentsSize(contentsSize().width(), viewport()->height());
1795 verticalScrollBar()->setRange(0, 0); // we see all the contents anyway
1796 }
1797 }
1798 }
1799}
1800
1801/*!
1802 \reimp
1803*/
1804bool QListView::isIndexHidden(const QModelIndex &index) const
1805{
1806 Q_D(const QListView);
1807 return (d->isHidden(index.row())
1808 && (index.parent() == d->root)
1809 && index.column() == d->column);
1810}
1811
1812/*!
1813 \property QListView::modelColumn
1814 \brief the column in the model that is visible
1815
1816 By default, this property contains 0, indicating that the first
1817 column in the model will be shown.
1818*/
1819void QListView::setModelColumn(int column)
1820{
1821 Q_D(QListView);
1822 if (column < 0 || column >= d->model->columnCount(d->root))
1823 return;
1824 d->column = column;
1825 d->doDelayedItemsLayout();
1826}
1827
1828int QListView::modelColumn() const
1829{
1830 Q_D(const QListView);
1831 return d->column;
1832}
1833
1834/*!
1835 \property QListView::uniformItemSizes
1836 \brief whether all items in the listview have the same size
1837 \since 4.1
1838
1839 This property should only be set to true if it is guaranteed that all items
1840 in the view have the same size. This enables the view to do some
1841 optimizations for performance purposes.
1842
1843 By default, this property is false.
1844*/
1845void QListView::setUniformItemSizes(bool enable)
1846{
1847 Q_D(QListView);
1848 d->uniformItemSizes = enable;
1849}
1850
1851bool QListView::uniformItemSizes() const
1852{
1853 Q_D(const QListView);
1854 return d->uniformItemSizes;
1855}
1856
1857/*!
1858 \property QListView::wordWrap
1859 \brief the item text word-wrapping policy
1860 \since 4.2
1861
1862 If this property is true then the item text is wrapped where
1863 necessary at word-breaks; otherwise it is not wrapped at all.
1864 This property is false by default.
1865
1866 Please note that even if wrapping is enabled, the cell will not be
1867 expanded to make room for the text. It will print ellipsis for
1868 text that cannot be shown, according to the view's
1869 \l{QAbstractItemView::}{textElideMode}.
1870*/
1871void QListView::setWordWrap(bool on)
1872{
1873 Q_D(QListView);
1874 if (d->wrapItemText == on)
1875 return;
1876 d->wrapItemText = on;
1877 d->doDelayedItemsLayout();
1878}
1879
1880bool QListView::wordWrap() const
1881{
1882 Q_D(const QListView);
1883 return d->wrapItemText;
1884}
1885
1886/*!
1887 \property QListView::selectionRectVisible
1888 \brief if the selection rectangle should be visible
1889 \since 4.3
1890
1891 If this property is true then the selection rectangle is visible;
1892 otherwise it will be hidden.
1893
1894 \note The selection rectangle will only be visible if the selection mode
1895 is in a mode where more than one item can be selected; i.e., it will not
1896 draw a selection rectangle if the selection mode is
1897 QAbstractItemView::SingleSelection.
1898
1899 By default, this property is false.
1900*/
1901void QListView::setSelectionRectVisible(bool show)
1902{
1903 Q_D(QListView);
1904 d->modeProperties |= uint(QListViewPrivate::SelectionRectVisible);
1905 d->setSelectionRectVisible(show);
1906}
1907
1908bool QListView::isSelectionRectVisible() const
1909{
1910 Q_D(const QListView);
1911 return d->isSelectionRectVisible();
1912}
1913
1914/*!
1915 \reimp
1916*/
1917bool QListView::event(QEvent *e)
1918{
1919 return QAbstractItemView::event(e);
1920}
1921
1922/*
1923 * private object implementation
1924 */
1925
1926QListViewPrivate::QListViewPrivate()
1927 : QAbstractItemViewPrivate(),
1928 dynamicListView(0),
1929 staticListView(0),
1930 wrap(false),
1931 space(0),
1932 flow(QListView::TopToBottom),
1933 movement(QListView::Static),
1934 resizeMode(QListView::Fixed),
1935 layoutMode(QListView::SinglePass),
1936 viewMode(QListView::ListMode),
1937 modeProperties(0),
1938 column(0),
1939 uniformItemSizes(false),
1940 batchSize(100)
1941{
1942}
1943
1944QListViewPrivate::~QListViewPrivate()
1945{
1946 delete staticListView;
1947 delete dynamicListView;
1948}
1949
1950void QListViewPrivate::clear()
1951{
1952 // ### split into dynamic and static
1953 // initialization of data structs
1954 cachedItemSize = QSize();
1955 if (viewMode == QListView::ListMode)
1956 staticListView->clear();
1957 else
1958 dynamicListView->clear();
1959}
1960
1961void QListViewPrivate::prepareItemsLayout()
1962{
1963 Q_Q(QListView);
1964 clear();
1965
1966 //take the size as if there were scrollbar in order to prevent scrollbar to blink
1967 layoutBounds = QRect(QPoint(0,0), q->maximumViewportSize());
1968
1969 int frameAroundContents = 0;
1970 if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents))
1971 frameAroundContents = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2;
1972 int verticalMargin = vbarpolicy==Qt::ScrollBarAlwaysOff ? 0 :
1973 q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, q->verticalScrollBar()) + frameAroundContents;
1974 int horizontalMargin = hbarpolicy==Qt::ScrollBarAlwaysOff ? 0 :
1975 q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, q->horizontalScrollBar()) + frameAroundContents;
1976
1977 layoutBounds.adjust(0, 0, -verticalMargin, -horizontalMargin);
1978
1979 int rowCount = model->rowCount(root);
1980 int colCount = model->columnCount(root);
1981 if (colCount <= 0)
1982 rowCount = 0; // no contents
1983 if (viewMode == QListView::ListMode) {
1984 staticListView->flowPositions.resize(rowCount);
1985 } else {
1986 dynamicListView->tree.create(qMax(rowCount - hiddenRows.count(), 0));
1987 }
1988}
1989
1990/*!
1991 \internal
1992*/
1993bool QListViewPrivate::doItemsLayout(int delta)
1994{
1995 // ### split into static and dynamic
1996 int max = model->rowCount(root) - 1;
1997 int first = batchStartRow();
1998 int last = qMin(first + delta - 1, max);
1999
2000 if (max < 0 || last < first)
2001 return true; // nothing to do
2002
2003 if (first == 0) {
2004 layoutChildren(); // make sure the viewport has the right size
2005 prepareItemsLayout();
2006 }
2007
2008 QListViewLayoutInfo info;
2009 info.bounds = layoutBounds;
2010 info.grid = gridSize();
2011 info.spacing = (info.grid.isValid() ? 0 : spacing());
2012 info.first = first;
2013 info.last = last;
2014 info.wrap = isWrapping();
2015 info.flow = flow;
2016 info.max = max;
2017
2018 if (viewMode == QListView::ListMode)
2019 return staticListView->doBatchedItemLayout(info, max);
2020 return dynamicListView->doBatchedItemLayout(info, max);
2021}
2022
2023QListViewItem QListViewPrivate::indexToListViewItem(const QModelIndex &index) const
2024{
2025 if (!index.isValid() || isHidden(index.row()))
2026 return QListViewItem();
2027
2028 if (viewMode == QListView::ListMode)
2029 return staticListView->indexToListViewItem(index);
2030 return dynamicListView->indexToListViewItem(index);
2031}
2032
2033
2034int QListViewPrivate::itemIndex(const QListViewItem &item) const
2035{
2036 if (viewMode == QListView::ListMode)
2037 return staticListView->itemIndex(item);
2038 return dynamicListView->itemIndex(item);
2039}
2040
2041QRect QListViewPrivate::mapToViewport(const QRect &rect, bool greedy) const
2042{
2043 Q_Q(const QListView);
2044 if (!rect.isValid())
2045 return rect;
2046
2047 QRect result = rect;
2048 if (greedy)
2049 result = staticListView->mapToViewport(rect);
2050
2051 int dx = -q->horizontalOffset();
2052 int dy = -q->verticalOffset();
2053 result.adjust(dx, dy, dx, dy);
2054 return result;
2055}
2056
2057QModelIndex QListViewPrivate::closestIndex(const QRect &target,
2058 const QVector<QModelIndex> &candidates) const
2059{
2060 int distance = 0;
2061 int shortest = INT_MAX;
2062 QModelIndex closest;
2063 QVector<QModelIndex>::const_iterator it = candidates.begin();
2064
2065 for (; it != candidates.end(); ++it) {
2066 if (!(*it).isValid())
2067 continue;
2068
2069 const QRect indexRect = indexToListViewItem(*it).rect();
2070
2071 //if the center x (or y) position of an item is included in the rect of the other item,
2072 //we define the distance between them as the difference in x (or y) of their respective center.
2073 // Otherwise, we use the nahattan length between the 2 items
2074 if ((target.center().x() >= indexRect.x() && target.center().x() < indexRect.right())
2075 || (indexRect.center().x() >= target.x() && indexRect.center().x() < target.right())) {
2076 //one item's center is at the vertical of the other
2077 distance = qAbs(indexRect.center().y() - target.center().y());
2078 } else if ((target.center().y() >= indexRect.y() && target.center().y() < indexRect.bottom())
2079 || (indexRect.center().y() >= target.y() && indexRect.center().y() < target.bottom())) {
2080 //one item's center is at the vertical of the other
2081 distance = qAbs(indexRect.center().x() - target.center().x());
2082 } else {
2083 distance = (indexRect.center() - target.center()).manhattanLength();
2084 }
2085 if (distance < shortest) {
2086 shortest = distance;
2087 closest = *it;
2088 }
2089 }
2090 return closest;
2091}
2092
2093QSize QListViewPrivate::itemSize(const QStyleOptionViewItem &option, const QModelIndex &index) const
2094{
2095 if (!uniformItemSizes) {
2096 const QAbstractItemDelegate *delegate = delegateForIndex(index);
2097 return delegate ? delegate->sizeHint(option, index) : QSize();
2098 }
2099 if (!cachedItemSize.isValid()) { // the last item is probaly the largest, so we use its size
2100 int row = model->rowCount(root) - 1;
2101 QModelIndex sample = model->index(row, column, root);
2102 const QAbstractItemDelegate *delegate = delegateForIndex(sample);
2103 cachedItemSize = delegate ? delegate->sizeHint(option, sample) : QSize();
2104 }
2105 return cachedItemSize;
2106}
2107
2108QItemSelection QListViewPrivate::selection(const QRect &rect) const
2109{
2110 QItemSelection selection;
2111 QModelIndex tl, br;
2112 intersectingSet(rect);
2113 QVector<QModelIndex>::iterator it = intersectVector.begin();
2114 for (; it != intersectVector.end(); ++it) {
2115 if (!tl.isValid() && !br.isValid()) {
2116 tl = br = *it;
2117 } else if ((*it).row() == (tl.row() - 1)) {
2118 tl = *it; // expand current range
2119 } else if ((*it).row() == (br.row() + 1)) {
2120 br = (*it); // expand current range
2121 } else {
2122 selection.select(tl, br); // select current range
2123 tl = br = *it; // start new range
2124 }
2125 }
2126
2127 if (tl.isValid() && br.isValid())
2128 selection.select(tl, br);
2129 else if (tl.isValid())
2130 selection.select(tl, tl);
2131 else if (br.isValid())
2132 selection.select(br, br);
2133
2134 return selection;
2135}
2136
2137/*
2138 * Static ListView Implementation
2139*/
2140
2141int QStaticListViewBase::verticalPerItemValue(int itemIndex, int verticalValue, int areaHeight,
2142 bool above, bool below, bool wrap,
2143 QListView::ScrollHint hint, int itemHeight) const
2144{
2145 int value = qBound(0, verticalValue, flowPositions.count() - 1);
2146 if (above)
2147 return perItemScrollToValue(itemIndex, value, areaHeight, QListView::PositionAtTop,
2148 Qt::Vertical,wrap, itemHeight);
2149 else if (below)
2150 return perItemScrollToValue(itemIndex, value, areaHeight, QListView::PositionAtBottom,
2151 Qt::Vertical, wrap, itemHeight);
2152 else if (hint != QListView::EnsureVisible)
2153 return perItemScrollToValue(itemIndex, value, areaHeight, hint, Qt::Vertical, wrap, itemHeight);
2154 return value;
2155}
2156
2157int QStaticListViewBase::horizontalPerItemValue(int itemIndex, int horizontalValue, int areaWidth,
2158 bool leftOf, bool rightOf, bool wrap,
2159 QListView::ScrollHint hint, int itemWidth) const
2160{
2161 int value = qBound(0, horizontalValue, flowPositions.count() - 1);
2162 if (leftOf)
2163 return perItemScrollToValue(itemIndex, value, areaWidth, QListView::PositionAtTop,
2164 Qt::Horizontal, wrap, itemWidth);
2165 else if (rightOf)
2166 return perItemScrollToValue(itemIndex, value, areaWidth, QListView::PositionAtBottom,
2167 Qt::Horizontal, wrap, itemWidth);
2168 else if (hint != QListView::EnsureVisible)
2169 return perItemScrollToValue(itemIndex, value, areaWidth, hint, Qt::Horizontal, wrap, itemWidth);
2170 return value;
2171}
2172
2173void QStaticListViewBase::scrollContentsBy(int &dx, int &dy)
2174{
2175 // ### reorder this logic
2176 const int verticalValue = verticalScrollBarValue();
2177 const int horizontalValue = horizontalScrollBarValue();
2178 const bool vertical = (verticalScrollMode() == QAbstractItemView::ScrollPerItem);
2179 const bool horizontal = (horizontalScrollMode() == QAbstractItemView::ScrollPerItem);
2180
2181 if (isWrapping()) {
2182 if (segmentPositions.isEmpty())
2183 return;
2184 const int max = segmentPositions.count() - 1;
2185 if (horizontal && flow() == QListView::TopToBottom && dx != 0) {
2186 int currentValue = qBound(0, horizontalValue, max);
2187 int previousValue = qBound(0, currentValue + dx, max);
2188 int currentCoordinate = segmentPositions.at(currentValue);
2189 int previousCoordinate = segmentPositions.at(previousValue);
2190 dx = previousCoordinate - currentCoordinate;
2191 } else if (vertical && flow() == QListView::LeftToRight && dy != 0) {
2192 int currentValue = qBound(0, verticalValue, max);
2193 int previousValue = qBound(0, currentValue + dy, max);
2194 int currentCoordinate = segmentPositions.at(currentValue);
2195 int previousCoordinate = segmentPositions.at(previousValue);
2196 dy = previousCoordinate - currentCoordinate;
2197 }
2198 } else {
2199 if (flowPositions.isEmpty())
2200 return;
2201 const int max = flowPositions.count() - 1;
2202 if (vertical && flow() == QListView::TopToBottom && dy != 0) {
2203 int currentValue = qBound(0, verticalValue, max);
2204 int previousValue = qBound(0, currentValue + dy, max);
2205 int currentCoordinate = flowPositions.at(currentValue);
2206 int previousCoordinate = flowPositions.at(previousValue);
2207 dy = previousCoordinate - currentCoordinate;
2208 } else if (horizontal && flow() == QListView::LeftToRight && dx != 0) {
2209 int currentValue = qBound(0, horizontalValue, max);
2210 int previousValue = qBound(0, currentValue + dx, max);
2211 int currentCoordinate = flowPositions.at(currentValue);
2212 int previousCoordinate = flowPositions.at(previousValue);
2213 dx = previousCoordinate - currentCoordinate;
2214 }
2215 }
2216}
2217
2218bool QStaticListViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max)
2219{
2220 doStaticLayout(info);
2221 if (batchStartRow > max) { // stop items layout
2222 flowPositions.resize(flowPositions.count());
2223 segmentPositions.resize(segmentPositions.count());
2224 segmentStartRows.resize(segmentStartRows.count());
2225 return true; // done
2226 }
2227 return false; // not done
2228}
2229
2230QListViewItem QStaticListViewBase::indexToListViewItem(const QModelIndex &index) const
2231{
2232 if (flowPositions.isEmpty()
2233 || segmentPositions.isEmpty()
2234 || index.row() > flowPositions.count())
2235 return QListViewItem();
2236
2237 const int segment = qBinarySearch<int>(segmentStartRows, index.row(),
2238 0, segmentStartRows.count() - 1);
2239
2240
2241 QStyleOptionViewItemV4 options = viewOptions();
2242 options.rect.setSize(contentsSize);
2243 QSize size = (uniformItemSizes() && cachedItemSize().isValid())
2244 ? cachedItemSize() : itemSize(options, index);
2245
2246 QPoint pos;
2247 if (flow() == QListView::LeftToRight) {
2248 pos.setX(flowPositions.at(index.row()));
2249 pos.setY(segmentPositions.at(segment));
2250 } else { // TopToBottom
2251 pos.setY(flowPositions.at(index.row()));
2252 pos.setX(segmentPositions.at(segment));
2253 if (isWrapping()) { // make the items as wide as the segment
2254 int right = (segment + 1 >= segmentPositions.count()
2255 ? contentsSize.width()
2256 : segmentPositions.at(segment + 1));
2257 size.setWidth(right - pos.x());
2258 } else { // make the items as wide as the viewport
2259 size.setWidth(qMax(size.width(), viewport()->width()));
2260 }
2261 }
2262
2263 return QListViewItem(QRect(pos, size), index.row());
2264}
2265
2266QPoint QStaticListViewBase::initStaticLayout(const QListViewLayoutInfo &info)
2267{
2268 int x, y;
2269 if (info.first == 0) {
2270 flowPositions.clear();
2271 segmentPositions.clear();
2272 segmentStartRows.clear();
2273 segmentExtents.clear();
2274 x = info.bounds.left() + info.spacing;
2275 y = info.bounds.top() + info.spacing;
2276 segmentPositions.append(info.flow == QListView::LeftToRight ? y : x);
2277 segmentStartRows.append(0);
2278 } else if (info.wrap) {
2279 if (info.flow == QListView::LeftToRight) {
2280 x = batchSavedPosition;
2281 y = segmentPositions.last();
2282 } else { // flow == QListView::TopToBottom
2283 x = segmentPositions.last();
2284 y = batchSavedPosition;
2285 }
2286 } else { // not first and not wrap
2287 if (info.flow == QListView::LeftToRight) {
2288 x = batchSavedPosition;
2289 y = info.bounds.top() + info.spacing;
2290 } else { // flow == QListView::TopToBottom
2291 x = info.bounds.left() + info.spacing;
2292 y = batchSavedPosition;
2293 }
2294 }
2295 return QPoint(x, y);
2296}
2297
2298/*!
2299 \internal
2300*/
2301void QStaticListViewBase::doStaticLayout(const QListViewLayoutInfo &info)
2302{
2303 const bool useItemSize = !info.grid.isValid();
2304 const QPoint topLeft = initStaticLayout(info);
2305 QStyleOptionViewItemV4 option = viewOptions();
2306 option.rect = info.bounds;
2307
2308 // The static layout data structures are as follows:
2309 // One vector contains the coordinate in the direction of layout flow.
2310 // Another vector contains the coordinates of the segments.
2311 // A third vector contains the index (model row) of the first item
2312 // of each segment.
2313
2314 int segStartPosition;
2315 int segEndPosition;
2316 int deltaFlowPosition;
2317 int deltaSegPosition;
2318 int deltaSegHint;
2319 int flowPosition;
2320 int segPosition;
2321
2322 if (info.flow == QListView::LeftToRight) {
2323 segStartPosition = info.bounds.left();
2324 segEndPosition = info.bounds.width();
2325 flowPosition = topLeft.x();
2326 segPosition = topLeft.y();
2327 deltaFlowPosition = info.grid.width(); // dx
2328 deltaSegPosition = useItemSize ? batchSavedDeltaSeg : info.grid.height(); // dy
2329 deltaSegHint = info.grid.height();
2330 } else { // flow == QListView::TopToBottom
2331 segStartPosition = info.bounds.top();
2332 segEndPosition = info.bounds.height();
2333 flowPosition = topLeft.y();
2334 segPosition = topLeft.x();
2335 deltaFlowPosition = info.grid.height(); // dy
2336 deltaSegPosition = useItemSize ? batchSavedDeltaSeg : info.grid.width(); // dx
2337 deltaSegHint = info.grid.width();
2338 }
2339
2340 for (int row = info.first; row <= info.last; ++row) {
2341 if (isHidden(row)) { // ###
2342 flowPositions.append(flowPosition);
2343 } else {
2344 // if we are not using a grid, we need to find the deltas
2345 if (useItemSize) {
2346 QSize hint = itemSize(option, modelIndex(row));
2347 if (info.flow == QListView::LeftToRight) {
2348 deltaFlowPosition = hint.width() + info.spacing;
2349 deltaSegHint = hint.height() + info.spacing;
2350 } else { // TopToBottom
2351 deltaFlowPosition = hint.height() + info.spacing;
2352 deltaSegHint = hint.width() + info.spacing;
2353 }
2354 }
2355 // create new segment
2356 if (info.wrap && (flowPosition + deltaFlowPosition >= segEndPosition)) {
2357 segmentExtents.append(flowPosition);
2358 flowPosition = info.spacing + segStartPosition;
2359 segPosition += deltaSegPosition;
2360 segmentPositions.append(segPosition);
2361 segmentStartRows.append(row);
2362 deltaSegPosition = 0;
2363 }
2364 // save the flow position of this item
2365 flowPositions.append(flowPosition);
2366 // prepare for the next item
2367 deltaSegPosition = qMax(deltaSegHint, deltaSegPosition);
2368 flowPosition += info.spacing + deltaFlowPosition;
2369 }
2370 }
2371 // used when laying out next batch
2372 batchSavedPosition = flowPosition;
2373 batchSavedDeltaSeg = deltaSegPosition;
2374 batchStartRow = info.last + 1;
2375 if (info.last == info.max)
2376 flowPosition -= info.spacing; // remove extra spacing
2377 // set the contents size
2378 QRect rect = info.bounds;
2379 if (info.flow == QListView::LeftToRight) {
2380 rect.setRight(segmentPositions.count() == 1 ? flowPosition : info.bounds.right());
2381 rect.setBottom(segPosition + deltaSegPosition);
2382 } else { // TopToBottom
2383 rect.setRight(segPosition + deltaSegPosition);
2384 rect.setBottom(segmentPositions.count() == 1 ? flowPosition : info.bounds.bottom());
2385 }
2386 contentsSize = QSize(rect.right(), rect.bottom());
2387 // if it is the last batch, save the end of the segments
2388 if (info.last == info.max) {
2389 segmentExtents.append(flowPosition);
2390 flowPositions.append(flowPosition);
2391 segmentPositions.append(info.wrap ? segPosition + deltaSegPosition : INT_MAX);
2392 }
2393 // if the new items are visble, update the viewport
2394 QRect changedRect(topLeft, rect.bottomRight());
2395 if (clipRect().intersects(changedRect))
2396 viewport()->update();
2397}
2398
2399/*!
2400 \internal
2401 Finds the set of items intersecting with \a area.
2402 In this function, itemsize is counted from topleft to the start of the next item.
2403*/
2404void QStaticListViewBase::intersectingStaticSet(const QRect &area) const
2405{
2406 clearIntersections();
2407 int segStartPosition;
2408 int segEndPosition;
2409 int flowStartPosition;
2410 int flowEndPosition;
2411 if (flow() == QListView::LeftToRight) {
2412 segStartPosition = area.top();
2413 segEndPosition = area.bottom();
2414 flowStartPosition = area.left();
2415 flowEndPosition = area.right();
2416 } else {
2417 segStartPosition = area.left();
2418 segEndPosition = area.right();
2419 flowStartPosition = area.top();
2420 flowEndPosition = area.bottom();
2421 }
2422 if (segmentPositions.count() < 2 || flowPositions.isEmpty())
2423 return;
2424 // the last segment position is actually the edge of the last segment
2425 const int segLast = segmentPositions.count() - 2;
2426 int seg = qBinarySearch<int>(segmentPositions, segStartPosition, 0, segLast + 1);
2427 for (; seg <= segLast && segmentPositions.at(seg) <= segEndPosition; ++seg) {
2428 int first = segmentStartRows.at(seg);
2429 int last = (seg < segLast ? segmentStartRows.at(seg + 1) : batchStartRow) - 1;
2430 if (segmentExtents.at(seg) < flowStartPosition)
2431 continue;
2432 int row = qBinarySearch<int>(flowPositions, flowStartPosition, first, last);
2433 for (; row <= last && flowPositions.at(row) <= flowEndPosition; ++row) {
2434 if (isHidden(row))
2435 continue;
2436 QModelIndex index = modelIndex(row);
2437 if (index.isValid())
2438 appendToIntersections(index);
2439#if 0 // for debugging
2440 else
2441 qWarning("intersectingStaticSet: row %d was invalid", row);
2442#endif
2443 }
2444 }
2445}
2446
2447int QStaticListViewBase::itemIndex(const QListViewItem &item) const
2448{
2449 return item.indexHint;
2450}
2451
2452QRect QStaticListViewBase::mapToViewport(const QRect &rect) const
2453{
2454 if (isWrapping())
2455 return rect;
2456 // If the listview is in "listbox-mode", the items are as wide as the view.
2457 QRect result = rect;
2458 QSize vsize = viewport()->size();
2459 QSize csize = contentsSize;
2460 if (flow() == QListView::TopToBottom) {
2461 result.setLeft(spacing());
2462 result.setWidth(qMax(csize.width(), vsize.width()) - 2 * spacing());
2463 } else { // LeftToRight
2464 result.setTop(spacing());
2465 result.setHeight(qMax(csize.height(), vsize.height()) - 2 * spacing());
2466 }
2467 return result;
2468}
2469
2470int QStaticListViewBase::perItemScrollingPageSteps(int length, int bounds, bool wrap) const
2471{
2472 const QVector<int> positions = (wrap ? segmentPositions : flowPositions);
2473 if (positions.isEmpty() || bounds <= length)
2474 return positions.count();
2475 if (uniformItemSizes()) {
2476 for (int i = 1; i < positions.count(); ++i)
2477 if (positions.at(i) > 0)
2478 return length / positions.at(i);
2479 return 0; // all items had height 0
2480 }
2481 int pageSteps = 0;
2482 int steps = positions.count() - 1;
2483 int max = qMax(length, bounds);
2484 int min = qMin(length, bounds);
2485 int pos = min - (max - positions.last());
2486
2487 while (pos >= 0 && steps > 0) {
2488 pos -= (positions.at(steps) - positions.at(steps - 1));
2489 if (pos >= 0) //this item should be visible
2490 ++pageSteps;
2491 --steps;
2492 }
2493
2494 // at this point we know that positions has at least one entry
2495 return qMax(pageSteps, 1);
2496}
2497
2498int QStaticListViewBase::perItemScrollToValue(int index, int scrollValue, int viewportSize,
2499 QAbstractItemView::ScrollHint hint,
2500 Qt::Orientation orientation, bool wrap, int itemExtent) const
2501{
2502 if (index < 0)
2503 return scrollValue;
2504 if (!wrap) {
2505 int topIndex = index;
2506 const int bottomIndex = topIndex;
2507 const int bottomCoordinate = flowPositions.at(index);
2508
2509 while (topIndex > 0 &&
2510 (bottomCoordinate - flowPositions.at(topIndex-1) + itemExtent) <= (viewportSize)) {
2511 topIndex--;
2512 }
2513
2514 const int itemCount = bottomIndex - topIndex + 1;
2515 switch (hint) {
2516 case QAbstractItemView::PositionAtTop:
2517 return index;
2518 case QAbstractItemView::PositionAtBottom:
2519 return index - itemCount + 1;
2520 case QAbstractItemView::PositionAtCenter:
2521 return index - (itemCount / 2);
2522 default:
2523 break;
2524 }
2525 } else { // wrapping
2526 Qt::Orientation flowOrientation = (flow() == QListView::LeftToRight
2527 ? Qt::Horizontal : Qt::Vertical);
2528 if (flowOrientation == orientation) { // scrolling in the "flow" direction
2529 // ### wrapped scrolling in the flow direction
2530 return flowPositions.at(index); // ### always pixel based for now
2531 } else if (!segmentStartRows.isEmpty()) { // we are scrolling in the "segment" direction
2532 int segment = qBinarySearch<int>(segmentStartRows, index, 0, segmentStartRows.count() - 1);
2533 int leftSegment = segment;
2534 const int rightSegment = leftSegment;
2535 const int bottomCoordinate = segmentPositions.at(segment);
2536
2537 while (leftSegment > scrollValue &&
2538 (bottomCoordinate - segmentPositions.at(leftSegment-1) + itemExtent) <= (viewportSize)) {
2539 leftSegment--;
2540 }
2541
2542 const int segmentCount = rightSegment - leftSegment + 1;
2543 switch (hint) {
2544 case QAbstractItemView::PositionAtTop:
2545 return segment;
2546 case QAbstractItemView::PositionAtBottom:
2547 return segment - segmentCount + 1;
2548 case QAbstractItemView::PositionAtCenter:
2549 return segment - (segmentCount / 2);
2550 default:
2551 break;
2552 }
2553 }
2554 }
2555 return scrollValue;
2556}
2557
2558void QStaticListViewBase::clear()
2559{
2560 flowPositions.clear();
2561 segmentPositions.clear();
2562 segmentStartRows.clear();
2563 segmentExtents.clear();
2564 batchSavedPosition = 0;
2565 batchStartRow = 0;
2566 batchSavedDeltaSeg = 0;
2567}
2568
2569/*
2570 * Dynamic ListView Implementation
2571*/
2572
2573void QDynamicListViewBase::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
2574{
2575 if (column() >= topLeft.column() && column() <= bottomRight.column()) {
2576 QStyleOptionViewItemV4 option = viewOptions();
2577 int bottom = qMin(items.count(), bottomRight.row() + 1);
2578 for (int row = topLeft.row(); row < bottom; ++row)
2579 items[row].resize(itemSize(option, modelIndex(row)));
2580 }
2581}
2582
2583bool QDynamicListViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max)
2584{
2585 if (info.last >= items.count()) {
2586 createItems(info.last + 1);
2587 doDynamicLayout(info);
2588 }
2589 return (batchStartRow > max); // done
2590}
2591
2592QListViewItem QDynamicListViewBase::indexToListViewItem(const QModelIndex &index) const
2593{
2594 if (index.isValid() && index.row() < items.count())
2595 return items.at(index.row());
2596 return QListViewItem();
2597}
2598
2599void QDynamicListViewBase::initBspTree(const QSize &contents)
2600{
2601 // remove all items from the tree
2602 int leafCount = tree.leafCount();
2603 for (int l = 0; l < leafCount; ++l)
2604 tree.leaf(l).clear();
2605 // we have to get the bounding rect of the items before we can initialize the tree
2606 QBspTree::Node::Type type = QBspTree::Node::Both; // 2D
2607 // simple heuristics to get better bsp
2608 if (contents.height() / contents.width() >= 3)
2609 type = QBspTree::Node::HorizontalPlane;
2610 else if (contents.width() / contents.height() >= 3)
2611 type = QBspTree::Node::VerticalPlane;
2612 // build tree for the bounding rect (not just the contents rect)
2613 tree.init(QRect(0, 0, contents.width(), contents.height()), type);
2614}
2615
2616QPoint QDynamicListViewBase::initDynamicLayout(const QListViewLayoutInfo &info)
2617{
2618 int x, y;
2619 if (info.first == 0) {
2620 x = info.bounds.x() + info.spacing;
2621 y = info.bounds.y() + info.spacing;
2622 items.reserve(rowCount() - hiddenCount());
2623 } else {
2624 const QListViewItem item = items.at(info.first - 1);
2625 x = item.x;
2626 y = item.y;
2627 if (info.flow == QListView::LeftToRight)
2628 x += (info.grid.isValid() ? info.grid.width() : item.w) + info.spacing;
2629 else
2630 y += (info.grid.isValid() ? info.grid.height() : item.h) + info.spacing;
2631 }
2632 return QPoint(x, y);
2633}
2634
2635/*!
2636 \internal
2637*/
2638void QDynamicListViewBase::doDynamicLayout(const QListViewLayoutInfo &info)
2639{
2640 const bool useItemSize = !info.grid.isValid();
2641 const QPoint topLeft = initDynamicLayout(info);
2642
2643 int segStartPosition;
2644 int segEndPosition;
2645 int deltaFlowPosition;
2646 int deltaSegPosition;
2647 int deltaSegHint;
2648 int flowPosition;
2649 int segPosition;
2650
2651 if (info.flow == QListView::LeftToRight) {
2652 segStartPosition = info.bounds.left() + info.spacing;
2653 segEndPosition = info.bounds.right();
2654 deltaFlowPosition = info.grid.width(); // dx
2655 deltaSegPosition = (useItemSize ? batchSavedDeltaSeg : info.grid.height()); // dy
2656 deltaSegHint = info.grid.height();
2657 flowPosition = topLeft.x();
2658 segPosition = topLeft.y();
2659 } else { // flow == QListView::TopToBottom
2660 segStartPosition = info.bounds.top() + info.spacing;
2661 segEndPosition = info.bounds.bottom();
2662 deltaFlowPosition = info.grid.height(); // dy
2663 deltaSegPosition = (useItemSize ? batchSavedDeltaSeg : info.grid.width()); // dx
2664 deltaSegHint = info.grid.width();
2665 flowPosition = topLeft.y();
2666 segPosition = topLeft.x();
2667 }
2668
2669 if (moved.count() != items.count())
2670 moved.resize(items.count());
2671
2672 QRect rect(QPoint(0, 0), topLeft);
2673 QListViewItem *item = 0;
2674 for (int row = info.first; row <= info.last; ++row) {
2675 item = &items[row];
2676 if (isHidden(row)) {
2677 item->invalidate();
2678 } else {
2679 // if we are not using a grid, we need to find the deltas
2680 if (useItemSize) {
2681 if (info.flow == QListView::LeftToRight)
2682 deltaFlowPosition = item->w + info.spacing;
2683 else
2684 deltaFlowPosition = item->h + info.spacing;
2685 } else {
2686 item->w = qMin<int>(info.grid.width(), item->w);
2687 item->h = qMin<int>(info.grid.height(), item->h);
2688 }
2689
2690 // create new segment
2691 if (info.wrap
2692 && flowPosition + deltaFlowPosition > segEndPosition
2693 && flowPosition > segStartPosition) {
2694 flowPosition = segStartPosition;
2695 segPosition += deltaSegPosition;
2696 if (useItemSize)
2697 deltaSegPosition = 0;
2698 }
2699 // We must delay calculation of the seg adjustment, as this item
2700 // may have caused a wrap to occur
2701 if (useItemSize) {
2702 if (info.flow == QListView::LeftToRight)
2703 deltaSegHint = item->h + info.spacing;
2704 else
2705 deltaSegHint = item->w + info.spacing;
2706 deltaSegPosition = qMax(deltaSegPosition, deltaSegHint);
2707 }
2708
2709 // set the position of the item
2710 // ### idealy we should have some sort of alignment hint for the item
2711 // ### (normally that would be a point between the icon and the text)
2712 if (!moved.testBit(row)) {
2713 if (info.flow == QListView::LeftToRight) {
2714 if (useItemSize) {
2715 item->x = flowPosition;
2716 item->y = segPosition;
2717 } else { // use grid
2718 item->x = flowPosition + ((deltaFlowPosition - item->w) / 2);
2719 item->y = segPosition;
2720 }
2721 } else { // TopToBottom
2722 if (useItemSize) {
2723 item->y = flowPosition;
2724 item->x = segPosition;
2725 } else { // use grid
2726 item->y = flowPosition + ((deltaFlowPosition - item->h) / 2);
2727 item->x = segPosition;
2728 }
2729 }
2730 }
2731
2732 // let the contents contain the new item
2733 if (useItemSize)
2734 rect |= item->rect();
2735 else if (info.flow == QListView::LeftToRight)
2736 rect |= QRect(flowPosition, segPosition, deltaFlowPosition, deltaSegPosition);
2737 else // flow == TopToBottom
2738 rect |= QRect(segPosition, flowPosition, deltaSegPosition, deltaFlowPosition);
2739
2740 // prepare for next item
2741 flowPosition += deltaFlowPosition; // current position + item width + gap
2742 }
2743 }
2744 batchSavedDeltaSeg = deltaSegPosition;
2745 batchStartRow = info.last + 1;
2746 bool done = (info.last >= rowCount() - 1);
2747 // resize the content area
2748 if (done || !info.bounds.contains(item->rect()))
2749 contentsSize = QSize(rect.width(), rect.height());
2750 // resize tree
2751 int insertFrom = info.first;
2752 if (done || info.first == 0) {
2753 initBspTree(rect.size());
2754 insertFrom = 0;
2755 }
2756 // insert items in tree
2757 for (int row = insertFrom; row <= info.last; ++row)
2758 tree.insertLeaf(items.at(row).rect(), row);
2759 // if the new items are visble, update the viewport
2760 QRect changedRect(topLeft, rect.bottomRight());
2761 if (clipRect().intersects(changedRect))
2762 viewport()->update();
2763}
2764
2765void QDynamicListViewBase::intersectingDynamicSet(const QRect &area) const
2766{
2767 clearIntersections();
2768 QListViewPrivate *that = const_cast<QListViewPrivate*>(dd);
2769 QBspTree::Data data(static_cast<void*>(that));
2770 that->dynamicListView->tree.climbTree(area, &QDynamicListViewBase::addLeaf, data);
2771}
2772
2773void QDynamicListViewBase::createItems(int to)
2774{
2775 int count = items.count();
2776 QSize size;
2777 QStyleOptionViewItemV4 option = viewOptions();
2778 for (int row = count; row < to; ++row) {
2779 size = itemSize(option, modelIndex(row));
2780 QListViewItem item(QRect(0, 0, size.width(), size.height()), row); // default pos
2781 items.append(item);
2782 }
2783}
2784
2785void QDynamicListViewBase::drawItems(QPainter *painter, const QVector<QModelIndex> &indexes) const
2786{
2787 QStyleOptionViewItemV4 option = viewOptions();
2788 option.state &= ~QStyle::State_MouseOver;
2789 QVector<QModelIndex>::const_iterator it = indexes.begin();
2790 QListViewItem item = indexToListViewItem(*it);
2791 for (; it != indexes.end(); ++it) {
2792 item = indexToListViewItem(*it);
2793 option.rect = viewItemRect(item);
2794 delegate(*it)->paint(painter, option, *it);
2795 }
2796}
2797
2798QRect QDynamicListViewBase::itemsRect(const QVector<QModelIndex> &indexes) const
2799{
2800 QVector<QModelIndex>::const_iterator it = indexes.begin();
2801 QListViewItem item = indexToListViewItem(*it);
2802 QRect rect(item.x, item.y, item.w, item.h);
2803 for (; it != indexes.end(); ++it) {
2804 item = indexToListViewItem(*it);
2805 rect |= viewItemRect(item);
2806 }
2807 return rect;
2808}
2809
2810int QDynamicListViewBase::itemIndex(const QListViewItem &item) const
2811{
2812 if (!item.isValid())
2813 return -1;
2814 int i = item.indexHint;
2815 if (i < items.count()) {
2816 if (items.at(i) == item)
2817 return i;
2818 } else {
2819 i = items.count() - 1;
2820 }
2821
2822 int j = i;
2823 int c = items.count();
2824 bool a = true;
2825 bool b = true;
2826
2827 while (a || b) {
2828 if (a) {
2829 if (items.at(i) == item) {
2830 items.at(i).indexHint = i;
2831 return i;
2832 }
2833 a = ++i < c;
2834 }
2835 if (b) {
2836 if (items.at(j) == item) {
2837 items.at(j).indexHint = j;
2838 return j;
2839 }
2840 b = --j > -1;
2841 }
2842 }
2843 return -1;
2844}
2845
2846void QDynamicListViewBase::addLeaf(QVector<int> &leaf, const QRect &area,
2847 uint visited, QBspTree::Data data)
2848{
2849 QListViewItem *vi;
2850 QListViewPrivate *_this = static_cast<QListViewPrivate *>(data.ptr);
2851 for (int i = 0; i < leaf.count(); ++i) {
2852 int idx = leaf.at(i);
2853 if (idx < 0 || idx >= _this->dynamicListView->items.count())
2854 continue;
2855 vi = &_this->dynamicListView->items[idx];
2856 Q_ASSERT(vi);
2857 if (vi->isValid() && vi->rect().intersects(area) && vi->visited != visited) {
2858 QModelIndex index = _this->listViewItemToIndex(*vi);
2859 Q_ASSERT(index.isValid());
2860 _this->intersectVector.append(index);
2861 vi->visited = visited;
2862 }
2863 }
2864}
2865
2866void QDynamicListViewBase::insertItem(int index)
2867{
2868 if (index >= 0 && index < items.count())
2869 tree.insertLeaf(items.at(index).rect(), index);
2870}
2871
2872void QDynamicListViewBase::removeItem(int index)
2873{
2874 if (index >= 0 && index < items.count())
2875 tree.removeLeaf(items.at(index).rect(), index);
2876}
2877
2878void QDynamicListViewBase::moveItem(int index, const QPoint &dest)
2879{
2880 // does not impact on the bintree itself or the contents rect
2881 QListViewItem *item = &items[index];
2882 QRect rect = item->rect();
2883
2884 // move the item without removing it from the tree
2885 tree.removeLeaf(rect, index);
2886 item->move(dest);
2887 tree.insertLeaf(QRect(dest, rect.size()), index);
2888
2889 // resize the contents area
2890 contentsSize = (QRect(QPoint(0, 0), contentsSize)|QRect(dest, rect.size())).size();
2891
2892 // mark the item as moved
2893 if (moved.count() != items.count())
2894 moved.resize(items.count());
2895 moved.setBit(index, true);
2896}
2897
2898QPoint QDynamicListViewBase::snapToGrid(const QPoint &pos) const
2899{
2900 int x = pos.x() - (pos.x() % gridSize().width());
2901 int y = pos.y() - (pos.y() % gridSize().height());
2902 return QPoint(x, y);
2903}
2904
2905QPoint QDynamicListViewBase::draggedItemsDelta() const
2906{
2907 if (movement() == QListView::Snap) {
2908 QPoint snapdelta = QPoint((offset().x() % gridSize().width()),
2909 (offset().y() % gridSize().height()));
2910 return snapToGrid(draggedItemsPos + snapdelta) - snapToGrid(pressedPosition()) - snapdelta;
2911 }
2912 return draggedItemsPos - pressedPosition();
2913}
2914
2915QRect QDynamicListViewBase::draggedItemsRect() const
2916{
2917 QRect rect = itemsRect(draggedItems);
2918 rect.translate(draggedItemsDelta());
2919 return rect;
2920}
2921
2922void QListViewPrivate::scrollElasticBandBy(int dx, int dy)
2923{
2924 if (dx > 0) // right
2925 elasticBand.moveRight(elasticBand.right() + dx);
2926 else if (dx < 0) // left
2927 elasticBand.moveLeft(elasticBand.left() - dx);
2928 if (dy > 0) // down
2929 elasticBand.moveBottom(elasticBand.bottom() + dy);
2930 else if (dy < 0) // up
2931 elasticBand.moveTop(elasticBand.top() - dy);
2932}
2933
2934void QDynamicListViewBase::clear()
2935{
2936 tree.destroy();
2937 items.clear();
2938 moved.clear();
2939 batchStartRow = 0;
2940 batchSavedDeltaSeg = 0;
2941}
2942
2943void QDynamicListViewBase::updateContentsSize()
2944{
2945 QRect bounding;
2946 for (int i = 0; i < items.count(); ++i)
2947 bounding |= items.at(i).rect();
2948 contentsSize = bounding.size();
2949}
2950
2951/*!
2952 \reimp
2953*/
2954void QListView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
2955{
2956#ifndef QT_NO_ACCESSIBILITY
2957 if (QAccessible::isActive()) {
2958 if (current.isValid()) {
2959 int entry = visualIndex(current) + 1;
2960 QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus);
2961 }
2962 }
2963#endif
2964 QAbstractItemView::currentChanged(current, previous);
2965}
2966
2967/*!
2968 \reimp
2969*/
2970void QListView::selectionChanged(const QItemSelection &selected,
2971 const QItemSelection &deselected)
2972{
2973#ifndef QT_NO_ACCESSIBILITY
2974 if (QAccessible::isActive()) {
2975 // ### does not work properly for selection ranges.
2976 QModelIndex sel = selected.indexes().value(0);
2977 if (sel.isValid()) {
2978 int entry = visualIndex(sel) + 1;
2979 QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection);
2980 }
2981 QModelIndex desel = deselected.indexes().value(0);
2982 if (desel.isValid()) {
2983 int entry = visualIndex(desel) + 1;
2984 QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove);
2985 }
2986 }
2987#endif
2988 QAbstractItemView::selectionChanged(selected, deselected);
2989}
2990
2991int QListView::visualIndex(const QModelIndex &index) const
2992{
2993 Q_D(const QListView);
2994 d->executePostedLayout();
2995 QListViewItem itm = d->indexToListViewItem(index);
2996 return d->itemIndex(itm);
2997}
2998
2999QT_END_NAMESPACE
3000
3001#endif // QT_NO_LISTVIEW
Note: See TracBrowser for help on using the repository browser.