util/src/gui/itemviews/qlistview.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     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 &current, 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