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