|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
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 around the items in the layout |
|
361 |
|
362 This property is the size of the empty space that is padded around |
|
363 an item in the layout. |
|
364 |
|
365 Setting this property when the view is visible will cause the |
|
366 items to be laid out again. |
|
367 |
|
368 By default, this property contains a value of 0. |
|
369 |
|
370 \sa viewMode |
|
371 */ |
|
372 // ### Qt5: Use same semantic as layouts (spacing is the size of space |
|
373 // *between* items) |
|
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 Q_D(QListView); |
|
857 if (!d->commonListView->filterDragMoveEvent(e)) { |
|
858 if (viewMode() == QListView::ListMode && flow() == QListView::LeftToRight) |
|
859 static_cast<QListModeViewBase *>(d->commonListView)->dragMoveEvent(e); |
|
860 else |
|
861 QAbstractItemView::dragMoveEvent(e); |
|
862 } |
|
863 } |
|
864 |
|
865 |
|
866 /*! |
|
867 \reimp |
|
868 */ |
|
869 void QListView::dragLeaveEvent(QDragLeaveEvent *e) |
|
870 { |
|
871 if (!d_func()->commonListView->filterDragLeaveEvent(e)) |
|
872 QAbstractItemView::dragLeaveEvent(e); |
|
873 } |
|
874 |
|
875 /*! |
|
876 \reimp |
|
877 */ |
|
878 void QListView::dropEvent(QDropEvent *e) |
|
879 { |
|
880 if (!d_func()->commonListView->filterDropEvent(e)) |
|
881 QAbstractItemView::dropEvent(e); |
|
882 } |
|
883 |
|
884 /*! |
|
885 \reimp |
|
886 */ |
|
887 void QListView::startDrag(Qt::DropActions supportedActions) |
|
888 { |
|
889 if (!d_func()->commonListView->filterStartDrag(supportedActions)) |
|
890 QAbstractItemView::startDrag(supportedActions); |
|
891 } |
|
892 |
|
893 /*! |
|
894 \internal |
|
895 |
|
896 Called whenever items from the view is dropped on the viewport. |
|
897 The \a event provides additional information. |
|
898 */ |
|
899 void QListView::internalDrop(QDropEvent *event) |
|
900 { |
|
901 // ### Qt5: remove that function |
|
902 Q_UNUSED(event); |
|
903 } |
|
904 |
|
905 /*! |
|
906 \internal |
|
907 |
|
908 Called whenever the user starts dragging items and the items are movable, |
|
909 enabling internal dragging and dropping of items. |
|
910 */ |
|
911 void QListView::internalDrag(Qt::DropActions supportedActions) |
|
912 { |
|
913 // ### Qt5: remove that function |
|
914 Q_UNUSED(supportedActions); |
|
915 } |
|
916 |
|
917 #endif // QT_NO_DRAGANDDROP |
|
918 |
|
919 /*! |
|
920 \reimp |
|
921 */ |
|
922 QStyleOptionViewItem QListView::viewOptions() const |
|
923 { |
|
924 Q_D(const QListView); |
|
925 QStyleOptionViewItem option = QAbstractItemView::viewOptions(); |
|
926 if (!d->iconSize.isValid()) { // otherwise it was already set in abstractitemview |
|
927 int pm = (d->viewMode == ListMode |
|
928 ? style()->pixelMetric(QStyle::PM_ListViewIconSize, 0, this) |
|
929 : style()->pixelMetric(QStyle::PM_IconViewIconSize, 0, this)); |
|
930 option.decorationSize = QSize(pm, pm); |
|
931 } |
|
932 if (d->viewMode == IconMode) { |
|
933 option.showDecorationSelected = false; |
|
934 option.decorationPosition = QStyleOptionViewItem::Top; |
|
935 option.displayAlignment = Qt::AlignCenter; |
|
936 } else { |
|
937 option.decorationPosition = QStyleOptionViewItem::Left; |
|
938 } |
|
939 return option; |
|
940 } |
|
941 |
|
942 |
|
943 /*! |
|
944 \reimp |
|
945 */ |
|
946 void QListView::paintEvent(QPaintEvent *e) |
|
947 { |
|
948 Q_D(QListView); |
|
949 if (!d->itemDelegate) |
|
950 return; |
|
951 QStyleOptionViewItemV4 option = d->viewOptionsV4(); |
|
952 QPainter painter(d->viewport); |
|
953 |
|
954 const QVector<QModelIndex> toBeRendered = d->intersectingSet(e->rect().translated(horizontalOffset(), verticalOffset()), false); |
|
955 |
|
956 const QModelIndex current = currentIndex(); |
|
957 const QModelIndex hover = d->hover; |
|
958 const QAbstractItemModel *itemModel = d->model; |
|
959 const QItemSelectionModel *selections = d->selectionModel; |
|
960 const bool focus = (hasFocus() || d->viewport->hasFocus()) && current.isValid(); |
|
961 const bool alternate = d->alternatingColors; |
|
962 const QStyle::State state = option.state; |
|
963 const QAbstractItemView::State viewState = this->state(); |
|
964 const bool enabled = (state & QStyle::State_Enabled) != 0; |
|
965 |
|
966 bool alternateBase = false; |
|
967 int previousRow = -2; // trigger the alternateBase adjustment on first pass |
|
968 |
|
969 int maxSize = (flow() == TopToBottom) |
|
970 ? qMax(viewport()->size().width(), d->contentsSize().width()) - 2 * d->spacing() |
|
971 : qMax(viewport()->size().height(), d->contentsSize().height()) - 2 * d->spacing(); |
|
972 |
|
973 QVector<QModelIndex>::const_iterator end = toBeRendered.constEnd(); |
|
974 for (QVector<QModelIndex>::const_iterator it = toBeRendered.constBegin(); it != end; ++it) { |
|
975 Q_ASSERT((*it).isValid()); |
|
976 option.rect = visualRect(*it); |
|
977 |
|
978 if (flow() == TopToBottom) |
|
979 option.rect.setWidth(qMin(maxSize, option.rect.width())); |
|
980 else |
|
981 option.rect.setHeight(qMin(maxSize, option.rect.height())); |
|
982 |
|
983 option.state = state; |
|
984 if (selections && selections->isSelected(*it)) |
|
985 option.state |= QStyle::State_Selected; |
|
986 if (enabled) { |
|
987 QPalette::ColorGroup cg; |
|
988 if ((itemModel->flags(*it) & Qt::ItemIsEnabled) == 0) { |
|
989 option.state &= ~QStyle::State_Enabled; |
|
990 cg = QPalette::Disabled; |
|
991 } else { |
|
992 cg = QPalette::Normal; |
|
993 } |
|
994 option.palette.setCurrentColorGroup(cg); |
|
995 } |
|
996 if (focus && current == *it) { |
|
997 option.state |= QStyle::State_HasFocus; |
|
998 if (viewState == EditingState) |
|
999 option.state |= QStyle::State_Editing; |
|
1000 } |
|
1001 if (*it == hover) |
|
1002 option.state |= QStyle::State_MouseOver; |
|
1003 else |
|
1004 option.state &= ~QStyle::State_MouseOver; |
|
1005 |
|
1006 if (alternate) { |
|
1007 int row = (*it).row(); |
|
1008 if (row != previousRow + 1) { |
|
1009 // adjust alternateBase according to rows in the "gap" |
|
1010 if (!d->hiddenRows.isEmpty()) { |
|
1011 for (int r = qMax(previousRow + 1, 0); r < row; ++r) { |
|
1012 if (!d->isHidden(r)) |
|
1013 alternateBase = !alternateBase; |
|
1014 } |
|
1015 } else { |
|
1016 alternateBase = (row & 1) != 0; |
|
1017 } |
|
1018 } |
|
1019 if (alternateBase) { |
|
1020 option.features |= QStyleOptionViewItemV2::Alternate; |
|
1021 } else { |
|
1022 option.features &= ~QStyleOptionViewItemV2::Alternate; |
|
1023 } |
|
1024 |
|
1025 // draw background of the item (only alternate row). rest of the background |
|
1026 // is provided by the delegate |
|
1027 QStyle::State oldState = option.state; |
|
1028 option.state &= ~QStyle::State_Selected; |
|
1029 style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &option, &painter, this); |
|
1030 option.state = oldState; |
|
1031 |
|
1032 alternateBase = !alternateBase; |
|
1033 previousRow = row; |
|
1034 } |
|
1035 |
|
1036 if (const QWidget *widget = d->editorForIndex(*it).editor) { |
|
1037 QRegion itemGeometry(option.rect); |
|
1038 QRegion widgetGeometry(widget->geometry()); |
|
1039 painter.save(); |
|
1040 painter.setClipRegion(itemGeometry.subtracted(widgetGeometry)); |
|
1041 d->delegateForIndex(*it)->paint(&painter, option, *it); |
|
1042 painter.restore(); |
|
1043 } else { |
|
1044 d->delegateForIndex(*it)->paint(&painter, option, *it); |
|
1045 } |
|
1046 } |
|
1047 |
|
1048 #ifndef QT_NO_DRAGANDDROP |
|
1049 d->commonListView->paintDragDrop(&painter); |
|
1050 #endif |
|
1051 |
|
1052 #ifndef QT_NO_RUBBERBAND |
|
1053 // #### move this implementation into a dynamic class |
|
1054 if (d->showElasticBand && d->elasticBand.isValid()) { |
|
1055 QStyleOptionRubberBand opt; |
|
1056 opt.initFrom(this); |
|
1057 opt.shape = QRubberBand::Rectangle; |
|
1058 opt.opaque = false; |
|
1059 opt.rect = d->mapToViewport(d->elasticBand, false).intersected( |
|
1060 d->viewport->rect().adjusted(-16, -16, 16, 16)); |
|
1061 painter.save(); |
|
1062 style()->drawControl(QStyle::CE_RubberBand, &opt, &painter); |
|
1063 painter.restore(); |
|
1064 } |
|
1065 #endif |
|
1066 } |
|
1067 |
|
1068 /*! |
|
1069 \reimp |
|
1070 */ |
|
1071 QModelIndex QListView::indexAt(const QPoint &p) const |
|
1072 { |
|
1073 Q_D(const QListView); |
|
1074 QRect rect(p.x() + horizontalOffset(), p.y() + verticalOffset(), 1, 1); |
|
1075 const QVector<QModelIndex> intersectVector = d->intersectingSet(rect); |
|
1076 QModelIndex index = intersectVector.count() > 0 |
|
1077 ? intersectVector.last() : QModelIndex(); |
|
1078 if (index.isValid() && visualRect(index).contains(p)) |
|
1079 return index; |
|
1080 return QModelIndex(); |
|
1081 } |
|
1082 |
|
1083 /*! |
|
1084 \reimp |
|
1085 */ |
|
1086 int QListView::horizontalOffset() const |
|
1087 { |
|
1088 return d_func()->commonListView->horizontalOffset(); |
|
1089 } |
|
1090 |
|
1091 /*! |
|
1092 \reimp |
|
1093 */ |
|
1094 int QListView::verticalOffset() const |
|
1095 { |
|
1096 return d_func()->commonListView->verticalOffset(); |
|
1097 } |
|
1098 |
|
1099 /*! |
|
1100 \reimp |
|
1101 */ |
|
1102 QModelIndex QListView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) |
|
1103 { |
|
1104 Q_D(QListView); |
|
1105 Q_UNUSED(modifiers); |
|
1106 |
|
1107 QModelIndex current = currentIndex(); |
|
1108 if (!current.isValid()) { |
|
1109 int rowCount = d->model->rowCount(d->root); |
|
1110 if (!rowCount) |
|
1111 return QModelIndex(); |
|
1112 int row = 0; |
|
1113 while (row < rowCount && d->isHiddenOrDisabled(row)) |
|
1114 ++row; |
|
1115 if (row >= rowCount) |
|
1116 return QModelIndex(); |
|
1117 return d->model->index(row, d->column, d->root); |
|
1118 } |
|
1119 |
|
1120 const QRect initialRect = rectForIndex(current); |
|
1121 QRect rect = initialRect; |
|
1122 if (rect.isEmpty()) { |
|
1123 return d->model->index(0, d->column, d->root); |
|
1124 } |
|
1125 if (d->gridSize().isValid()) rect.setSize(d->gridSize()); |
|
1126 |
|
1127 QSize contents = d->contentsSize(); |
|
1128 QVector<QModelIndex> intersectVector; |
|
1129 |
|
1130 switch (cursorAction) { |
|
1131 case MoveLeft: |
|
1132 while (intersectVector.isEmpty()) { |
|
1133 rect.translate(-rect.width(), 0); |
|
1134 if (rect.right() <= 0) |
|
1135 return current; |
|
1136 if (rect.left() < 0) |
|
1137 rect.setLeft(0); |
|
1138 intersectVector = d->intersectingSet(rect); |
|
1139 d->removeCurrentAndDisabled(&intersectVector, current); |
|
1140 } |
|
1141 return d->closestIndex(initialRect, intersectVector); |
|
1142 case MoveRight: |
|
1143 while (intersectVector.isEmpty()) { |
|
1144 rect.translate(rect.width(), 0); |
|
1145 if (rect.left() >= contents.width()) |
|
1146 return current; |
|
1147 if (rect.right() > contents.width()) |
|
1148 rect.setRight(contents.width()); |
|
1149 intersectVector = d->intersectingSet(rect); |
|
1150 d->removeCurrentAndDisabled(&intersectVector, current); |
|
1151 } |
|
1152 return d->closestIndex(initialRect, intersectVector); |
|
1153 case MovePageUp: |
|
1154 // move current by (visibileRowCount - 1) items. |
|
1155 // rect.translate(0, -rect.height()); will happen in the switch fallthrough for MoveUp. |
|
1156 rect.moveTop(rect.top() - d->viewport->height() + 2 * rect.height()); |
|
1157 if (rect.top() < rect.height()) |
|
1158 rect.moveTop(rect.height()); |
|
1159 case MovePrevious: |
|
1160 case MoveUp: |
|
1161 while (intersectVector.isEmpty()) { |
|
1162 rect.translate(0, -rect.height()); |
|
1163 if (rect.bottom() <= 0) { |
|
1164 #ifdef QT_KEYPAD_NAVIGATION |
|
1165 if (QApplication::keypadNavigationEnabled()) { |
|
1166 int row = d->batchStartRow() - 1; |
|
1167 while (row >= 0 && d->isHiddenOrDisabled(row)) |
|
1168 --row; |
|
1169 if (row >= 0) |
|
1170 return d->model->index(row, d->column, d->root); |
|
1171 } |
|
1172 #endif |
|
1173 return current; |
|
1174 } |
|
1175 if (rect.top() < 0) |
|
1176 rect.setTop(0); |
|
1177 intersectVector = d->intersectingSet(rect); |
|
1178 d->removeCurrentAndDisabled(&intersectVector, current); |
|
1179 } |
|
1180 return d->closestIndex(initialRect, intersectVector); |
|
1181 case MovePageDown: |
|
1182 // move current by (visibileRowCount - 1) items. |
|
1183 // rect.translate(0, rect.height()); will happen in the switch fallthrough for MoveDown. |
|
1184 rect.moveTop(rect.top() + d->viewport->height() - 2 * rect.height()); |
|
1185 if (rect.bottom() > contents.height() - rect.height()) |
|
1186 rect.moveBottom(contents.height() - rect.height()); |
|
1187 case MoveNext: |
|
1188 case MoveDown: |
|
1189 while (intersectVector.isEmpty()) { |
|
1190 rect.translate(0, rect.height()); |
|
1191 if (rect.top() >= contents.height()) { |
|
1192 #ifdef QT_KEYPAD_NAVIGATION |
|
1193 if (QApplication::keypadNavigationEnabled()) { |
|
1194 int rowCount = d->model->rowCount(d->root); |
|
1195 int row = 0; |
|
1196 while (row < rowCount && d->isHiddenOrDisabled(row)) |
|
1197 ++row; |
|
1198 if (row < rowCount) |
|
1199 return d->model->index(row, d->column, d->root); |
|
1200 } |
|
1201 #endif |
|
1202 return current; |
|
1203 } |
|
1204 if (rect.bottom() > contents.height()) |
|
1205 rect.setBottom(contents.height()); |
|
1206 intersectVector = d->intersectingSet(rect); |
|
1207 d->removeCurrentAndDisabled(&intersectVector, current); |
|
1208 } |
|
1209 return d->closestIndex(initialRect, intersectVector); |
|
1210 case MoveHome: |
|
1211 return d->model->index(0, d->column, d->root); |
|
1212 case MoveEnd: |
|
1213 return d->model->index(d->batchStartRow() - 1, d->column, d->root);} |
|
1214 |
|
1215 return current; |
|
1216 } |
|
1217 |
|
1218 /*! |
|
1219 Returns the rectangle of the item at position \a index in the |
|
1220 model. The rectangle is in contents coordinates. |
|
1221 |
|
1222 \sa visualRect() |
|
1223 */ |
|
1224 QRect QListView::rectForIndex(const QModelIndex &index) const |
|
1225 { |
|
1226 return d_func()->rectForIndex(index); |
|
1227 } |
|
1228 |
|
1229 /*! |
|
1230 \since 4.1 |
|
1231 |
|
1232 Sets the contents position of the item at \a index in the model to the given |
|
1233 \a position. |
|
1234 If the list view's movement mode is Static or its view mode is ListView, |
|
1235 this function will have no effect. |
|
1236 */ |
|
1237 void QListView::setPositionForIndex(const QPoint &position, const QModelIndex &index) |
|
1238 { |
|
1239 Q_D(QListView); |
|
1240 if (d->movement == Static |
|
1241 || !d->isIndexValid(index) |
|
1242 || index.parent() != d->root |
|
1243 || index.column() != d->column) |
|
1244 return; |
|
1245 |
|
1246 d->executePostedLayout(); |
|
1247 d->commonListView->setPositionForIndex(position, index); |
|
1248 } |
|
1249 |
|
1250 /*! |
|
1251 \reimp |
|
1252 */ |
|
1253 void QListView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) |
|
1254 { |
|
1255 Q_D(QListView); |
|
1256 if (!d->selectionModel) |
|
1257 return; |
|
1258 |
|
1259 // if we are wrapping, we can only selecte inside the contents rectangle |
|
1260 int w = qMax(d->contentsSize().width(), d->viewport->width()); |
|
1261 int h = qMax(d->contentsSize().height(), d->viewport->height()); |
|
1262 if (d->wrap && !QRect(0, 0, w, h).intersects(rect)) |
|
1263 return; |
|
1264 |
|
1265 QItemSelection selection; |
|
1266 |
|
1267 if (rect.width() == 1 && rect.height() == 1) { |
|
1268 const QVector<QModelIndex> intersectVector = d->intersectingSet(rect.translated(horizontalOffset(), verticalOffset())); |
|
1269 QModelIndex tl; |
|
1270 if (!intersectVector.isEmpty()) |
|
1271 tl = intersectVector.last(); // special case for mouse press; only select the top item |
|
1272 if (tl.isValid() && d->isIndexEnabled(tl)) |
|
1273 selection.select(tl, tl); |
|
1274 } else { |
|
1275 if (state() == DragSelectingState) { // visual selection mode (rubberband selection) |
|
1276 selection = d->selection(rect.translated(horizontalOffset(), verticalOffset())); |
|
1277 } else { // logical selection mode (key and mouse click selection) |
|
1278 QModelIndex tl, br; |
|
1279 // get the first item |
|
1280 const QRect topLeft(rect.left() + horizontalOffset(), rect.top() + verticalOffset(), 1, 1); |
|
1281 QVector<QModelIndex> intersectVector = d->intersectingSet(topLeft); |
|
1282 if (!intersectVector.isEmpty()) |
|
1283 tl = intersectVector.last(); |
|
1284 // get the last item |
|
1285 const QRect bottomRight(rect.right() + horizontalOffset(), rect.bottom() + verticalOffset(), 1, 1); |
|
1286 intersectVector = d->intersectingSet(bottomRight); |
|
1287 if (!intersectVector.isEmpty()) |
|
1288 br = intersectVector.last(); |
|
1289 |
|
1290 // get the ranges |
|
1291 if (tl.isValid() && br.isValid() |
|
1292 && d->isIndexEnabled(tl) |
|
1293 && d->isIndexEnabled(br)) { |
|
1294 QRect first = rectForIndex(tl); |
|
1295 QRect last = rectForIndex(br); |
|
1296 QRect middle; |
|
1297 if (d->flow == LeftToRight) { |
|
1298 QRect &top = first; |
|
1299 QRect &bottom = last; |
|
1300 // if bottom is above top, swap them |
|
1301 if (top.center().y() > bottom.center().y()) { |
|
1302 QRect tmp = top; |
|
1303 top = bottom; |
|
1304 bottom = tmp; |
|
1305 } |
|
1306 // if the rect are on differnet lines, expand |
|
1307 if (top.top() != bottom.top()) { |
|
1308 // top rectangle |
|
1309 if (isRightToLeft()) |
|
1310 top.setLeft(0); |
|
1311 else |
|
1312 top.setRight(contentsSize().width()); |
|
1313 // bottom rectangle |
|
1314 if (isRightToLeft()) |
|
1315 bottom.setRight(contentsSize().width()); |
|
1316 else |
|
1317 bottom.setLeft(0); |
|
1318 } else if (top.left() > bottom.right()) { |
|
1319 if (isRightToLeft()) |
|
1320 bottom.setLeft(top.right()); |
|
1321 else |
|
1322 bottom.setRight(top.left()); |
|
1323 } else { |
|
1324 if (isRightToLeft()) |
|
1325 top.setLeft(bottom.right()); |
|
1326 else |
|
1327 top.setRight(bottom.left()); |
|
1328 } |
|
1329 // middle rectangle |
|
1330 if (top.bottom() < bottom.top()) { |
|
1331 if (gridSize().isValid() && !gridSize().isNull()) |
|
1332 middle.setTop(top.top() + gridSize().height()); |
|
1333 else |
|
1334 middle.setTop(top.bottom() + 1); |
|
1335 middle.setLeft(qMin(top.left(), bottom.left())); |
|
1336 middle.setBottom(bottom.top() - 1); |
|
1337 middle.setRight(qMax(top.right(), bottom.right())); |
|
1338 } |
|
1339 } else { // TopToBottom |
|
1340 QRect &left = first; |
|
1341 QRect &right = last; |
|
1342 if (left.center().x() > right.center().x()) |
|
1343 qSwap(left, right); |
|
1344 |
|
1345 int ch = contentsSize().height(); |
|
1346 if (left.left() != right.left()) { |
|
1347 // left rectangle |
|
1348 if (isRightToLeft()) |
|
1349 left.setTop(0); |
|
1350 else |
|
1351 left.setBottom(ch); |
|
1352 |
|
1353 // top rectangle |
|
1354 if (isRightToLeft()) |
|
1355 right.setBottom(ch); |
|
1356 else |
|
1357 right.setTop(0); |
|
1358 // only set middle if the |
|
1359 middle.setTop(0); |
|
1360 middle.setBottom(ch); |
|
1361 if (gridSize().isValid() && !gridSize().isNull()) |
|
1362 middle.setLeft(left.left() + gridSize().width()); |
|
1363 else |
|
1364 middle.setLeft(left.right() + 1); |
|
1365 middle.setRight(right.left() - 1); |
|
1366 } else if (left.bottom() < right.top()) { |
|
1367 left.setBottom(right.top() - 1); |
|
1368 } else { |
|
1369 right.setBottom(left.top() - 1); |
|
1370 } |
|
1371 } |
|
1372 |
|
1373 // do the selections |
|
1374 QItemSelection topSelection = d->selection(first); |
|
1375 QItemSelection middleSelection = d->selection(middle); |
|
1376 QItemSelection bottomSelection = d->selection(last); |
|
1377 // merge |
|
1378 selection.merge(topSelection, QItemSelectionModel::Select); |
|
1379 selection.merge(middleSelection, QItemSelectionModel::Select); |
|
1380 selection.merge(bottomSelection, QItemSelectionModel::Select); |
|
1381 } |
|
1382 } |
|
1383 } |
|
1384 |
|
1385 d->selectionModel->select(selection, command); |
|
1386 } |
|
1387 |
|
1388 /*! |
|
1389 \reimp |
|
1390 */ |
|
1391 QRegion QListView::visualRegionForSelection(const QItemSelection &selection) const |
|
1392 { |
|
1393 Q_D(const QListView); |
|
1394 // ### NOTE: this is a potential bottleneck in non-static mode |
|
1395 int c = d->column; |
|
1396 QRegion selectionRegion; |
|
1397 for (int i = 0; i < selection.count(); ++i) { |
|
1398 if (!selection.at(i).isValid()) |
|
1399 continue; |
|
1400 QModelIndex parent = selection.at(i).topLeft().parent(); |
|
1401 //we only display the children of the root in a listview |
|
1402 //we're not interested in the other model indexes |
|
1403 if (parent != d->root) |
|
1404 continue; |
|
1405 int t = selection.at(i).topLeft().row(); |
|
1406 int b = selection.at(i).bottomRight().row(); |
|
1407 if (d->viewMode == IconMode || d->isWrapping()) { // in non-static mode, we have to go through all selected items |
|
1408 for (int r = t; r <= b; ++r) |
|
1409 selectionRegion += QRegion(visualRect(d->model->index(r, c, parent))); |
|
1410 } else { // in static mode, we can optimize a bit |
|
1411 while (t <= b && d->isHidden(t)) ++t; |
|
1412 while (b >= t && d->isHidden(b)) --b; |
|
1413 const QModelIndex top = d->model->index(t, c, parent); |
|
1414 const QModelIndex bottom = d->model->index(b, c, parent); |
|
1415 QRect rect(visualRect(top).topLeft(), |
|
1416 visualRect(bottom).bottomRight()); |
|
1417 selectionRegion += QRegion(rect); |
|
1418 } |
|
1419 } |
|
1420 |
|
1421 return selectionRegion; |
|
1422 } |
|
1423 |
|
1424 /*! |
|
1425 \reimp |
|
1426 */ |
|
1427 QModelIndexList QListView::selectedIndexes() const |
|
1428 { |
|
1429 Q_D(const QListView); |
|
1430 if (!d->selectionModel) |
|
1431 return QModelIndexList(); |
|
1432 |
|
1433 QModelIndexList viewSelected = d->selectionModel->selectedIndexes(); |
|
1434 for (int i = 0; i < viewSelected.count(); ++i) { |
|
1435 const QModelIndex &index = viewSelected.at(i); |
|
1436 if (!isIndexHidden(index) && index.parent() == d->root && index.column() == d->column) |
|
1437 ++i; |
|
1438 else |
|
1439 viewSelected.removeAt(i); |
|
1440 } |
|
1441 return viewSelected; |
|
1442 } |
|
1443 |
|
1444 /*! |
|
1445 \internal |
|
1446 |
|
1447 Layout the items according to the flow and wrapping properties. |
|
1448 */ |
|
1449 void QListView::doItemsLayout() |
|
1450 { |
|
1451 Q_D(QListView); |
|
1452 // showing the scroll bars will trigger a resize event, |
|
1453 // so we set the state to expanding to avoid |
|
1454 // triggering another layout |
|
1455 QAbstractItemView::State oldState = state(); |
|
1456 setState(ExpandingState); |
|
1457 if (d->model->columnCount(d->root) > 0) { // no columns means no contents |
|
1458 d->resetBatchStartRow(); |
|
1459 if (layoutMode() == SinglePass) |
|
1460 d->doItemsLayout(d->model->rowCount(d->root)); // layout everything |
|
1461 else if (!d->batchLayoutTimer.isActive()) { |
|
1462 if (!d->doItemsLayout(d->batchSize)) // layout is done |
|
1463 d->batchLayoutTimer.start(0, this); // do a new batch as fast as possible |
|
1464 } |
|
1465 } |
|
1466 QAbstractItemView::doItemsLayout(); |
|
1467 setState(oldState); // restoring the oldState |
|
1468 } |
|
1469 |
|
1470 /*! |
|
1471 \reimp |
|
1472 */ |
|
1473 void QListView::updateGeometries() |
|
1474 { |
|
1475 Q_D(QListView); |
|
1476 if (d->model->rowCount(d->root) <= 0 || d->model->columnCount(d->root) <= 0) { |
|
1477 horizontalScrollBar()->setRange(0, 0); |
|
1478 verticalScrollBar()->setRange(0, 0); |
|
1479 } else { |
|
1480 QModelIndex index = d->model->index(0, d->column, d->root); |
|
1481 QStyleOptionViewItemV4 option = d->viewOptionsV4(); |
|
1482 QSize step = d->itemSize(option, index); |
|
1483 d->commonListView->updateHorizontalScrollBar(step); |
|
1484 d->commonListView->updateVerticalScrollBar(step); |
|
1485 } |
|
1486 |
|
1487 QAbstractItemView::updateGeometries(); |
|
1488 |
|
1489 // if the scroll bars are turned off, we resize the contents to the viewport |
|
1490 if (d->movement == Static && !d->isWrapping()) { |
|
1491 d->layoutChildren(); // we need the viewport size to be updated |
|
1492 if (d->flow == TopToBottom) { |
|
1493 if (horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) { |
|
1494 d->setContentsSize(viewport()->width(), contentsSize().height()); |
|
1495 horizontalScrollBar()->setRange(0, 0); // we see all the contents anyway |
|
1496 } |
|
1497 } else { // LeftToRight |
|
1498 if (verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) { |
|
1499 d->setContentsSize(contentsSize().width(), viewport()->height()); |
|
1500 verticalScrollBar()->setRange(0, 0); // we see all the contents anyway |
|
1501 } |
|
1502 } |
|
1503 } |
|
1504 } |
|
1505 |
|
1506 /*! |
|
1507 \reimp |
|
1508 */ |
|
1509 bool QListView::isIndexHidden(const QModelIndex &index) const |
|
1510 { |
|
1511 Q_D(const QListView); |
|
1512 return (d->isHidden(index.row()) |
|
1513 && (index.parent() == d->root) |
|
1514 && index.column() == d->column); |
|
1515 } |
|
1516 |
|
1517 /*! |
|
1518 \property QListView::modelColumn |
|
1519 \brief the column in the model that is visible |
|
1520 |
|
1521 By default, this property contains 0, indicating that the first |
|
1522 column in the model will be shown. |
|
1523 */ |
|
1524 void QListView::setModelColumn(int column) |
|
1525 { |
|
1526 Q_D(QListView); |
|
1527 if (column < 0 || column >= d->model->columnCount(d->root)) |
|
1528 return; |
|
1529 d->column = column; |
|
1530 d->doDelayedItemsLayout(); |
|
1531 } |
|
1532 |
|
1533 int QListView::modelColumn() const |
|
1534 { |
|
1535 Q_D(const QListView); |
|
1536 return d->column; |
|
1537 } |
|
1538 |
|
1539 /*! |
|
1540 \property QListView::uniformItemSizes |
|
1541 \brief whether all items in the listview have the same size |
|
1542 \since 4.1 |
|
1543 |
|
1544 This property should only be set to true if it is guaranteed that all items |
|
1545 in the view have the same size. This enables the view to do some |
|
1546 optimizations for performance purposes. |
|
1547 |
|
1548 By default, this property is false. |
|
1549 */ |
|
1550 void QListView::setUniformItemSizes(bool enable) |
|
1551 { |
|
1552 Q_D(QListView); |
|
1553 d->uniformItemSizes = enable; |
|
1554 } |
|
1555 |
|
1556 bool QListView::uniformItemSizes() const |
|
1557 { |
|
1558 Q_D(const QListView); |
|
1559 return d->uniformItemSizes; |
|
1560 } |
|
1561 |
|
1562 /*! |
|
1563 \property QListView::wordWrap |
|
1564 \brief the item text word-wrapping policy |
|
1565 \since 4.2 |
|
1566 |
|
1567 If this property is true then the item text is wrapped where |
|
1568 necessary at word-breaks; otherwise it is not wrapped at all. |
|
1569 This property is false by default. |
|
1570 |
|
1571 Please note that even if wrapping is enabled, the cell will not be |
|
1572 expanded to make room for the text. It will print ellipsis for |
|
1573 text that cannot be shown, according to the view's |
|
1574 \l{QAbstractItemView::}{textElideMode}. |
|
1575 */ |
|
1576 void QListView::setWordWrap(bool on) |
|
1577 { |
|
1578 Q_D(QListView); |
|
1579 if (d->wrapItemText == on) |
|
1580 return; |
|
1581 d->wrapItemText = on; |
|
1582 d->doDelayedItemsLayout(); |
|
1583 } |
|
1584 |
|
1585 bool QListView::wordWrap() const |
|
1586 { |
|
1587 Q_D(const QListView); |
|
1588 return d->wrapItemText; |
|
1589 } |
|
1590 |
|
1591 /*! |
|
1592 \property QListView::selectionRectVisible |
|
1593 \brief if the selection rectangle should be visible |
|
1594 \since 4.3 |
|
1595 |
|
1596 If this property is true then the selection rectangle is visible; |
|
1597 otherwise it will be hidden. |
|
1598 |
|
1599 \note The selection rectangle will only be visible if the selection mode |
|
1600 is in a mode where more than one item can be selected; i.e., it will not |
|
1601 draw a selection rectangle if the selection mode is |
|
1602 QAbstractItemView::SingleSelection. |
|
1603 |
|
1604 By default, this property is false. |
|
1605 */ |
|
1606 void QListView::setSelectionRectVisible(bool show) |
|
1607 { |
|
1608 Q_D(QListView); |
|
1609 d->modeProperties |= uint(QListViewPrivate::SelectionRectVisible); |
|
1610 d->setSelectionRectVisible(show); |
|
1611 } |
|
1612 |
|
1613 bool QListView::isSelectionRectVisible() const |
|
1614 { |
|
1615 Q_D(const QListView); |
|
1616 return d->isSelectionRectVisible(); |
|
1617 } |
|
1618 |
|
1619 /*! |
|
1620 \reimp |
|
1621 */ |
|
1622 bool QListView::event(QEvent *e) |
|
1623 { |
|
1624 return QAbstractItemView::event(e); |
|
1625 } |
|
1626 |
|
1627 /* |
|
1628 * private object implementation |
|
1629 */ |
|
1630 |
|
1631 QListViewPrivate::QListViewPrivate() |
|
1632 : QAbstractItemViewPrivate(), |
|
1633 commonListView(0), |
|
1634 wrap(false), |
|
1635 space(0), |
|
1636 flow(QListView::TopToBottom), |
|
1637 movement(QListView::Static), |
|
1638 resizeMode(QListView::Fixed), |
|
1639 layoutMode(QListView::SinglePass), |
|
1640 viewMode(QListView::ListMode), |
|
1641 modeProperties(0), |
|
1642 column(0), |
|
1643 uniformItemSizes(false), |
|
1644 batchSize(100), |
|
1645 showElasticBand(false) |
|
1646 { |
|
1647 } |
|
1648 |
|
1649 QListViewPrivate::~QListViewPrivate() |
|
1650 { |
|
1651 delete commonListView; |
|
1652 } |
|
1653 |
|
1654 void QListViewPrivate::clear() |
|
1655 { |
|
1656 // initialization of data structs |
|
1657 cachedItemSize = QSize(); |
|
1658 commonListView->clear(); |
|
1659 } |
|
1660 |
|
1661 void QListViewPrivate::prepareItemsLayout() |
|
1662 { |
|
1663 Q_Q(QListView); |
|
1664 clear(); |
|
1665 |
|
1666 //take the size as if there were scrollbar in order to prevent scrollbar to blink |
|
1667 layoutBounds = QRect(QPoint(), q->maximumViewportSize()); |
|
1668 |
|
1669 int frameAroundContents = 0; |
|
1670 if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) |
|
1671 frameAroundContents = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2; |
|
1672 |
|
1673 // maximumViewportSize() already takes scrollbar into account if policy is |
|
1674 // Qt::ScrollBarAlwaysOn but scrollbar extent must be deduced if policy |
|
1675 // is Qt::ScrollBarAsNeeded |
|
1676 int verticalMargin = vbarpolicy==Qt::ScrollBarAsNeeded |
|
1677 ? q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, vbar) + frameAroundContents |
|
1678 : 0; |
|
1679 int horizontalMargin = hbarpolicy==Qt::ScrollBarAsNeeded |
|
1680 ? q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, hbar) + frameAroundContents |
|
1681 : 0; |
|
1682 |
|
1683 layoutBounds.adjust(0, 0, -verticalMargin, -horizontalMargin); |
|
1684 |
|
1685 int rowCount = model->columnCount(root) <= 0 ? 0 : model->rowCount(root); |
|
1686 commonListView->setRowCount(rowCount); |
|
1687 } |
|
1688 |
|
1689 /*! |
|
1690 \internal |
|
1691 */ |
|
1692 bool QListViewPrivate::doItemsLayout(int delta) |
|
1693 { |
|
1694 int max = model->rowCount(root) - 1; |
|
1695 int first = batchStartRow(); |
|
1696 int last = qMin(first + delta - 1, max); |
|
1697 |
|
1698 if (first == 0) { |
|
1699 layoutChildren(); // make sure the viewport has the right size |
|
1700 prepareItemsLayout(); |
|
1701 } |
|
1702 |
|
1703 if (max < 0 || last < first) { |
|
1704 return true; // nothing to do |
|
1705 } |
|
1706 |
|
1707 QListViewLayoutInfo info; |
|
1708 info.bounds = layoutBounds; |
|
1709 info.grid = gridSize(); |
|
1710 info.spacing = (info.grid.isValid() ? 0 : spacing()); |
|
1711 info.first = first; |
|
1712 info.last = last; |
|
1713 info.wrap = isWrapping(); |
|
1714 info.flow = flow; |
|
1715 info.max = max; |
|
1716 |
|
1717 return commonListView->doBatchedItemLayout(info, max); |
|
1718 } |
|
1719 |
|
1720 QListViewItem QListViewPrivate::indexToListViewItem(const QModelIndex &index) const |
|
1721 { |
|
1722 if (!index.isValid() || isHidden(index.row())) |
|
1723 return QListViewItem(); |
|
1724 |
|
1725 return commonListView->indexToListViewItem(index); |
|
1726 } |
|
1727 |
|
1728 QRect QListViewPrivate::mapToViewport(const QRect &rect, bool extend) const |
|
1729 { |
|
1730 Q_Q(const QListView); |
|
1731 if (!rect.isValid()) |
|
1732 return rect; |
|
1733 |
|
1734 QRect result = extend ? commonListView->mapToViewport(rect) : rect; |
|
1735 int dx = -q->horizontalOffset(); |
|
1736 int dy = -q->verticalOffset(); |
|
1737 return result.adjusted(dx, dy, dx, dy); |
|
1738 } |
|
1739 |
|
1740 QModelIndex QListViewPrivate::closestIndex(const QRect &target, |
|
1741 const QVector<QModelIndex> &candidates) const |
|
1742 { |
|
1743 int distance = 0; |
|
1744 int shortest = INT_MAX; |
|
1745 QModelIndex closest; |
|
1746 QVector<QModelIndex>::const_iterator it = candidates.begin(); |
|
1747 |
|
1748 for (; it != candidates.end(); ++it) { |
|
1749 if (!(*it).isValid()) |
|
1750 continue; |
|
1751 |
|
1752 const QRect indexRect = indexToListViewItem(*it).rect(); |
|
1753 |
|
1754 //if the center x (or y) position of an item is included in the rect of the other item, |
|
1755 //we define the distance between them as the difference in x (or y) of their respective center. |
|
1756 // Otherwise, we use the nahattan length between the 2 items |
|
1757 if ((target.center().x() >= indexRect.x() && target.center().x() < indexRect.right()) |
|
1758 || (indexRect.center().x() >= target.x() && indexRect.center().x() < target.right())) { |
|
1759 //one item's center is at the vertical of the other |
|
1760 distance = qAbs(indexRect.center().y() - target.center().y()); |
|
1761 } else if ((target.center().y() >= indexRect.y() && target.center().y() < indexRect.bottom()) |
|
1762 || (indexRect.center().y() >= target.y() && indexRect.center().y() < target.bottom())) { |
|
1763 //one item's center is at the vertical of the other |
|
1764 distance = qAbs(indexRect.center().x() - target.center().x()); |
|
1765 } else { |
|
1766 distance = (indexRect.center() - target.center()).manhattanLength(); |
|
1767 } |
|
1768 if (distance < shortest) { |
|
1769 shortest = distance; |
|
1770 closest = *it; |
|
1771 } |
|
1772 } |
|
1773 return closest; |
|
1774 } |
|
1775 |
|
1776 QSize QListViewPrivate::itemSize(const QStyleOptionViewItem &option, const QModelIndex &index) const |
|
1777 { |
|
1778 if (!uniformItemSizes) { |
|
1779 const QAbstractItemDelegate *delegate = delegateForIndex(index); |
|
1780 return delegate ? delegate->sizeHint(option, index) : QSize(); |
|
1781 } |
|
1782 if (!cachedItemSize.isValid()) { // the last item is probaly the largest, so we use its size |
|
1783 int row = model->rowCount(root) - 1; |
|
1784 QModelIndex sample = model->index(row, column, root); |
|
1785 const QAbstractItemDelegate *delegate = delegateForIndex(sample); |
|
1786 cachedItemSize = delegate ? delegate->sizeHint(option, sample) : QSize(); |
|
1787 } |
|
1788 return cachedItemSize; |
|
1789 } |
|
1790 |
|
1791 QItemSelection QListViewPrivate::selection(const QRect &rect) const |
|
1792 { |
|
1793 QItemSelection selection; |
|
1794 QModelIndex tl, br; |
|
1795 const QVector<QModelIndex> intersectVector = intersectingSet(rect); |
|
1796 QVector<QModelIndex>::const_iterator it = intersectVector.begin(); |
|
1797 for (; it != intersectVector.end(); ++it) { |
|
1798 if (!tl.isValid() && !br.isValid()) { |
|
1799 tl = br = *it; |
|
1800 } else if ((*it).row() == (tl.row() - 1)) { |
|
1801 tl = *it; // expand current range |
|
1802 } else if ((*it).row() == (br.row() + 1)) { |
|
1803 br = (*it); // expand current range |
|
1804 } else { |
|
1805 selection.select(tl, br); // select current range |
|
1806 tl = br = *it; // start new range |
|
1807 } |
|
1808 } |
|
1809 |
|
1810 if (tl.isValid() && br.isValid()) |
|
1811 selection.select(tl, br); |
|
1812 else if (tl.isValid()) |
|
1813 selection.select(tl, tl); |
|
1814 else if (br.isValid()) |
|
1815 selection.select(br, br); |
|
1816 |
|
1817 return selection; |
|
1818 } |
|
1819 |
|
1820 #ifndef QT_NO_DRAGANDDROP |
|
1821 QAbstractItemView::DropIndicatorPosition QListViewPrivate::position(const QPoint &pos, const QRect &rect, const QModelIndex &idx) const |
|
1822 { |
|
1823 if (viewMode == QListView::ListMode && flow == QListView::LeftToRight) |
|
1824 return static_cast<QListModeViewBase *>(commonListView)->position(pos, rect, idx); |
|
1825 else |
|
1826 return QAbstractItemViewPrivate::position(pos, rect, idx); |
|
1827 } |
|
1828 #endif |
|
1829 |
|
1830 /* |
|
1831 * Common ListView Implementation |
|
1832 */ |
|
1833 |
|
1834 void QCommonListViewBase::appendHiddenRow(int row) |
|
1835 { |
|
1836 dd->hiddenRows.append(dd->model->index(row, 0, qq->rootIndex())); |
|
1837 } |
|
1838 |
|
1839 void QCommonListViewBase::removeHiddenRow(int row) |
|
1840 { |
|
1841 dd->hiddenRows.remove(dd->hiddenRows.indexOf(dd->model->index(row, 0, qq->rootIndex()))); |
|
1842 } |
|
1843 |
|
1844 void QCommonListViewBase::updateHorizontalScrollBar(const QSize &step) |
|
1845 { |
|
1846 horizontalScrollBar()->setSingleStep(step.width() + spacing()); |
|
1847 horizontalScrollBar()->setPageStep(viewport()->width()); |
|
1848 horizontalScrollBar()->setRange(0, contentsSize.width() - viewport()->width() - 2 * spacing()); |
|
1849 } |
|
1850 |
|
1851 void QCommonListViewBase::updateVerticalScrollBar(const QSize &step) |
|
1852 { |
|
1853 verticalScrollBar()->setSingleStep(step.height() + spacing()); |
|
1854 verticalScrollBar()->setPageStep(viewport()->height()); |
|
1855 verticalScrollBar()->setRange(0, contentsSize.height() - viewport()->height() - 2 * spacing()); |
|
1856 } |
|
1857 |
|
1858 void QCommonListViewBase::scrollContentsBy(int dx, int dy, bool /*scrollElasticBand*/) |
|
1859 { |
|
1860 dd->scrollContentsBy(isRightToLeft() ? -dx : dx, dy); |
|
1861 } |
|
1862 |
|
1863 int QCommonListViewBase::verticalScrollToValue(int /*index*/, QListView::ScrollHint hint, |
|
1864 bool above, bool below, const QRect &area, const QRect &rect) const |
|
1865 { |
|
1866 int verticalValue = verticalScrollBar()->value(); |
|
1867 QRect adjusted = rect.adjusted(-spacing(), -spacing(), spacing(), spacing()); |
|
1868 if (hint == QListView::PositionAtTop || above) |
|
1869 verticalValue += adjusted.top(); |
|
1870 else if (hint == QListView::PositionAtBottom || below) |
|
1871 verticalValue += qMin(adjusted.top(), adjusted.bottom() - area.height() + 1); |
|
1872 else if (hint == QListView::PositionAtCenter) |
|
1873 verticalValue += adjusted.top() - ((area.height() - adjusted.height()) / 2); |
|
1874 return verticalValue; |
|
1875 } |
|
1876 |
|
1877 int QCommonListViewBase::horizontalOffset() const |
|
1878 { |
|
1879 return (isRightToLeft() ? horizontalScrollBar()->maximum() - horizontalScrollBar()->value() : horizontalScrollBar()->value()); |
|
1880 } |
|
1881 |
|
1882 int QCommonListViewBase::horizontalScrollToValue(const int /*index*/, QListView::ScrollHint hint, |
|
1883 bool leftOf, bool rightOf, const QRect &area, const QRect &rect) const |
|
1884 { |
|
1885 int horizontalValue = horizontalScrollBar()->value(); |
|
1886 if (isRightToLeft()) { |
|
1887 if (hint == QListView::PositionAtCenter) { |
|
1888 horizontalValue += ((area.width() - rect.width()) / 2) - rect.left(); |
|
1889 } else { |
|
1890 if (leftOf) |
|
1891 horizontalValue -= rect.left(); |
|
1892 else if (rightOf) |
|
1893 horizontalValue += qMin(rect.left(), area.width() - rect.right()); |
|
1894 } |
|
1895 } else { |
|
1896 if (hint == QListView::PositionAtCenter) { |
|
1897 horizontalValue += rect.left() - ((area.width()- rect.width()) / 2); |
|
1898 } else { |
|
1899 if (leftOf) |
|
1900 horizontalValue += rect.left(); |
|
1901 else if (rightOf) |
|
1902 horizontalValue += qMin(rect.left(), rect.right() - area.width()); |
|
1903 } |
|
1904 } |
|
1905 return horizontalValue; |
|
1906 } |
|
1907 |
|
1908 /* |
|
1909 * ListMode ListView Implementation |
|
1910 */ |
|
1911 |
|
1912 #ifndef QT_NO_DRAGANDDROP |
|
1913 void QListModeViewBase::paintDragDrop(QPainter *painter) |
|
1914 { |
|
1915 // FIXME: Until the we can provide a proper drop indicator |
|
1916 // in IconMode, it makes no sense to show it |
|
1917 dd->paintDropIndicator(painter); |
|
1918 } |
|
1919 |
|
1920 QAbstractItemView::DropIndicatorPosition QListModeViewBase::position(const QPoint &pos, const QRect &rect, const QModelIndex &index) const |
|
1921 { |
|
1922 QAbstractItemView::DropIndicatorPosition r = QAbstractItemView::OnViewport; |
|
1923 if (!dd->overwrite) { |
|
1924 const int margin = 2; |
|
1925 if (pos.x() - rect.left() < margin) { |
|
1926 r = QAbstractItemView::AboveItem; // Visually, on the left |
|
1927 } else if (rect.right() - pos.x() < margin) { |
|
1928 r = QAbstractItemView::BelowItem; // Visually, on the right |
|
1929 } else if (rect.contains(pos, true)) { |
|
1930 r = QAbstractItemView::OnItem; |
|
1931 } |
|
1932 } else { |
|
1933 QRect touchingRect = rect; |
|
1934 touchingRect.adjust(-1, -1, 1, 1); |
|
1935 if (touchingRect.contains(pos, false)) { |
|
1936 r = QAbstractItemView::OnItem; |
|
1937 } |
|
1938 } |
|
1939 |
|
1940 if (r == QAbstractItemView::OnItem && (!(dd->model->flags(index) & Qt::ItemIsDropEnabled))) |
|
1941 r = pos.x() < rect.center().x() ? QAbstractItemView::AboveItem : QAbstractItemView::BelowItem; |
|
1942 |
|
1943 return r; |
|
1944 } |
|
1945 |
|
1946 void QListModeViewBase::dragMoveEvent(QDragMoveEvent *event) |
|
1947 { |
|
1948 if (qq->dragDropMode() == QAbstractItemView::InternalMove |
|
1949 && (event->source() != qq || !(event->possibleActions() & Qt::MoveAction))) |
|
1950 return; |
|
1951 |
|
1952 // ignore by default |
|
1953 event->ignore(); |
|
1954 |
|
1955 QModelIndex index = qq->indexAt(event->pos()); |
|
1956 dd->hover = index; |
|
1957 if (!dd->droppingOnItself(event, index) |
|
1958 && dd->canDecode(event)) { |
|
1959 |
|
1960 if (index.isValid() && dd->showDropIndicator) { |
|
1961 QRect rect = qq->visualRect(index); |
|
1962 dd->dropIndicatorPosition = position(event->pos(), rect, index); |
|
1963 switch (dd->dropIndicatorPosition) { |
|
1964 case QAbstractItemView::AboveItem: |
|
1965 if (dd->isIndexDropEnabled(index.parent())) { |
|
1966 dd->dropIndicatorRect = QRect(rect.left(), rect.top(), 0, rect.height()); |
|
1967 event->accept(); |
|
1968 } else { |
|
1969 dd->dropIndicatorRect = QRect(); |
|
1970 } |
|
1971 break; |
|
1972 case QAbstractItemView::BelowItem: |
|
1973 if (dd->isIndexDropEnabled(index.parent())) { |
|
1974 dd->dropIndicatorRect = QRect(rect.right(), rect.top(), 0, rect.height()); |
|
1975 event->accept(); |
|
1976 } else { |
|
1977 dd->dropIndicatorRect = QRect(); |
|
1978 } |
|
1979 break; |
|
1980 case QAbstractItemView::OnItem: |
|
1981 if (dd->isIndexDropEnabled(index)) { |
|
1982 dd->dropIndicatorRect = rect; |
|
1983 event->accept(); |
|
1984 } else { |
|
1985 dd->dropIndicatorRect = QRect(); |
|
1986 } |
|
1987 break; |
|
1988 case QAbstractItemView::OnViewport: |
|
1989 dd->dropIndicatorRect = QRect(); |
|
1990 if (dd->isIndexDropEnabled(qq->rootIndex())) { |
|
1991 event->accept(); // allow dropping in empty areas |
|
1992 } |
|
1993 break; |
|
1994 } |
|
1995 } else { |
|
1996 dd->dropIndicatorRect = QRect(); |
|
1997 dd->dropIndicatorPosition = QAbstractItemView::OnViewport; |
|
1998 if (dd->isIndexDropEnabled(qq->rootIndex())) { |
|
1999 event->accept(); // allow dropping in empty areas |
|
2000 } |
|
2001 } |
|
2002 dd->viewport->update(); |
|
2003 } // can decode |
|
2004 |
|
2005 if (dd->shouldAutoScroll(event->pos())) |
|
2006 qq->startAutoScroll(); |
|
2007 } |
|
2008 |
|
2009 #endif //QT_NO_DRAGANDDROP |
|
2010 |
|
2011 void QListModeViewBase::updateVerticalScrollBar(const QSize &step) |
|
2012 { |
|
2013 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem |
|
2014 && ((flow() == QListView::TopToBottom && !isWrapping()) |
|
2015 || (flow() == QListView::LeftToRight && isWrapping()))) { |
|
2016 const int steps = (flow() == QListView::TopToBottom ? scrollValueMap : segmentPositions).count() - 1; |
|
2017 if (steps > 0) { |
|
2018 const int pageSteps = perItemScrollingPageSteps(viewport()->height(), contentsSize.height(), isWrapping()); |
|
2019 verticalScrollBar()->setSingleStep(1); |
|
2020 verticalScrollBar()->setPageStep(pageSteps); |
|
2021 verticalScrollBar()->setRange(0, steps - pageSteps); |
|
2022 } else { |
|
2023 verticalScrollBar()->setRange(0, 0); |
|
2024 } |
|
2025 // } else if (vertical && d->isWrapping() && d->movement == Static) { |
|
2026 // ### wrapped scrolling in flow direction |
|
2027 } else { |
|
2028 QCommonListViewBase::updateVerticalScrollBar(step); |
|
2029 } |
|
2030 } |
|
2031 |
|
2032 void QListModeViewBase::updateHorizontalScrollBar(const QSize &step) |
|
2033 { |
|
2034 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem |
|
2035 && ((flow() == QListView::TopToBottom && isWrapping()) |
|
2036 || (flow() == QListView::LeftToRight && !isWrapping()))) { |
|
2037 int steps = (flow() == QListView::TopToBottom ? segmentPositions : scrollValueMap).count() - 1; |
|
2038 if (steps > 0) { |
|
2039 const int pageSteps = perItemScrollingPageSteps(viewport()->width(), contentsSize.width(), isWrapping()); |
|
2040 horizontalScrollBar()->setSingleStep(1); |
|
2041 horizontalScrollBar()->setPageStep(pageSteps); |
|
2042 horizontalScrollBar()->setRange(0, steps - pageSteps); |
|
2043 } else { |
|
2044 horizontalScrollBar()->setRange(0, 0); |
|
2045 } |
|
2046 } else { |
|
2047 QCommonListViewBase::updateHorizontalScrollBar(step); |
|
2048 } |
|
2049 } |
|
2050 |
|
2051 int QListModeViewBase::verticalScrollToValue(int index, QListView::ScrollHint hint, |
|
2052 bool above, bool below, const QRect &area, const QRect &rect) const |
|
2053 { |
|
2054 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) { |
|
2055 int value; |
|
2056 if (scrollValueMap.isEmpty()) |
|
2057 value = 0; |
|
2058 else |
|
2059 value = qBound(0, scrollValueMap.at(verticalScrollBar()->value()), flowPositions.count() - 1); |
|
2060 if (above) |
|
2061 hint = QListView::PositionAtTop; |
|
2062 else if (below) |
|
2063 hint = QListView::PositionAtBottom; |
|
2064 if (hint == QListView::EnsureVisible) |
|
2065 return value; |
|
2066 |
|
2067 return perItemScrollToValue(index, value, area.height(), hint, Qt::Vertical, isWrapping(), rect.height()); |
|
2068 } |
|
2069 |
|
2070 return QCommonListViewBase::verticalScrollToValue(index, hint, above, below, area, rect); |
|
2071 } |
|
2072 |
|
2073 int QListModeViewBase::horizontalOffset() const |
|
2074 { |
|
2075 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) { |
|
2076 if (isWrapping()) { |
|
2077 if (flow() == QListView::TopToBottom && !segmentPositions.isEmpty()) { |
|
2078 const int max = segmentPositions.count() - 1; |
|
2079 int currentValue = qBound(0, horizontalScrollBar()->value(), max); |
|
2080 int position = segmentPositions.at(currentValue); |
|
2081 int maximumValue = qBound(0, horizontalScrollBar()->maximum(), max); |
|
2082 int maximum = segmentPositions.at(maximumValue); |
|
2083 return (isRightToLeft() ? maximum - position : position); |
|
2084 } |
|
2085 } else if (flow() == QListView::LeftToRight && !flowPositions.isEmpty()) { |
|
2086 int position = flowPositions.at(scrollValueMap.at(horizontalScrollBar()->value())); |
|
2087 int maximum = flowPositions.at(scrollValueMap.at(horizontalScrollBar()->maximum())); |
|
2088 return (isRightToLeft() ? maximum - position : position); |
|
2089 } |
|
2090 } |
|
2091 return QCommonListViewBase::horizontalOffset(); |
|
2092 } |
|
2093 |
|
2094 int QListModeViewBase::verticalOffset() const |
|
2095 { |
|
2096 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) { |
|
2097 if (isWrapping()) { |
|
2098 if (flow() == QListView::LeftToRight && !segmentPositions.isEmpty()) { |
|
2099 int value = verticalScrollBar()->value(); |
|
2100 if (value >= segmentPositions.count()) |
|
2101 return 0; |
|
2102 return segmentPositions.at(value); |
|
2103 } |
|
2104 } else if (flow() == QListView::TopToBottom && !flowPositions.isEmpty()) { |
|
2105 int value = verticalScrollBar()->value(); |
|
2106 if (value > scrollValueMap.count()) |
|
2107 return 0; |
|
2108 return flowPositions.at(scrollValueMap.at(value)) - spacing(); |
|
2109 } |
|
2110 } |
|
2111 return QCommonListViewBase::verticalOffset(); |
|
2112 } |
|
2113 |
|
2114 int QListModeViewBase::horizontalScrollToValue(int index, QListView::ScrollHint hint, |
|
2115 bool leftOf, bool rightOf, const QRect &area, const QRect &rect) const |
|
2116 { |
|
2117 if (horizontalScrollMode() != QAbstractItemView::ScrollPerItem) |
|
2118 return QCommonListViewBase::horizontalScrollToValue(index, hint, leftOf, rightOf, area, rect); |
|
2119 |
|
2120 int value; |
|
2121 if (scrollValueMap.isEmpty()) |
|
2122 value = 0; |
|
2123 else |
|
2124 value = qBound(0, scrollValueMap.at(horizontalScrollBar()->value()), flowPositions.count() - 1); |
|
2125 if (leftOf) |
|
2126 hint = QListView::PositionAtTop; |
|
2127 else if (rightOf) |
|
2128 hint = QListView::PositionAtBottom; |
|
2129 if (hint == QListView::EnsureVisible) |
|
2130 return value; |
|
2131 |
|
2132 return perItemScrollToValue(index, value, area.width(), hint, Qt::Horizontal, isWrapping(), rect.width()); |
|
2133 } |
|
2134 |
|
2135 void QListModeViewBase::scrollContentsBy(int dx, int dy, bool scrollElasticBand) |
|
2136 { |
|
2137 // ### reorder this logic |
|
2138 const int verticalValue = verticalScrollBar()->value(); |
|
2139 const int horizontalValue = horizontalScrollBar()->value(); |
|
2140 const bool vertical = (verticalScrollMode() == QAbstractItemView::ScrollPerItem); |
|
2141 const bool horizontal = (horizontalScrollMode() == QAbstractItemView::ScrollPerItem); |
|
2142 |
|
2143 if (isWrapping()) { |
|
2144 if (segmentPositions.isEmpty()) |
|
2145 return; |
|
2146 const int max = segmentPositions.count() - 1; |
|
2147 if (horizontal && flow() == QListView::TopToBottom && dx != 0) { |
|
2148 int currentValue = qBound(0, horizontalValue, max); |
|
2149 int previousValue = qBound(0, currentValue + dx, max); |
|
2150 int currentCoordinate = segmentPositions.at(currentValue); |
|
2151 int previousCoordinate = segmentPositions.at(previousValue); |
|
2152 dx = previousCoordinate - currentCoordinate; |
|
2153 } else if (vertical && flow() == QListView::LeftToRight && dy != 0) { |
|
2154 int currentValue = qBound(0, verticalValue, max); |
|
2155 int previousValue = qBound(0, currentValue + dy, max); |
|
2156 int currentCoordinate = segmentPositions.at(currentValue); |
|
2157 int previousCoordinate = segmentPositions.at(previousValue); |
|
2158 dy = previousCoordinate - currentCoordinate; |
|
2159 } |
|
2160 } else { |
|
2161 if (flowPositions.isEmpty()) |
|
2162 return; |
|
2163 const int max = scrollValueMap.count() - 1; |
|
2164 if (vertical && flow() == QListView::TopToBottom && dy != 0) { |
|
2165 int currentValue = qBound(0, verticalValue, max); |
|
2166 int previousValue = qBound(0, currentValue + dy, max); |
|
2167 int currentCoordinate = flowPositions.at(scrollValueMap.at(currentValue)); |
|
2168 int previousCoordinate = flowPositions.at(scrollValueMap.at(previousValue)); |
|
2169 dy = previousCoordinate - currentCoordinate; |
|
2170 } else if (horizontal && flow() == QListView::LeftToRight && dx != 0) { |
|
2171 int currentValue = qBound(0, horizontalValue, max); |
|
2172 int previousValue = qBound(0, currentValue + dx, max); |
|
2173 int currentCoordinate = flowPositions.at(scrollValueMap.at(currentValue)); |
|
2174 int previousCoordinate = flowPositions.at(scrollValueMap.at(previousValue)); |
|
2175 dx = previousCoordinate - currentCoordinate; |
|
2176 } |
|
2177 } |
|
2178 QCommonListViewBase::scrollContentsBy(dx, dy, scrollElasticBand); |
|
2179 } |
|
2180 |
|
2181 bool QListModeViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max) |
|
2182 { |
|
2183 doStaticLayout(info); |
|
2184 if (batchStartRow > max) { // stop items layout |
|
2185 flowPositions.resize(flowPositions.count()); |
|
2186 segmentPositions.resize(segmentPositions.count()); |
|
2187 segmentStartRows.resize(segmentStartRows.count()); |
|
2188 return true; // done |
|
2189 } |
|
2190 return false; // not done |
|
2191 } |
|
2192 |
|
2193 QListViewItem QListModeViewBase::indexToListViewItem(const QModelIndex &index) const |
|
2194 { |
|
2195 if (flowPositions.isEmpty() |
|
2196 || segmentPositions.isEmpty() |
|
2197 || index.row() >= flowPositions.count()) |
|
2198 return QListViewItem(); |
|
2199 |
|
2200 const int segment = qBinarySearch<int>(segmentStartRows, index.row(), |
|
2201 0, segmentStartRows.count() - 1); |
|
2202 |
|
2203 |
|
2204 QStyleOptionViewItemV4 options = viewOptions(); |
|
2205 options.rect.setSize(contentsSize); |
|
2206 QSize size = (uniformItemSizes() && cachedItemSize().isValid()) |
|
2207 ? cachedItemSize() : itemSize(options, index); |
|
2208 |
|
2209 QPoint pos; |
|
2210 if (flow() == QListView::LeftToRight) { |
|
2211 pos.setX(flowPositions.at(index.row())); |
|
2212 pos.setY(segmentPositions.at(segment)); |
|
2213 } else { // TopToBottom |
|
2214 pos.setY(flowPositions.at(index.row())); |
|
2215 pos.setX(segmentPositions.at(segment)); |
|
2216 if (isWrapping()) { // make the items as wide as the segment |
|
2217 int right = (segment + 1 >= segmentPositions.count() |
|
2218 ? contentsSize.width() |
|
2219 : segmentPositions.at(segment + 1)); |
|
2220 size.setWidth(right - pos.x()); |
|
2221 } else { // make the items as wide as the viewport |
|
2222 size.setWidth(qMax(size.width(), viewport()->width())); |
|
2223 } |
|
2224 } |
|
2225 |
|
2226 return QListViewItem(QRect(pos, size), index.row()); |
|
2227 } |
|
2228 |
|
2229 QPoint QListModeViewBase::initStaticLayout(const QListViewLayoutInfo &info) |
|
2230 { |
|
2231 int x, y; |
|
2232 if (info.first == 0) { |
|
2233 flowPositions.clear(); |
|
2234 segmentPositions.clear(); |
|
2235 segmentStartRows.clear(); |
|
2236 segmentExtents.clear(); |
|
2237 scrollValueMap.clear(); |
|
2238 x = info.bounds.left() + info.spacing; |
|
2239 y = info.bounds.top() + info.spacing; |
|
2240 segmentPositions.append(info.flow == QListView::LeftToRight ? y : x); |
|
2241 segmentStartRows.append(0); |
|
2242 } else if (info.wrap) { |
|
2243 if (info.flow == QListView::LeftToRight) { |
|
2244 x = batchSavedPosition; |
|
2245 y = segmentPositions.last(); |
|
2246 } else { // flow == QListView::TopToBottom |
|
2247 x = segmentPositions.last(); |
|
2248 y = batchSavedPosition; |
|
2249 } |
|
2250 } else { // not first and not wrap |
|
2251 if (info.flow == QListView::LeftToRight) { |
|
2252 x = batchSavedPosition; |
|
2253 y = info.bounds.top() + info.spacing; |
|
2254 } else { // flow == QListView::TopToBottom |
|
2255 x = info.bounds.left() + info.spacing; |
|
2256 y = batchSavedPosition; |
|
2257 } |
|
2258 } |
|
2259 return QPoint(x, y); |
|
2260 } |
|
2261 |
|
2262 /*! |
|
2263 \internal |
|
2264 */ |
|
2265 void QListModeViewBase::doStaticLayout(const QListViewLayoutInfo &info) |
|
2266 { |
|
2267 const bool useItemSize = !info.grid.isValid(); |
|
2268 const QPoint topLeft = initStaticLayout(info); |
|
2269 QStyleOptionViewItemV4 option = viewOptions(); |
|
2270 option.rect = info.bounds; |
|
2271 |
|
2272 // The static layout data structures are as follows: |
|
2273 // One vector contains the coordinate in the direction of layout flow. |
|
2274 // Another vector contains the coordinates of the segments. |
|
2275 // A third vector contains the index (model row) of the first item |
|
2276 // of each segment. |
|
2277 |
|
2278 int segStartPosition; |
|
2279 int segEndPosition; |
|
2280 int deltaFlowPosition; |
|
2281 int deltaSegPosition; |
|
2282 int deltaSegHint; |
|
2283 int flowPosition; |
|
2284 int segPosition; |
|
2285 |
|
2286 if (info.flow == QListView::LeftToRight) { |
|
2287 segStartPosition = info.bounds.left(); |
|
2288 segEndPosition = info.bounds.width(); |
|
2289 flowPosition = topLeft.x(); |
|
2290 segPosition = topLeft.y(); |
|
2291 deltaFlowPosition = info.grid.width(); // dx |
|
2292 deltaSegPosition = useItemSize ? batchSavedDeltaSeg : info.grid.height(); // dy |
|
2293 deltaSegHint = info.grid.height(); |
|
2294 } else { // flow == QListView::TopToBottom |
|
2295 segStartPosition = info.bounds.top(); |
|
2296 segEndPosition = info.bounds.height(); |
|
2297 flowPosition = topLeft.y(); |
|
2298 segPosition = topLeft.x(); |
|
2299 deltaFlowPosition = info.grid.height(); // dy |
|
2300 deltaSegPosition = useItemSize ? batchSavedDeltaSeg : info.grid.width(); // dx |
|
2301 deltaSegHint = info.grid.width(); |
|
2302 } |
|
2303 |
|
2304 for (int row = info.first; row <= info.last; ++row) { |
|
2305 if (isHidden(row)) { // ### |
|
2306 flowPositions.append(flowPosition); |
|
2307 } else { |
|
2308 // if we are not using a grid, we need to find the deltas |
|
2309 if (useItemSize) { |
|
2310 QSize hint = itemSize(option, modelIndex(row)); |
|
2311 if (info.flow == QListView::LeftToRight) { |
|
2312 deltaFlowPosition = hint.width() + info.spacing; |
|
2313 deltaSegHint = hint.height() + info.spacing; |
|
2314 } else { // TopToBottom |
|
2315 deltaFlowPosition = hint.height() + info.spacing; |
|
2316 deltaSegHint = hint.width() + info.spacing; |
|
2317 } |
|
2318 } |
|
2319 // create new segment |
|
2320 if (info.wrap && (flowPosition + deltaFlowPosition >= segEndPosition)) { |
|
2321 segmentExtents.append(flowPosition); |
|
2322 flowPosition = info.spacing + segStartPosition; |
|
2323 segPosition += deltaSegPosition; |
|
2324 segmentPositions.append(segPosition); |
|
2325 segmentStartRows.append(row); |
|
2326 deltaSegPosition = 0; |
|
2327 } |
|
2328 // save the flow position of this item |
|
2329 scrollValueMap.append(flowPositions.count()); |
|
2330 flowPositions.append(flowPosition); |
|
2331 // prepare for the next item |
|
2332 deltaSegPosition = qMax(deltaSegHint, deltaSegPosition); |
|
2333 flowPosition += info.spacing + deltaFlowPosition; |
|
2334 } |
|
2335 } |
|
2336 // used when laying out next batch |
|
2337 batchSavedPosition = flowPosition; |
|
2338 batchSavedDeltaSeg = deltaSegPosition; |
|
2339 batchStartRow = info.last + 1; |
|
2340 if (info.last == info.max) |
|
2341 flowPosition -= info.spacing; // remove extra spacing |
|
2342 // set the contents size |
|
2343 QRect rect = info.bounds; |
|
2344 if (info.flow == QListView::LeftToRight) { |
|
2345 rect.setRight(segmentPositions.count() == 1 ? flowPosition : info.bounds.right()); |
|
2346 rect.setBottom(segPosition + deltaSegPosition); |
|
2347 } else { // TopToBottom |
|
2348 rect.setRight(segPosition + deltaSegPosition); |
|
2349 rect.setBottom(segmentPositions.count() == 1 ? flowPosition : info.bounds.bottom()); |
|
2350 } |
|
2351 contentsSize = QSize(rect.right(), rect.bottom()); |
|
2352 // if it is the last batch, save the end of the segments |
|
2353 if (info.last == info.max) { |
|
2354 segmentExtents.append(flowPosition); |
|
2355 scrollValueMap.append(flowPositions.count()); |
|
2356 flowPositions.append(flowPosition); |
|
2357 segmentPositions.append(info.wrap ? segPosition + deltaSegPosition : INT_MAX); |
|
2358 } |
|
2359 // if the new items are visble, update the viewport |
|
2360 QRect changedRect(topLeft, rect.bottomRight()); |
|
2361 if (clipRect().intersects(changedRect)) |
|
2362 viewport()->update(); |
|
2363 } |
|
2364 |
|
2365 /*! |
|
2366 \internal |
|
2367 Finds the set of items intersecting with \a area. |
|
2368 In this function, itemsize is counted from topleft to the start of the next item. |
|
2369 */ |
|
2370 QVector<QModelIndex> QListModeViewBase::intersectingSet(const QRect &area) const |
|
2371 { |
|
2372 QVector<QModelIndex> ret; |
|
2373 int segStartPosition; |
|
2374 int segEndPosition; |
|
2375 int flowStartPosition; |
|
2376 int flowEndPosition; |
|
2377 if (flow() == QListView::LeftToRight) { |
|
2378 segStartPosition = area.top(); |
|
2379 segEndPosition = area.bottom(); |
|
2380 flowStartPosition = area.left(); |
|
2381 flowEndPosition = area.right(); |
|
2382 } else { |
|
2383 segStartPosition = area.left(); |
|
2384 segEndPosition = area.right(); |
|
2385 flowStartPosition = area.top(); |
|
2386 flowEndPosition = area.bottom(); |
|
2387 } |
|
2388 if (segmentPositions.count() < 2 || flowPositions.isEmpty()) |
|
2389 return ret; |
|
2390 // the last segment position is actually the edge of the last segment |
|
2391 const int segLast = segmentPositions.count() - 2; |
|
2392 int seg = qBinarySearch<int>(segmentPositions, segStartPosition, 0, segLast + 1); |
|
2393 for (; seg <= segLast && segmentPositions.at(seg) <= segEndPosition; ++seg) { |
|
2394 int first = segmentStartRows.at(seg); |
|
2395 int last = (seg < segLast ? segmentStartRows.at(seg + 1) : batchStartRow) - 1; |
|
2396 if (segmentExtents.at(seg) < flowStartPosition) |
|
2397 continue; |
|
2398 int row = qBinarySearch<int>(flowPositions, flowStartPosition, first, last); |
|
2399 for (; row <= last && flowPositions.at(row) <= flowEndPosition; ++row) { |
|
2400 if (isHidden(row)) |
|
2401 continue; |
|
2402 QModelIndex index = modelIndex(row); |
|
2403 if (index.isValid()) |
|
2404 ret += index; |
|
2405 #if 0 // for debugging |
|
2406 else |
|
2407 qWarning("intersectingSet: row %d was invalid", row); |
|
2408 #endif |
|
2409 } |
|
2410 } |
|
2411 return ret; |
|
2412 } |
|
2413 |
|
2414 void QListModeViewBase::dataChanged(const QModelIndex &, const QModelIndex &) |
|
2415 { |
|
2416 dd->doDelayedItemsLayout(); |
|
2417 } |
|
2418 |
|
2419 |
|
2420 QRect QListModeViewBase::mapToViewport(const QRect &rect) const |
|
2421 { |
|
2422 if (isWrapping()) |
|
2423 return rect; |
|
2424 // If the listview is in "listbox-mode", the items are as wide as the view. |
|
2425 // But we don't shrink the items. |
|
2426 QRect result = rect; |
|
2427 if (flow() == QListView::TopToBottom) { |
|
2428 result.setLeft(spacing()); |
|
2429 result.setWidth(qMax(rect.width(), qMax(contentsSize.width(), viewport()->width()) - 2 * spacing())); |
|
2430 } else { // LeftToRight |
|
2431 result.setTop(spacing()); |
|
2432 result.setHeight(qMax(rect.height(), qMax(contentsSize.height(), viewport()->height()) - 2 * spacing())); |
|
2433 } |
|
2434 return result; |
|
2435 } |
|
2436 |
|
2437 int QListModeViewBase::perItemScrollingPageSteps(int length, int bounds, bool wrap) const |
|
2438 { |
|
2439 QVector<int> positions; |
|
2440 if (wrap) |
|
2441 positions = segmentPositions; |
|
2442 else if (!flowPositions.isEmpty()) { |
|
2443 positions.reserve(scrollValueMap.size()); |
|
2444 foreach (int itemShown, scrollValueMap) |
|
2445 positions.append(flowPositions.at(itemShown)); |
|
2446 } |
|
2447 if (positions.isEmpty() || bounds <= length) |
|
2448 return positions.count(); |
|
2449 if (uniformItemSizes()) { |
|
2450 for (int i = 1; i < positions.count(); ++i) |
|
2451 if (positions.at(i) > 0) |
|
2452 return length / positions.at(i); |
|
2453 return 0; // all items had height 0 |
|
2454 } |
|
2455 int pageSteps = 0; |
|
2456 int steps = positions.count() - 1; |
|
2457 int max = qMax(length, bounds); |
|
2458 int min = qMin(length, bounds); |
|
2459 int pos = min - (max - positions.last()); |
|
2460 |
|
2461 while (pos >= 0 && steps > 0) { |
|
2462 pos -= (positions.at(steps) - positions.at(steps - 1)); |
|
2463 if (pos >= 0) //this item should be visible |
|
2464 ++pageSteps; |
|
2465 --steps; |
|
2466 } |
|
2467 |
|
2468 // at this point we know that positions has at least one entry |
|
2469 return qMax(pageSteps, 1); |
|
2470 } |
|
2471 |
|
2472 int QListModeViewBase::perItemScrollToValue(int index, int scrollValue, int viewportSize, |
|
2473 QAbstractItemView::ScrollHint hint, |
|
2474 Qt::Orientation orientation, bool wrap, int itemExtent) const |
|
2475 { |
|
2476 if (index < 0) |
|
2477 return scrollValue; |
|
2478 if (!wrap) { |
|
2479 int topIndex = index; |
|
2480 const int bottomIndex = topIndex; |
|
2481 const int bottomCoordinate = flowPositions.at(index); |
|
2482 |
|
2483 while (topIndex > 0 && |
|
2484 (bottomCoordinate - flowPositions.at(topIndex-1) + itemExtent) <= (viewportSize)) { |
|
2485 topIndex--; |
|
2486 } |
|
2487 |
|
2488 const int itemCount = bottomIndex - topIndex + 1; |
|
2489 switch (hint) { |
|
2490 case QAbstractItemView::PositionAtTop: |
|
2491 return index; |
|
2492 case QAbstractItemView::PositionAtBottom: |
|
2493 return index - itemCount + 1; |
|
2494 case QAbstractItemView::PositionAtCenter: |
|
2495 return index - (itemCount / 2); |
|
2496 default: |
|
2497 break; |
|
2498 } |
|
2499 } else { // wrapping |
|
2500 Qt::Orientation flowOrientation = (flow() == QListView::LeftToRight |
|
2501 ? Qt::Horizontal : Qt::Vertical); |
|
2502 if (flowOrientation == orientation) { // scrolling in the "flow" direction |
|
2503 // ### wrapped scrolling in the flow direction |
|
2504 return flowPositions.at(index); // ### always pixel based for now |
|
2505 } else if (!segmentStartRows.isEmpty()) { // we are scrolling in the "segment" direction |
|
2506 int segment = qBinarySearch<int>(segmentStartRows, index, 0, segmentStartRows.count() - 1); |
|
2507 int leftSegment = segment; |
|
2508 const int rightSegment = leftSegment; |
|
2509 const int bottomCoordinate = segmentPositions.at(segment); |
|
2510 |
|
2511 while (leftSegment > scrollValue && |
|
2512 (bottomCoordinate - segmentPositions.at(leftSegment-1) + itemExtent) <= (viewportSize)) { |
|
2513 leftSegment--; |
|
2514 } |
|
2515 |
|
2516 const int segmentCount = rightSegment - leftSegment + 1; |
|
2517 switch (hint) { |
|
2518 case QAbstractItemView::PositionAtTop: |
|
2519 return segment; |
|
2520 case QAbstractItemView::PositionAtBottom: |
|
2521 return segment - segmentCount + 1; |
|
2522 case QAbstractItemView::PositionAtCenter: |
|
2523 return segment - (segmentCount / 2); |
|
2524 default: |
|
2525 break; |
|
2526 } |
|
2527 } |
|
2528 } |
|
2529 return scrollValue; |
|
2530 } |
|
2531 |
|
2532 void QListModeViewBase::clear() |
|
2533 { |
|
2534 flowPositions.clear(); |
|
2535 segmentPositions.clear(); |
|
2536 segmentStartRows.clear(); |
|
2537 segmentExtents.clear(); |
|
2538 batchSavedPosition = 0; |
|
2539 batchStartRow = 0; |
|
2540 batchSavedDeltaSeg = 0; |
|
2541 } |
|
2542 |
|
2543 /* |
|
2544 * IconMode ListView Implementation |
|
2545 */ |
|
2546 |
|
2547 void QIconModeViewBase::setPositionForIndex(const QPoint &position, const QModelIndex &index) |
|
2548 { |
|
2549 if (index.row() >= items.count()) |
|
2550 return; |
|
2551 const QSize oldContents = contentsSize; |
|
2552 qq->update(index); // update old position |
|
2553 moveItem(index.row(), position); |
|
2554 qq->update(index); // update new position |
|
2555 |
|
2556 if (contentsSize != oldContents) |
|
2557 dd->viewUpdateGeometries(); // update the scroll bars |
|
2558 } |
|
2559 |
|
2560 void QIconModeViewBase::appendHiddenRow(int row) |
|
2561 { |
|
2562 if (row >= 0 && row < items.count()) //remove item |
|
2563 tree.removeLeaf(items.at(row).rect(), row); |
|
2564 QCommonListViewBase::appendHiddenRow(row); |
|
2565 } |
|
2566 |
|
2567 void QIconModeViewBase::removeHiddenRow(int row) |
|
2568 { |
|
2569 QCommonListViewBase::removeHiddenRow(row); |
|
2570 if (row >= 0 && row < items.count()) //insert item |
|
2571 tree.insertLeaf(items.at(row).rect(), row); |
|
2572 } |
|
2573 |
|
2574 #ifndef QT_NO_DRAGANDDROP |
|
2575 void QIconModeViewBase::paintDragDrop(QPainter *painter) |
|
2576 { |
|
2577 if (!draggedItems.isEmpty() && viewport()->rect().contains(draggedItemsPos)) { |
|
2578 //we need to draw the items that arre dragged |
|
2579 painter->translate(draggedItemsDelta()); |
|
2580 QStyleOptionViewItemV4 option = viewOptions(); |
|
2581 option.state &= ~QStyle::State_MouseOver; |
|
2582 QVector<QModelIndex>::const_iterator it = draggedItems.begin(); |
|
2583 QListViewItem item = indexToListViewItem(*it); |
|
2584 for (; it != draggedItems.end(); ++it) { |
|
2585 item = indexToListViewItem(*it); |
|
2586 option.rect = viewItemRect(item); |
|
2587 delegate(*it)->paint(painter, option, *it); |
|
2588 } |
|
2589 } |
|
2590 } |
|
2591 |
|
2592 bool QIconModeViewBase::filterStartDrag(Qt::DropActions supportedActions) |
|
2593 { |
|
2594 // This function does the same thing as in QAbstractItemView::startDrag(), |
|
2595 // plus adding viewitems to the draggedItems list. |
|
2596 // We need these items to draw the drag items |
|
2597 QModelIndexList indexes = dd->selectionModel->selectedIndexes(); |
|
2598 if (indexes.count() > 0 ) { |
|
2599 if (viewport()->acceptDrops()) { |
|
2600 QModelIndexList::ConstIterator it = indexes.constBegin(); |
|
2601 for (; it != indexes.constEnd(); ++it) |
|
2602 if (dd->model->flags(*it) & Qt::ItemIsDragEnabled |
|
2603 && (*it).column() == dd->column) |
|
2604 draggedItems.push_back(*it); |
|
2605 } |
|
2606 QDrag *drag = new QDrag(qq); |
|
2607 drag->setMimeData(dd->model->mimeData(indexes)); |
|
2608 Qt::DropAction action = drag->exec(supportedActions, Qt::CopyAction); |
|
2609 draggedItems.clear(); |
|
2610 if (action == Qt::MoveAction) |
|
2611 dd->clearOrRemove(); |
|
2612 } |
|
2613 return true; |
|
2614 } |
|
2615 |
|
2616 bool QIconModeViewBase::filterDropEvent(QDropEvent *e) |
|
2617 { |
|
2618 if (e->source() != qq) |
|
2619 return false; |
|
2620 |
|
2621 const QSize contents = contentsSize; |
|
2622 QPoint offset(horizontalOffset(), verticalOffset()); |
|
2623 QPoint end = e->pos() + offset; |
|
2624 if (qq->acceptDrops()) { |
|
2625 const Qt::ItemFlags dropableFlags = Qt::ItemIsDropEnabled|Qt::ItemIsEnabled; |
|
2626 const QVector<QModelIndex> &dropIndices = intersectingSet(QRect(end, QSize(1, 1))); |
|
2627 foreach (const QModelIndex &index, dropIndices) |
|
2628 if ((index.flags() & dropableFlags) == dropableFlags) |
|
2629 return false; |
|
2630 } |
|
2631 QPoint start = dd->pressedPosition; |
|
2632 QPoint delta = (dd->movement == QListView::Snap ? snapToGrid(end) - snapToGrid(start) : end - start); |
|
2633 QList<QModelIndex> indexes = dd->selectionModel->selectedIndexes(); |
|
2634 for (int i = 0; i < indexes.count(); ++i) { |
|
2635 QModelIndex index = indexes.at(i); |
|
2636 QRect rect = dd->rectForIndex(index); |
|
2637 viewport()->update(dd->mapToViewport(rect, false)); |
|
2638 QPoint dest = rect.topLeft() + delta; |
|
2639 if (qq->isRightToLeft()) |
|
2640 dest.setX(dd->flipX(dest.x()) - rect.width()); |
|
2641 moveItem(index.row(), dest); |
|
2642 qq->update(index); |
|
2643 } |
|
2644 dd->stopAutoScroll(); |
|
2645 draggedItems.clear(); |
|
2646 dd->emitIndexesMoved(indexes); |
|
2647 e->accept(); // we have handled the event |
|
2648 // if the size has not grown, we need to check if it has shrinked |
|
2649 if (contentsSize != contents) { |
|
2650 if ((contentsSize.width() <= contents.width() |
|
2651 || contentsSize.height() <= contents.height())) { |
|
2652 updateContentsSize(); |
|
2653 } |
|
2654 dd->viewUpdateGeometries(); |
|
2655 } |
|
2656 return true; |
|
2657 } |
|
2658 |
|
2659 bool QIconModeViewBase::filterDragLeaveEvent(QDragLeaveEvent *e) |
|
2660 { |
|
2661 viewport()->update(draggedItemsRect()); // erase the area |
|
2662 draggedItemsPos = QPoint(-1, -1); // don't draw the dragged items |
|
2663 return QCommonListViewBase::filterDragLeaveEvent(e); |
|
2664 } |
|
2665 |
|
2666 bool QIconModeViewBase::filterDragMoveEvent(QDragMoveEvent *e) |
|
2667 { |
|
2668 if (e->source() != qq || !dd->canDecode(e)) |
|
2669 return false; |
|
2670 |
|
2671 // ignore by default |
|
2672 e->ignore(); |
|
2673 // get old dragged items rect |
|
2674 QRect itemsRect = this->itemsRect(draggedItems); |
|
2675 viewport()->update(itemsRect.translated(draggedItemsDelta())); |
|
2676 // update position |
|
2677 draggedItemsPos = e->pos(); |
|
2678 // get new items rect |
|
2679 viewport()->update(itemsRect.translated(draggedItemsDelta())); |
|
2680 // set the item under the cursor to current |
|
2681 QModelIndex index; |
|
2682 if (movement() == QListView::Snap) { |
|
2683 QRect rect(snapToGrid(e->pos() + offset()), gridSize()); |
|
2684 const QVector<QModelIndex> intersectVector = intersectingSet(rect); |
|
2685 index = intersectVector.count() > 0 ? intersectVector.last() : QModelIndex(); |
|
2686 } else { |
|
2687 index = qq->indexAt(e->pos()); |
|
2688 } |
|
2689 // check if we allow drops here |
|
2690 if (draggedItems.contains(index)) |
|
2691 e->accept(); // allow changing item position |
|
2692 else if (dd->model->flags(index) & Qt::ItemIsDropEnabled) |
|
2693 e->accept(); // allow dropping on dropenabled items |
|
2694 else if (!index.isValid()) |
|
2695 e->accept(); // allow dropping in empty areas |
|
2696 |
|
2697 // the event was treated. do autoscrolling |
|
2698 if (dd->shouldAutoScroll(e->pos())) |
|
2699 dd->startAutoScroll(); |
|
2700 return true; |
|
2701 } |
|
2702 #endif // QT_NO_DRAGANDDROP |
|
2703 |
|
2704 void QIconModeViewBase::setRowCount(int rowCount) |
|
2705 { |
|
2706 tree.create(qMax(rowCount - hiddenCount(), 0)); |
|
2707 } |
|
2708 |
|
2709 void QIconModeViewBase::scrollContentsBy(int dx, int dy, bool scrollElasticBand) |
|
2710 { |
|
2711 if (scrollElasticBand) |
|
2712 dd->scrollElasticBandBy(isRightToLeft() ? -dx : dx, dy); |
|
2713 |
|
2714 QCommonListViewBase::scrollContentsBy(dx, dy, scrollElasticBand); |
|
2715 if (!draggedItems.isEmpty()) |
|
2716 viewport()->update(draggedItemsRect().translated(dx, dy)); |
|
2717 } |
|
2718 |
|
2719 void QIconModeViewBase::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) |
|
2720 { |
|
2721 if (column() >= topLeft.column() && column() <= bottomRight.column()) { |
|
2722 QStyleOptionViewItemV4 option = viewOptions(); |
|
2723 int bottom = qMin(items.count(), bottomRight.row() + 1); |
|
2724 for (int row = topLeft.row(); row < bottom; ++row) |
|
2725 items[row].resize(itemSize(option, modelIndex(row))); |
|
2726 } |
|
2727 } |
|
2728 |
|
2729 bool QIconModeViewBase::doBatchedItemLayout(const QListViewLayoutInfo &info, int max) |
|
2730 { |
|
2731 if (info.last >= items.count()) { |
|
2732 //first we create the items |
|
2733 QStyleOptionViewItemV4 option = viewOptions(); |
|
2734 for (int row = items.count(); row <= info.last; ++row) { |
|
2735 QSize size = itemSize(option, modelIndex(row)); |
|
2736 QListViewItem item(QRect(0, 0, size.width(), size.height()), row); // default pos |
|
2737 items.append(item); |
|
2738 } |
|
2739 doDynamicLayout(info); |
|
2740 } |
|
2741 return (batchStartRow > max); // done |
|
2742 } |
|
2743 |
|
2744 QListViewItem QIconModeViewBase::indexToListViewItem(const QModelIndex &index) const |
|
2745 { |
|
2746 if (index.isValid() && index.row() < items.count()) |
|
2747 return items.at(index.row()); |
|
2748 return QListViewItem(); |
|
2749 } |
|
2750 |
|
2751 void QIconModeViewBase::initBspTree(const QSize &contents) |
|
2752 { |
|
2753 // remove all items from the tree |
|
2754 int leafCount = tree.leafCount(); |
|
2755 for (int l = 0; l < leafCount; ++l) |
|
2756 tree.leaf(l).clear(); |
|
2757 // we have to get the bounding rect of the items before we can initialize the tree |
|
2758 QBspTree::Node::Type type = QBspTree::Node::Both; // 2D |
|
2759 // simple heuristics to get better bsp |
|
2760 if (contents.height() / contents.width() >= 3) |
|
2761 type = QBspTree::Node::HorizontalPlane; |
|
2762 else if (contents.width() / contents.height() >= 3) |
|
2763 type = QBspTree::Node::VerticalPlane; |
|
2764 // build tree for the bounding rect (not just the contents rect) |
|
2765 tree.init(QRect(0, 0, contents.width(), contents.height()), type); |
|
2766 } |
|
2767 |
|
2768 QPoint QIconModeViewBase::initDynamicLayout(const QListViewLayoutInfo &info) |
|
2769 { |
|
2770 int x, y; |
|
2771 if (info.first == 0) { |
|
2772 x = info.bounds.x() + info.spacing; |
|
2773 y = info.bounds.y() + info.spacing; |
|
2774 items.reserve(rowCount() - hiddenCount()); |
|
2775 } else { |
|
2776 const QListViewItem item = items.at(info.first - 1); |
|
2777 x = item.x; |
|
2778 y = item.y; |
|
2779 if (info.flow == QListView::LeftToRight) |
|
2780 x += (info.grid.isValid() ? info.grid.width() : item.w) + info.spacing; |
|
2781 else |
|
2782 y += (info.grid.isValid() ? info.grid.height() : item.h) + info.spacing; |
|
2783 } |
|
2784 return QPoint(x, y); |
|
2785 } |
|
2786 |
|
2787 /*! |
|
2788 \internal |
|
2789 */ |
|
2790 void QIconModeViewBase::doDynamicLayout(const QListViewLayoutInfo &info) |
|
2791 { |
|
2792 const bool useItemSize = !info.grid.isValid(); |
|
2793 const QPoint topLeft = initDynamicLayout(info); |
|
2794 |
|
2795 int segStartPosition; |
|
2796 int segEndPosition; |
|
2797 int deltaFlowPosition; |
|
2798 int deltaSegPosition; |
|
2799 int deltaSegHint; |
|
2800 int flowPosition; |
|
2801 int segPosition; |
|
2802 |
|
2803 if (info.flow == QListView::LeftToRight) { |
|
2804 segStartPosition = info.bounds.left() + info.spacing; |
|
2805 segEndPosition = info.bounds.right(); |
|
2806 deltaFlowPosition = info.grid.width(); // dx |
|
2807 deltaSegPosition = (useItemSize ? batchSavedDeltaSeg : info.grid.height()); // dy |
|
2808 deltaSegHint = info.grid.height(); |
|
2809 flowPosition = topLeft.x(); |
|
2810 segPosition = topLeft.y(); |
|
2811 } else { // flow == QListView::TopToBottom |
|
2812 segStartPosition = info.bounds.top() + info.spacing; |
|
2813 segEndPosition = info.bounds.bottom(); |
|
2814 deltaFlowPosition = info.grid.height(); // dy |
|
2815 deltaSegPosition = (useItemSize ? batchSavedDeltaSeg : info.grid.width()); // dx |
|
2816 deltaSegHint = info.grid.width(); |
|
2817 flowPosition = topLeft.y(); |
|
2818 segPosition = topLeft.x(); |
|
2819 } |
|
2820 |
|
2821 if (moved.count() != items.count()) |
|
2822 moved.resize(items.count()); |
|
2823 |
|
2824 QRect rect(QPoint(), topLeft); |
|
2825 QListViewItem *item = 0; |
|
2826 for (int row = info.first; row <= info.last; ++row) { |
|
2827 item = &items[row]; |
|
2828 if (isHidden(row)) { |
|
2829 item->invalidate(); |
|
2830 } else { |
|
2831 // if we are not using a grid, we need to find the deltas |
|
2832 if (useItemSize) { |
|
2833 if (info.flow == QListView::LeftToRight) |
|
2834 deltaFlowPosition = item->w + info.spacing; |
|
2835 else |
|
2836 deltaFlowPosition = item->h + info.spacing; |
|
2837 } else { |
|
2838 item->w = qMin<int>(info.grid.width(), item->w); |
|
2839 item->h = qMin<int>(info.grid.height(), item->h); |
|
2840 } |
|
2841 |
|
2842 // create new segment |
|
2843 if (info.wrap |
|
2844 && flowPosition + deltaFlowPosition > segEndPosition |
|
2845 && flowPosition > segStartPosition) { |
|
2846 flowPosition = segStartPosition; |
|
2847 segPosition += deltaSegPosition; |
|
2848 if (useItemSize) |
|
2849 deltaSegPosition = 0; |
|
2850 } |
|
2851 // We must delay calculation of the seg adjustment, as this item |
|
2852 // may have caused a wrap to occur |
|
2853 if (useItemSize) { |
|
2854 if (info.flow == QListView::LeftToRight) |
|
2855 deltaSegHint = item->h + info.spacing; |
|
2856 else |
|
2857 deltaSegHint = item->w + info.spacing; |
|
2858 deltaSegPosition = qMax(deltaSegPosition, deltaSegHint); |
|
2859 } |
|
2860 |
|
2861 // set the position of the item |
|
2862 // ### idealy we should have some sort of alignment hint for the item |
|
2863 // ### (normally that would be a point between the icon and the text) |
|
2864 if (!moved.testBit(row)) { |
|
2865 if (info.flow == QListView::LeftToRight) { |
|
2866 if (useItemSize) { |
|
2867 item->x = flowPosition; |
|
2868 item->y = segPosition; |
|
2869 } else { // use grid |
|
2870 item->x = flowPosition + ((deltaFlowPosition - item->w) / 2); |
|
2871 item->y = segPosition; |
|
2872 } |
|
2873 } else { // TopToBottom |
|
2874 if (useItemSize) { |
|
2875 item->y = flowPosition; |
|
2876 item->x = segPosition; |
|
2877 } else { // use grid |
|
2878 item->y = flowPosition + ((deltaFlowPosition - item->h) / 2); |
|
2879 item->x = segPosition; |
|
2880 } |
|
2881 } |
|
2882 } |
|
2883 |
|
2884 // let the contents contain the new item |
|
2885 if (useItemSize) |
|
2886 rect |= item->rect(); |
|
2887 else if (info.flow == QListView::LeftToRight) |
|
2888 rect |= QRect(flowPosition, segPosition, deltaFlowPosition, deltaSegPosition); |
|
2889 else // flow == TopToBottom |
|
2890 rect |= QRect(segPosition, flowPosition, deltaSegPosition, deltaFlowPosition); |
|
2891 |
|
2892 // prepare for next item |
|
2893 flowPosition += deltaFlowPosition; // current position + item width + gap |
|
2894 } |
|
2895 } |
|
2896 batchSavedDeltaSeg = deltaSegPosition; |
|
2897 batchStartRow = info.last + 1; |
|
2898 bool done = (info.last >= rowCount() - 1); |
|
2899 // resize the content area |
|
2900 if (done || !info.bounds.contains(item->rect())) |
|
2901 contentsSize = QSize(rect.width(), rect.height()); |
|
2902 // resize tree |
|
2903 int insertFrom = info.first; |
|
2904 if (done || info.first == 0) { |
|
2905 initBspTree(rect.size()); |
|
2906 insertFrom = 0; |
|
2907 } |
|
2908 // insert items in tree |
|
2909 for (int row = insertFrom; row <= info.last; ++row) |
|
2910 tree.insertLeaf(items.at(row).rect(), row); |
|
2911 // if the new items are visble, update the viewport |
|
2912 QRect changedRect(topLeft, rect.bottomRight()); |
|
2913 if (clipRect().intersects(changedRect)) |
|
2914 viewport()->update(); |
|
2915 } |
|
2916 |
|
2917 QVector<QModelIndex> QIconModeViewBase::intersectingSet(const QRect &area) const |
|
2918 { |
|
2919 QIconModeViewBase *that = const_cast<QIconModeViewBase*>(this); |
|
2920 QBspTree::Data data(static_cast<void*>(that)); |
|
2921 QVector<QModelIndex> res; |
|
2922 that->interSectingVector = &res; |
|
2923 that->tree.climbTree(area, &QIconModeViewBase::addLeaf, data); |
|
2924 that->interSectingVector = 0; |
|
2925 return res; |
|
2926 } |
|
2927 |
|
2928 QRect QIconModeViewBase::itemsRect(const QVector<QModelIndex> &indexes) const |
|
2929 { |
|
2930 QVector<QModelIndex>::const_iterator it = indexes.begin(); |
|
2931 QListViewItem item = indexToListViewItem(*it); |
|
2932 QRect rect(item.x, item.y, item.w, item.h); |
|
2933 for (; it != indexes.end(); ++it) { |
|
2934 item = indexToListViewItem(*it); |
|
2935 rect |= viewItemRect(item); |
|
2936 } |
|
2937 return rect; |
|
2938 } |
|
2939 |
|
2940 int QIconModeViewBase::itemIndex(const QListViewItem &item) const |
|
2941 { |
|
2942 if (!item.isValid()) |
|
2943 return -1; |
|
2944 int i = item.indexHint; |
|
2945 if (i < items.count()) { |
|
2946 if (items.at(i) == item) |
|
2947 return i; |
|
2948 } else { |
|
2949 i = items.count() - 1; |
|
2950 } |
|
2951 |
|
2952 int j = i; |
|
2953 int c = items.count(); |
|
2954 bool a = true; |
|
2955 bool b = true; |
|
2956 |
|
2957 while (a || b) { |
|
2958 if (a) { |
|
2959 if (items.at(i) == item) { |
|
2960 items.at(i).indexHint = i; |
|
2961 return i; |
|
2962 } |
|
2963 a = ++i < c; |
|
2964 } |
|
2965 if (b) { |
|
2966 if (items.at(j) == item) { |
|
2967 items.at(j).indexHint = j; |
|
2968 return j; |
|
2969 } |
|
2970 b = --j > -1; |
|
2971 } |
|
2972 } |
|
2973 return -1; |
|
2974 } |
|
2975 |
|
2976 void QIconModeViewBase::addLeaf(QVector<int> &leaf, const QRect &area, |
|
2977 uint visited, QBspTree::Data data) |
|
2978 { |
|
2979 QListViewItem *vi; |
|
2980 QIconModeViewBase *_this = static_cast<QIconModeViewBase *>(data.ptr); |
|
2981 for (int i = 0; i < leaf.count(); ++i) { |
|
2982 int idx = leaf.at(i); |
|
2983 if (idx < 0 || idx >= _this->items.count()) |
|
2984 continue; |
|
2985 vi = &_this->items[idx]; |
|
2986 Q_ASSERT(vi); |
|
2987 if (vi->isValid() && vi->rect().intersects(area) && vi->visited != visited) { |
|
2988 QModelIndex index = _this->dd->listViewItemToIndex(*vi); |
|
2989 Q_ASSERT(index.isValid()); |
|
2990 _this->interSectingVector->append(index); |
|
2991 vi->visited = visited; |
|
2992 } |
|
2993 } |
|
2994 } |
|
2995 |
|
2996 void QIconModeViewBase::moveItem(int index, const QPoint &dest) |
|
2997 { |
|
2998 // does not impact on the bintree itself or the contents rect |
|
2999 QListViewItem *item = &items[index]; |
|
3000 QRect rect = item->rect(); |
|
3001 |
|
3002 // move the item without removing it from the tree |
|
3003 tree.removeLeaf(rect, index); |
|
3004 item->move(dest); |
|
3005 tree.insertLeaf(QRect(dest, rect.size()), index); |
|
3006 |
|
3007 // resize the contents area |
|
3008 contentsSize = (QRect(QPoint(0, 0), contentsSize)|QRect(dest, rect.size())).size(); |
|
3009 |
|
3010 // mark the item as moved |
|
3011 if (moved.count() != items.count()) |
|
3012 moved.resize(items.count()); |
|
3013 moved.setBit(index, true); |
|
3014 } |
|
3015 |
|
3016 QPoint QIconModeViewBase::snapToGrid(const QPoint &pos) const |
|
3017 { |
|
3018 int x = pos.x() - (pos.x() % gridSize().width()); |
|
3019 int y = pos.y() - (pos.y() % gridSize().height()); |
|
3020 return QPoint(x, y); |
|
3021 } |
|
3022 |
|
3023 QPoint QIconModeViewBase::draggedItemsDelta() const |
|
3024 { |
|
3025 if (movement() == QListView::Snap) { |
|
3026 QPoint snapdelta = QPoint((offset().x() % gridSize().width()), |
|
3027 (offset().y() % gridSize().height())); |
|
3028 return snapToGrid(draggedItemsPos + snapdelta) - snapToGrid(pressedPosition()) - snapdelta; |
|
3029 } |
|
3030 return draggedItemsPos - pressedPosition(); |
|
3031 } |
|
3032 |
|
3033 QRect QIconModeViewBase::draggedItemsRect() const |
|
3034 { |
|
3035 QRect rect = itemsRect(draggedItems); |
|
3036 rect.translate(draggedItemsDelta()); |
|
3037 return rect; |
|
3038 } |
|
3039 |
|
3040 void QListViewPrivate::scrollElasticBandBy(int dx, int dy) |
|
3041 { |
|
3042 if (dx > 0) // right |
|
3043 elasticBand.moveRight(elasticBand.right() + dx); |
|
3044 else if (dx < 0) // left |
|
3045 elasticBand.moveLeft(elasticBand.left() - dx); |
|
3046 if (dy > 0) // down |
|
3047 elasticBand.moveBottom(elasticBand.bottom() + dy); |
|
3048 else if (dy < 0) // up |
|
3049 elasticBand.moveTop(elasticBand.top() - dy); |
|
3050 } |
|
3051 |
|
3052 void QIconModeViewBase::clear() |
|
3053 { |
|
3054 tree.destroy(); |
|
3055 items.clear(); |
|
3056 moved.clear(); |
|
3057 batchStartRow = 0; |
|
3058 batchSavedDeltaSeg = 0; |
|
3059 } |
|
3060 |
|
3061 void QIconModeViewBase::updateContentsSize() |
|
3062 { |
|
3063 QRect bounding; |
|
3064 for (int i = 0; i < items.count(); ++i) |
|
3065 bounding |= items.at(i).rect(); |
|
3066 contentsSize = bounding.size(); |
|
3067 } |
|
3068 |
|
3069 /*! |
|
3070 \reimp |
|
3071 */ |
|
3072 void QListView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) |
|
3073 { |
|
3074 #ifndef QT_NO_ACCESSIBILITY |
|
3075 if (QAccessible::isActive()) { |
|
3076 if (current.isValid()) { |
|
3077 int entry = visualIndex(current) + 1; |
|
3078 QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus); |
|
3079 } |
|
3080 } |
|
3081 #endif |
|
3082 QAbstractItemView::currentChanged(current, previous); |
|
3083 } |
|
3084 |
|
3085 /*! |
|
3086 \reimp |
|
3087 */ |
|
3088 void QListView::selectionChanged(const QItemSelection &selected, |
|
3089 const QItemSelection &deselected) |
|
3090 { |
|
3091 #ifndef QT_NO_ACCESSIBILITY |
|
3092 if (QAccessible::isActive()) { |
|
3093 // ### does not work properly for selection ranges. |
|
3094 QModelIndex sel = selected.indexes().value(0); |
|
3095 if (sel.isValid()) { |
|
3096 int entry = visualIndex(sel) + 1; |
|
3097 QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection); |
|
3098 } |
|
3099 QModelIndex desel = deselected.indexes().value(0); |
|
3100 if (desel.isValid()) { |
|
3101 int entry = visualIndex(desel) + 1; |
|
3102 QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove); |
|
3103 } |
|
3104 } |
|
3105 #endif |
|
3106 QAbstractItemView::selectionChanged(selected, deselected); |
|
3107 } |
|
3108 |
|
3109 int QListView::visualIndex(const QModelIndex &index) const |
|
3110 { |
|
3111 Q_D(const QListView); |
|
3112 d->executePostedLayout(); |
|
3113 QListViewItem itm = d->indexToListViewItem(index); |
|
3114 return d->commonListView->itemIndex(itm); |
|
3115 } |
|
3116 |
|
3117 QT_END_NAMESPACE |
|
3118 |
|
3119 #endif // QT_NO_LISTVIEW |