src/gui/itemviews/qlistview.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the QtGui module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include "qlistview.h"
00025 
00026 #ifndef QT_NO_LISTVIEW
00027 #include <qabstractitemdelegate.h>
00028 #include <qapplication.h>
00029 #include <qpainter.h>
00030 #include <qbitmap.h>
00031 #include <qvector.h>
00032 #include <qstyle.h>
00033 #include <qevent.h>
00034 #include <qscrollbar.h>
00035 #include <qrubberband.h>
00036 #include <private/qlistview_p.h>
00037 #include <qdebug.h>
00038 
00148 QListView::QListView(QWidget *parent)
00149     : QAbstractItemView(*new QListViewPrivate, parent)
00150 {
00151     setViewMode(ListMode);
00152     setSelectionMode(SingleSelection);
00153 }
00154 
00158 QListView::QListView(QListViewPrivate &dd, QWidget *parent)
00159     : QAbstractItemView(dd, parent)
00160 {
00161     setViewMode(ListMode);
00162     setSelectionMode(SingleSelection);
00163 }
00164 
00168 QListView::~QListView()
00169 {
00170 }
00171 
00189 void QListView::setMovement(Movement movement)
00190 {
00191     Q_D(QListView);
00192     d->modeProperties |= uint(QListViewPrivate::Movement);
00193     d->movement = movement;
00194 
00195 #ifndef QT_NO_DRAGANDDROP
00196     bool movable = (movement != Static);
00197     setDragEnabled(movable);
00198     d->viewport->setAcceptDrops(movable);
00199 #endif
00200 
00201     d->doDelayedItemsLayout();
00202 }
00203 
00204 QListView::Movement QListView::movement() const
00205 {
00206     Q_D(const QListView);
00207     return d->movement;
00208 }
00209 
00225 void QListView::setFlow(Flow flow)
00226 {
00227     Q_D(QListView);
00228     d->modeProperties |= uint(QListViewPrivate::Flow);
00229     d->flow = flow;
00230     d->doDelayedItemsLayout();
00231 }
00232 
00233 QListView::Flow QListView::flow() const
00234 {
00235     Q_D(const QListView);
00236     return d->flow;
00237 }
00238 
00252 void QListView::setWrapping(bool enable)
00253 {
00254     Q_D(QListView);
00255     d->modeProperties |= uint(QListViewPrivate::Wrap);
00256     d->wrap = enable;
00257     d->doDelayedItemsLayout();
00258 }
00259 
00260 bool QListView::isWrapping() const
00261 {
00262     Q_D(const QListView);
00263     return d->wrap;
00264 }
00265 
00276 void QListView::setResizeMode(ResizeMode mode)
00277 {
00278     Q_D(QListView);
00279     d->modeProperties |= uint(QListViewPrivate::ResizeMode);
00280     d->resizeMode = mode;
00281 }
00282 
00283 QListView::ResizeMode QListView::resizeMode() const
00284 {
00285     Q_D(const QListView);
00286     return d->resizeMode;
00287 }
00288 
00302 void QListView::setLayoutMode(LayoutMode mode)
00303 {
00304     Q_D(QListView);
00305     d->layoutMode = mode;
00306 }
00307 
00308 QListView::LayoutMode QListView::layoutMode() const
00309 {
00310     Q_D(const QListView);
00311     return d->layoutMode;
00312 }
00313 
00326 void QListView::setSpacing(int space)
00327 {
00328     Q_D(QListView);
00329     d->modeProperties |= uint(QListViewPrivate::Spacing);
00330     d->spacing = space;
00331     d->doDelayedItemsLayout();
00332 }
00333 
00334 int QListView::spacing() const
00335 {
00336     Q_D(const QListView);
00337     return d->spacing;
00338 }
00339 
00350 void QListView::setBatchSize(int batchSize)
00351 {
00352     Q_D(QListView);
00353     if (batchSize <= 0) {
00354         qWarning("Invalid batchSize (%d)", batchSize);
00355         return;
00356     }
00357     d->batchSize = batchSize;
00358 }
00359 
00360 int QListView::batchSize() const
00361 {
00362     Q_D(const QListView);
00363     return d->batchSize;
00364 }
00365 
00381 void QListView::setGridSize(const QSize &size)
00382 {
00383     Q_D(QListView);
00384     d->modeProperties |= uint(QListViewPrivate::GridSize);
00385     d->gridSize = size;
00386     d->doDelayedItemsLayout();
00387 }
00388 
00389 QSize QListView::gridSize() const
00390 {
00391     Q_D(const QListView);
00392     return d->gridSize;
00393 }
00394 
00410 void QListView::setViewMode(ViewMode mode)
00411 {
00412     Q_D(QListView);
00413     d->viewMode = mode;
00414 
00415     if (mode == ListMode) {
00416         if (!(d->modeProperties & QListViewPrivate::Wrap))
00417             d->wrap = false;
00418         if (!(d->modeProperties & QListViewPrivate::Spacing))
00419             d->spacing = 0;
00420         if (!(d->modeProperties & QListViewPrivate::GridSize))
00421             d->gridSize = QSize();
00422         if (!(d->modeProperties & QListViewPrivate::Flow))
00423             d->flow = TopToBottom;
00424         if (!(d->modeProperties & QListViewPrivate::Movement))
00425             d->movement = Static;
00426         if (!(d->modeProperties & QListViewPrivate::ResizeMode))
00427             d->resizeMode = Fixed;
00428     } else {
00429         if (!(d->modeProperties & QListViewPrivate::Wrap))
00430             d->wrap = true;
00431         if (!(d->modeProperties & QListViewPrivate::Spacing))
00432             d->spacing = 0;
00433         if (!(d->modeProperties & QListViewPrivate::GridSize))
00434             d->gridSize = QSize();
00435         if (!(d->modeProperties & QListViewPrivate::Flow))
00436             d->flow = LeftToRight;
00437         if (!(d->modeProperties & QListViewPrivate::Movement))
00438             d->movement = Free;
00439         if (!(d->modeProperties & QListViewPrivate::ResizeMode))
00440             d->resizeMode = Fixed;
00441     }
00442 
00443 #ifndef QT_NO_DRAGANDDROP
00444     bool movable = (d->movement != Static);
00445     setDragEnabled(movable);
00446     setAcceptDrops(movable);
00447 #endif
00448     d->doDelayedItemsLayout();
00449 }
00450 
00451 QListView::ViewMode QListView::viewMode() const
00452 {
00453     Q_D(const QListView);
00454     return d->viewMode;
00455 }
00456 
00464 void QListView::clearPropertyFlags()
00465 {
00466     Q_D(QListView);
00467     d->modeProperties = 0;
00468 }
00469 
00473 bool QListView::isRowHidden(int row) const
00474 {
00475     Q_D(const QListView);
00476     return d->hiddenRows.contains(row);
00477 }
00478 
00483 void QListView::setRowHidden(int row, bool hide)
00484 {
00485     Q_D(QListView);
00486     if (hide && !isRowHidden(row)) {
00487         d->removeItem(row);
00488         d->hiddenRows.append(row);
00489     } else if (!hide && isRowHidden(row)) {
00490         d->hiddenRows.remove(d->hiddenRows.indexOf(row));
00491         d->insertItem(row);
00492     }
00493     if (d->movement == Static)
00494         d->doDelayedItemsLayout();
00495     else
00496         d->viewport->update();
00497 }
00498 
00502 QRect QListView::visualRect(const QModelIndex &index) const
00503 {
00504     Q_D(const QListView);
00505     return d->mapToViewport(rectForIndex(index));
00506 }
00507 
00511 void QListView::scrollTo(const QModelIndex &index, ScrollHint hint)
00512 {
00513     Q_D(QListView);
00514 
00515     if (index.parent() != d->root || index.column() != d->column)
00516         return;
00517 
00518     const QRect area = d->viewport->rect();
00519     const QRect rect = visualRect(index);
00520     if (hint == EnsureVisible && area.contains(rect)) {
00521         d->setDirtyRegion(rect);
00522         return;
00523     }
00524 
00525     // vertical
00526     if (d->flow == QListView::TopToBottom || d->wrap) {
00527         const bool above = (hint == EnsureVisible && rect.top() < area.top());
00528         const bool below = (hint == EnsureVisible && rect.bottom() > area.bottom());
00529         int verticalValue = verticalScrollBar()->value();
00530         if (verticalScrollMode() == QAbstractItemView::ScrollPerItem && d->movement == Static) {
00531             const QListViewItem item = d->indexToListViewItem(index);
00532             const int itemIndex = d->itemIndex(item);
00533             verticalValue = qBound(0, verticalValue, d->flowPositions.count() - 1);
00534             if (above)
00535                 verticalValue = d->perItemScrollToValue(itemIndex, verticalValue,
00536                                                         area.height(), PositionAtTop,
00537                                                         Qt::Vertical);
00538             else if (below)
00539                 verticalValue = d->perItemScrollToValue(itemIndex, verticalValue,
00540                                                         area.height(), PositionAtBottom,
00541                                                         Qt::Vertical);
00542             else if (hint != EnsureVisible)
00543                 verticalValue = d->perItemScrollToValue(itemIndex, verticalValue,
00544                                                         area.height(), hint,
00545                                                         Qt::Vertical);
00546         } else {
00547             if (hint == PositionAtTop || above)
00548                 verticalValue += rect.top();
00549             else if (hint == PositionAtBottom || below)
00550                 verticalValue += rect.bottom() - area.height();
00551             else if (hint == PositionAtCenter)
00552                 verticalValue += rect.top() - ((area.height() - rect.height()) / 2);
00553         }
00554         verticalScrollBar()->setValue(verticalValue);
00555     }
00556 
00557     // horizontal
00558     if (d->flow == QListView::LeftToRight || d->wrap) {
00559         const bool leftOf = isRightToLeft()
00560                             ? (rect.left() < area.left()) && (rect.right() < area.right())
00561                             : rect.left() < area.left();
00562         const bool rightOf = isRightToLeft()
00563                              ? rect.right() > area.right()
00564                              : (rect.right() > area.right()) && (rect.left() > area.left());
00565         int horizontalValue = horizontalScrollBar()->value();
00566 
00567         if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem && d->movement == Static) {
00568             const QListViewItem item = d->indexToListViewItem(index);
00569             const int itemIndex = d->itemIndex(item);
00570             horizontalValue = qBound(0, horizontalValue, d->flowPositions.count() - 1);
00571             if (leftOf)
00572                 horizontalValue = d->perItemScrollToValue(itemIndex, horizontalValue,
00573                                                           area.width(), PositionAtTop,
00574                                                           Qt::Horizontal);
00575             else if (rightOf)
00576                 horizontalValue = d->perItemScrollToValue(itemIndex, horizontalValue,
00577                                                           area.width(), PositionAtBottom,
00578                                                           Qt::Horizontal);
00579             else if (hint != EnsureVisible) {
00580                 horizontalValue = d->perItemScrollToValue(itemIndex, horizontalValue,
00581                                                           area.width(), hint,
00582                                                       Qt::Horizontal);
00583             }
00584         } else {
00585             if (isRightToLeft()) {
00586                 if (hint == PositionAtCenter) {
00587                     horizontalValue += ((area.width() - rect.width()) / 2) - rect.left();
00588                 } else {
00589                     if (leftOf)
00590                         horizontalValue -= rect.left();
00591                     else if (rightOf)
00592                         horizontalValue += area.width() - rect.right();
00593                 }
00594             } else {
00595                 if (hint == PositionAtCenter) {
00596                     horizontalValue += rect.left() - ((area.width()- rect.width()) / 2);
00597                 } else {
00598                     if (leftOf)
00599                         horizontalValue += rect.left();
00600                     else if (rightOf)
00601                         horizontalValue += rect.right() - area.width();
00602                 }
00603             }
00604         }
00605         horizontalScrollBar()->setValue(horizontalValue);
00606     }
00607 }
00608 
00612 void QListView::reset()
00613 {
00614     Q_D(QListView);
00615     d->clear();
00616     d->hiddenRows.clear();
00617     QAbstractItemView::reset();
00618 }
00619 
00623 void QListView::setRootIndex(const QModelIndex &index)
00624 {
00625     Q_D(QListView);
00626     d->column = qBound(0, d->column, d->model->columnCount(index) - 1);
00627     QAbstractItemView::setRootIndex(index);
00628 }
00629 
00635 void QListView::scrollContentsBy(int dx, int dy)
00636 {
00637     Q_D(QListView);
00638 
00639     dx = isRightToLeft() ? -dx : dx;
00640 
00641     // ### reorder this logic
00642     if (d->movement == Static) {
00643         const bool vertical = verticalScrollMode() == QAbstractItemView::ScrollPerItem;
00644         const bool horizontal = horizontalScrollMode() == QAbstractItemView::ScrollPerItem;
00645         if (d->wrap) {
00646             if (d->segmentPositions.isEmpty())
00647                 return;
00648             const int max = d->segmentPositions.count() - 1;
00649             if (horizontal && d->flow == TopToBottom && dx != 0) {
00650                 int currentValue = qBound(0, horizontalScrollBar()->value(), max);
00651                 int previousValue = qBound(0, currentValue + dx, max);
00652                 int currentCoordinate = d->segmentPositions.at(currentValue);
00653                 int previousCoordinate = d->segmentPositions.at(previousValue);
00654                 dx = previousCoordinate - currentCoordinate;
00655             } else if (vertical && d->flow == LeftToRight && dy != 0) {
00656                 int currentValue = qBound(0, verticalScrollBar()->value(), max);
00657                 int previousValue = qBound(0, currentValue + dy, max);
00658                 int currentCoordinate = d->segmentPositions.at(currentValue);
00659                 int previousCoordinate = d->segmentPositions.at(previousValue);
00660                 dy = previousCoordinate - currentCoordinate;
00661             }
00662         } else {
00663             if (d->flowPositions.isEmpty())
00664                 return;
00665             const int max = d->flowPositions.count() - 1;
00666             if (vertical && d->flow == TopToBottom && dy != 0) {
00667                 int currentValue = qBound(0, verticalScrollBar()->value(), max);
00668                 int previousValue = qBound(0, currentValue + dy, max);
00669                 int currentCoordinate = d->flowPositions.at(currentValue);
00670                 int previousCoordinate = d->flowPositions.at(previousValue);
00671                 dy = previousCoordinate - currentCoordinate;
00672             } else if (horizontal && d->flow == LeftToRight && dx != 0) {
00673                 int currentValue = qBound(0, horizontalScrollBar()->value(), max);
00674                 int previousValue = qBound(0, currentValue + dx, max);
00675                 int currentCoordinate = d->flowPositions.at(currentValue);
00676                 int previousCoordinate = d->flowPositions.at(previousValue);
00677                 dx = previousCoordinate - currentCoordinate;
00678             }
00679         }
00680     }
00681 
00682     if (state() == DragSelectingState) {
00683         if (dx > 0) // right
00684             d->elasticBand.moveRight(d->elasticBand.right() + dx);
00685         else if (dx < 0) // left
00686             d->elasticBand.moveLeft(d->elasticBand.left() - dx);
00687         if (dy > 0) // down
00688             d->elasticBand.moveBottom(d->elasticBand.bottom() + dy);
00689         else if (dy < 0) // up
00690             d->elasticBand.moveTop(d->elasticBand.top() - dy);
00691     }
00692 
00693     d->scrollContentsBy(dx, dy);
00694 
00695     // update the dragged items
00696     if (!d->draggedItems.isEmpty())
00697         d->setDirtyRegion(d->draggedItemsRect().translated(dx, dy));
00698 }
00699 
00706 void QListView::resizeContents(int width, int height)
00707 {
00708     Q_D(QListView);
00709     d->contentsSize = QSize(width, height);
00710 }
00711 
00715 QSize QListView::contentsSize() const
00716 {
00717     return d_func()->contentsSize;
00718 }
00719 
00723 void QListView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
00724 {
00725     Q_D(QListView);
00726     if (d->movement != Static
00727         && d->column >= topLeft.column()
00728         && d->column <= bottomRight.column()) {
00729         QStyleOptionViewItemV2 option = d->viewOptionsV2();
00730         int bottom = qMin(d->items.count(), bottomRight.row() + 1);
00731         for (int row = topLeft.row(); row < bottom; ++row) {
00732             QModelIndex idx = d->model->index(row, d->column, d->root);
00733             d->items[row].resize(d->itemSize(option, idx));
00734         }
00735     }
00736     QAbstractItemView::dataChanged(topLeft, bottomRight);
00737 }
00738 
00742 void QListView::rowsInserted(const QModelIndex &parent, int start, int end)
00743 {
00744     Q_D(QListView);
00745     // if the parent is above d->root in the tree, nothing will happen
00746     if (parent == d->root) {
00747         int count = (end - start + 1);
00748         for (int i = d->hiddenRows.count() - 1; i >= 0; --i)
00749             if (d->hiddenRows.at(i) >= start)
00750                 d->hiddenRows[i] += count;
00751     }
00752     d->clear();
00753     d->doDelayedItemsLayout();
00754     QAbstractItemView::rowsInserted(parent, start, end);
00755 }
00756 
00760 void QListView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
00761 {
00762     Q_D(QListView);
00763     // if the parent is above d->root in the tree, nothing will happen
00764     QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
00765     if (parent == d->root) {
00766         int count = (end - start + 1);
00767         for (int i = d->hiddenRows.count() - 1; i >= 0; --i) {
00768             if (d->hiddenRows.at(i) >= start) {
00769                 if (d->hiddenRows.at(i) <= end) {
00770                     d->hiddenRows.remove(i);
00771                 } else {
00772                     d->hiddenRows[i] -= count;
00773                 }
00774             }
00775         }
00776     }
00777     d->clear();
00778     d->doDelayedItemsLayout();
00779 }
00780 
00784 void QListView::mouseMoveEvent(QMouseEvent *e)
00785 {
00786     Q_D(QListView);
00787     QAbstractItemView::mouseMoveEvent(e);
00788     if (d->movement != Static
00789         && state() == DragSelectingState
00790         && d->selectionMode != SingleSelection) {
00791         QRect rect(d->pressedPosition, e->pos() + QPoint(horizontalOffset(), verticalOffset()));
00792         rect = rect.normalized();
00793         d->setDirtyRegion(d->mapToViewport(rect.united(d->elasticBand)));
00794         d->elasticBand = rect;
00795     }
00796 }
00797 
00801 void QListView::mouseReleaseEvent(QMouseEvent *e)
00802 {
00803     Q_D(QListView);
00804     QAbstractItemView::mouseReleaseEvent(e);
00805     if (d->elasticBand.isValid()) {
00806         d->setDirtyRegion(d->mapToViewport(d->elasticBand));
00807         d->elasticBand = QRect();
00808     }
00809 }
00810 
00814 void QListView::timerEvent(QTimerEvent *e)
00815 {
00816     Q_D(QListView);
00817     if (e->timerId() == d->delayedLayout.timerId()) {
00818         setState(ExpandingState); // showing the scrollbars will trigger a resize event,
00819         doItemsLayout();          // so we set the state to expanding to avoid
00820         setState(NoState);        // triggering another layout
00821     } else if (e->timerId() == d->batchLayoutTimer.timerId()) {
00822         if (d->doItemsLayout(d->batchSize)) { // layout is done
00823             d->batchLayoutTimer.stop();
00824             updateGeometries();
00825             d->viewport->update();
00826         }
00827     }
00828     QAbstractItemView::timerEvent(e);
00829 }
00830 
00834 void QListView::resizeEvent(QResizeEvent *e)
00835 {
00836     Q_D(QListView);
00837     QAbstractItemView::resizeEvent(e);
00838     if (state() == NoState) {
00839         // if we are in adjust mode, post a delayed layout
00840         if (d->resizeMode == Adjust) {
00841             QSize delta = e->size() - e->oldSize();
00842             if (!d->delayedLayout.isActive()
00843                 && ((d->flow == LeftToRight && delta.width() != 0)
00844                     || (d->flow == TopToBottom && delta.height() != 0))) {
00845                 d->delayedLayout.start(100, this); // wait 1/10 sec before starting the layout
00846             }
00847         }
00848     }
00849 }
00850 
00851 #ifndef QT_NO_DRAGANDDROP
00852 
00856 void QListView::dragMoveEvent(QDragMoveEvent *e)
00857 {
00858     Q_D(QListView);
00859     if (e->source() == this && d->movement != Static) {
00860         // the ignore by default
00861         e->ignore();
00862         if (d->canDecode(e)) {
00863             // get old dragged items rect
00864             QRect itemsRect = d->itemsRect(d->draggedItems);
00865             d->setDirtyRegion(itemsRect.translated(d->draggedItemsDelta()));
00866             // update position
00867             d->draggedItemsPos = e->pos();
00868             // get new items rect
00869             d->setDirtyRegion(itemsRect.translated(d->draggedItemsDelta()));
00870             // set the item under the cursor to current
00871             QModelIndex index = indexAt(e->pos());
00872             // check if we allow drops here
00873             if (e->source() == this && d->draggedItems.contains(index))
00874                 e->accept(); // allow changing item position
00875             else if (d->model->flags(index) & Qt::ItemIsDropEnabled)
00876                 e->accept(); // allow dropping on dropenabled items
00877             else if (!index.isValid())
00878                 e->accept(); // allow dropping in empty areas
00879         }
00880         // do autoscrolling
00881         if (d->shouldAutoScroll(e->pos()))
00882             startAutoScroll();
00883     } else { // not internal
00884         QAbstractItemView::dragMoveEvent(e);
00885     }
00886 }
00887 
00891 void QListView::dragLeaveEvent(QDragLeaveEvent *e)
00892 {
00893     Q_D(QListView);
00894     if (d->movement != Static) {
00895         d->viewport->update(d->draggedItemsRect()); // erase the area
00896         d->draggedItemsPos = QPoint(-1, -1); // don't draw the dragged items
00897     }
00898     QAbstractItemView::dragLeaveEvent(e);
00899 }
00900 
00904 void QListView::dropEvent(QDropEvent *event)
00905 {
00906     Q_D(QListView);
00907     if (event->source() == this && d->movement != Static)
00908         internalDrop(event);
00909     else
00910         QAbstractItemView::dropEvent(event);
00911 }
00912 
00916 void QListView::startDrag(Qt::DropActions supportedActions)
00917 {
00918     Q_D(QListView);
00919     if (d->movement != Static)
00920         internalDrag(supportedActions);
00921     else
00922         QAbstractItemView::startDrag(supportedActions);
00923 }
00924 
00931 void QListView::internalDrop(QDropEvent *event)
00932 {
00933     Q_D(QListView);
00934     QPoint offset(horizontalOffset(), verticalOffset());
00935     QPoint end = event->pos() + offset;
00936     QPoint start = d->pressedPosition;
00937     QPoint delta = (d->movement == Snap ?
00938                     d->snapToGrid(end) - d->snapToGrid(start) : end - start);
00939     QList<QModelIndex> indexes = d->selectionModel->selectedIndexes();
00940     for (int i = 0; i < indexes.count(); ++i) {
00941         QModelIndex index = indexes.at(i);
00942         QRect rect = rectForIndex(index);
00943         d->setDirtyRegion(d->mapToViewport(rect));
00944         QPoint dest = rect.topLeft() + delta;
00945         if (isRightToLeft())
00946             dest.setX(d->flipX(dest.x()) - rect.width());
00947         d->moveItem(index.row(), dest);
00948         d->setDirtyRegion(visualRect(index));
00949     }
00950     stopAutoScroll();
00951     d->draggedItems.clear();
00952     emit indexesMoved(indexes);
00953     event->accept(); // we have handled the event
00954 }
00955 
00962 void QListView::internalDrag(Qt::DropActions supportedActions)
00963 {
00964     // This function does the same thing as in QAbstractItemView::startDrag(),
00965     // plus adding viewitems to the draggedItems list.
00966     // We need these items to draw the drag items
00967     Q_D(QListView);
00968     QModelIndexList indexes = d->selectionModel->selectedIndexes();
00969     if (indexes.count() > 0 ) {
00970         QModelIndexList::ConstIterator it = indexes.constBegin();
00971         for (; it != indexes.constEnd(); ++it)
00972             if (d->model->flags(*it) & Qt::ItemIsDragEnabled)
00973                 d->draggedItems.push_back(*it);
00974         QDrag *drag = new QDrag(this);
00975         drag->setMimeData(d->model->mimeData(indexes));
00976         Qt::DropAction action = drag->start(supportedActions);
00977         d->draggedItems.clear();
00978         if (action == Qt::MoveAction)
00979             d->clearOrRemove();
00980     }
00981 }
00982 
00983 #endif // QT_NO_DRAGANDDROP
00984 
00988 QStyleOptionViewItem QListView::viewOptions() const
00989 {
00990     Q_D(const QListView);
00991     QStyleOptionViewItem option = QAbstractItemView::viewOptions();
00992     if (!d->iconSize.isValid()) { // otherwise it was already set in abstractitemview
00993         int pm = (d->viewMode == ListMode
00994                   ? style()->pixelMetric(QStyle::PM_ListViewIconSize)
00995                   : style()->pixelMetric(QStyle::PM_IconViewIconSize));
00996         option.decorationSize = QSize(pm, pm);
00997     }
00998     if (d->viewMode == IconMode) {
00999         option.showDecorationSelected = false;
01000         option.decorationPosition = QStyleOptionViewItem::Top;
01001         option.displayAlignment = Qt::AlignCenter;
01002     } else {
01003         option.decorationPosition = QStyleOptionViewItem::Left;
01004     }
01005     return option;
01006 }
01007 
01011 void QListView::paintEvent(QPaintEvent *e)
01012 {
01013     Q_D(QListView);
01014     if (!d->itemDelegate)
01015         return;
01016     QStyleOptionViewItemV2 option = d->viewOptionsV2();
01017     QPainter painter(d->viewport);
01018     QRect area = e->rect();
01019 
01020     QVector<QModelIndex> toBeRendered;
01021 //     QVector<QRect> rects = e->region().rects();
01022 //     for (int i = 0; i < rects.size(); ++i) {
01023 //         d->intersectingSet(rects.at(i).translated(horizontalOffset(), verticalOffset()));
01024 //         toBeRendered += d->intersectVector;
01025 //     }
01026     d->intersectingSet(e->rect().translated(horizontalOffset(), verticalOffset()), false);
01027     toBeRendered = d->intersectVector;
01028 
01029     const QModelIndex current = currentIndex();
01030     const QModelIndex hover = d->hover;
01031     const QAbstractItemModel *itemModel = d->model;
01032     const QItemSelectionModel *selections = d->selectionModel;
01033     const bool focus = (hasFocus() || d->viewport->hasFocus()) && current.isValid();
01034     const bool alternate = d->alternatingColors;
01035     const QStyle::State state = option.state;
01036     const QAbstractItemView::State viewState = this->state();
01037     const bool enabled = (state & QStyle::State_Enabled) != 0;
01038 
01039     bool alternateBase = false;
01040     int previousRow = -2; // trigger the alternateBase adjustment on first pass
01041 
01042     QVector<QModelIndex>::const_iterator end = toBeRendered.constEnd();
01043     for (QVector<QModelIndex>::const_iterator it = toBeRendered.constBegin(); it != end; ++it) {
01044         Q_ASSERT((*it).isValid());
01045         option.rect = visualRect(*it);
01046         option.state = state;
01047         if (selections && selections->isSelected(*it))
01048             option.state |= QStyle::State_Selected;
01049         if (enabled) {
01050             QPalette::ColorGroup cg;
01051             if ((itemModel->flags(*it) & Qt::ItemIsEnabled) == 0) {
01052                 option.state &= ~QStyle::State_Enabled;
01053                 cg = QPalette::Disabled;
01054             } else {
01055                 cg = QPalette::Normal;
01056             }
01057             option.palette.setCurrentColorGroup(cg);
01058         }
01059         if (focus && current == *it) {
01060             option.state |= QStyle::State_HasFocus;
01061             if (viewState == EditingState)
01062                 option.state |= QStyle::State_Editing;
01063         }
01064         if (*it == hover)
01065             option.state |= QStyle::State_MouseOver;
01066         else
01067             option.state &= ~QStyle::State_MouseOver;
01068 
01069         if (alternate) {
01070             int row = (*it).row();
01071             if (row != previousRow + 1) {
01072                 // adjust alternateBase according to rows in the "gap"
01073                 if (!d->hiddenRows.isEmpty()) {
01074                     for (int r = qMax(previousRow + 1, 0); r < row; ++r) {
01075                         if (!d->hiddenRows.contains(r))
01076                             alternateBase = !alternateBase;
01077                     }
01078                 } else {
01079                     alternateBase = (row & 1) != 0;
01080                 }
01081             }
01082             QBrush fill;
01083             if (alternateBase) {
01084                 option.features |= QStyleOptionViewItemV2::Alternate;
01085                 fill = option.palette.brush(QPalette::AlternateBase);
01086             } else {
01087                 option.features &= ~QStyleOptionViewItemV2::Alternate;
01088                 fill = option.palette.brush(QPalette::Base);
01089             }
01090             alternateBase = !alternateBase;
01091             painter.fillRect(option.rect, fill);
01092             previousRow = row;
01093         }
01094 
01095         d->delegateForIndex(*it)->paint(&painter, option, *it);
01096     }
01097 
01098 #ifndef QT_NO_DRAGANDDROP
01099     if (!d->draggedItems.isEmpty() && d->viewport->rect().contains(d->draggedItemsPos)) {
01100         QPoint delta = d->draggedItemsDelta();
01101         painter.translate(delta.x(), delta.y());
01102         d->drawItems(&painter, d->draggedItems);
01103     }
01104     // FIXME: Until the we can provide a proper drop indicator
01105     // in IconMode, it makes no sense to show it
01106     if (d->viewMode == ListMode)
01107         d->paintDropIndicator(&painter);
01108 #endif
01109 
01110 #ifndef QT_NO_RUBBERBAND
01111     if (d->elasticBand.isValid()) {
01112         QStyleOptionRubberBand opt;
01113         opt.initFrom(this);
01114         opt.shape = QRubberBand::Rectangle;
01115         opt.opaque = false;
01116         opt.rect = d->mapToViewport(d->elasticBand).intersected(
01117             d->viewport->rect().adjusted(-16, -16, 16, 16));
01118         painter.save();
01119         style()->drawControl(QStyle::CE_RubberBand, &opt, &painter);
01120         painter.restore();
01121     }
01122 #endif
01123 }
01124 
01128 QModelIndex QListView::indexAt(const QPoint &p) const
01129 {
01130     Q_D(const QListView);
01131     QRect rect(p.x() + horizontalOffset(), p.y() + verticalOffset(), 1, 1);
01132     d->intersectingSet(rect);
01133     QModelIndex index = d->intersectVector.count() > 0
01134                         ? d->intersectVector.last() : QModelIndex();
01135     if (index.isValid() && visualRect(index).contains(p))
01136         return index;
01137     return QModelIndex();
01138 }
01139 
01143 int QListView::horizontalOffset() const
01144 {
01145     Q_D(const QListView);
01146     if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem && d->movement == Static ) {
01147         if (d->wrap) {
01148             if (d->flow == TopToBottom && !d->segmentPositions.isEmpty()) {
01149                 const int max = d->segmentPositions.count() - 1;
01150                 int currentValue = qBound(0, horizontalScrollBar()->value(), max);
01151                 int position = d->segmentPositions.at(currentValue);
01152                 int maximumValue = qBound(0, horizontalScrollBar()->maximum(), max);
01153                 int maximum = d->segmentPositions.at(maximumValue);
01154                 return (isRightToLeft() ? maximum - position : position);
01155             }
01156             //return 0;
01157         } else {
01158             if (d->flow == LeftToRight && !d->flowPositions.isEmpty()) {
01159                 int position = d->flowPositions.at(horizontalScrollBar()->value());
01160                 int maximum = d->flowPositions.at(horizontalScrollBar()->maximum());
01161                 return (isRightToLeft() ? maximum - position : position);
01162             }
01163             //return 0;
01164         }
01165     }
01166     return (isRightToLeft()
01167             ? horizontalScrollBar()->maximum() - horizontalScrollBar()->value()
01168             : horizontalScrollBar()->value());
01169 }
01170 
01174 int QListView::verticalOffset() const
01175 {
01176     Q_D(const QListView);
01177     if (verticalScrollMode() == QAbstractItemView::ScrollPerItem && d->movement == Static) {
01178         if (d->wrap) {
01179             if (d->flow == LeftToRight && !d->segmentPositions.isEmpty()) {
01180                 int value = verticalScrollBar()->value();
01181                 if (value >= d->segmentPositions.count()) {
01182                     qWarning("QListView: Vertical scrollbar is out of bounds");
01183                     return 0;
01184                 }
01185                 return d->segmentPositions.at(value);
01186             }
01187         } else {
01188             if (d->flow == TopToBottom && !d->flowPositions.isEmpty()) {
01189                 int value = verticalScrollBar()->value();
01190                 if (value >= d->flowPositions.count()) {
01191                     qWarning("QListView: Vertical scrollbar is out of bounds");
01192                     return 0;
01193                 }
01194                 return d->flowPositions.at(value);
01195             }
01196         }
01197     }
01198     return verticalScrollBar()->value();
01199 }
01200 
01204 QModelIndex QListView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
01205 {
01206     Q_D(QListView);
01207     Q_UNUSED(modifiers);
01208 
01209     QModelIndex current = currentIndex();
01210     if (!current.isValid()) {
01211         int rowCount = d->model->rowCount(d->root);
01212         if (!rowCount)
01213             return QModelIndex();
01214         int row = 0;
01215         while (row < rowCount && isRowHidden(row))
01216             ++row;
01217         if (row >= rowCount)
01218             return QModelIndex();
01219         return d->model->index(row, 0, d->root);
01220     }
01221 
01222     QRect rect = rectForIndex(current);
01223     if (rect.isEmpty()) {
01224         return d->model->index(0, 0, d->root);
01225     }
01226     if (d->gridSize.isValid()) rect.setSize(d->gridSize);
01227 
01228     QSize contents = d->contentsSize;
01229     QPoint pos = rect.center();
01230     d->intersectVector.clear();
01231 
01232     switch (cursorAction) {
01233     case MoveLeft:
01234         while (d->intersectVector.isEmpty()) {
01235             rect.translate(-rect.width(), 0);
01236             if (rect.right() <= 0)
01237                 return current;
01238             if (rect.left() < 0)
01239                 rect.setLeft(0);
01240             d->intersectingSet(rect);
01241             // don't get current in this set
01242             int idx = d->intersectVector.indexOf(current);
01243             if (idx > -1)
01244                 d->intersectVector.remove(idx);
01245         }
01246         return d->closestIndex(pos, d->intersectVector);
01247     case MoveRight:
01248         while (d->intersectVector.isEmpty()) {
01249             rect.translate(rect.width(), 0);
01250             if (rect.left() >= contents.width())
01251                 return current;
01252             if (rect.right() > contents.width())
01253                 rect.setRight(contents.width());
01254             d->intersectingSet(rect);
01255             // don't get current in this set
01256             int idx = d->intersectVector.indexOf(current);
01257             if (idx > -1)
01258                 d->intersectVector.remove(idx);
01259         }
01260         return d->closestIndex(pos, d->intersectVector);
01261     case MovePageUp:
01262         rect.moveTop(rect.top() - d->viewport->height());
01263         if (rect.top() < rect.height())
01264             rect.moveTop(rect.height());
01265     case MovePrevious:
01266     case MoveUp:
01267         while (d->intersectVector.isEmpty()) {
01268             rect.translate(0, -rect.height());
01269             if (rect.bottom() <= 0) {
01270 #ifdef QT_KEYPAD_NAVIGATION
01271                 if (QApplication::keypadNavigationEnabled())
01272                     return d->model->index(d->batchStartRow - 1, d->column, d->root);
01273 #endif
01274                 return current;
01275             }
01276             if (rect.top() < 0)
01277                 rect.setTop(0);
01278             d->intersectingSet(rect);
01279             // don't get current in this set
01280             int idx = d->intersectVector.indexOf(current);
01281             if (idx > -1)
01282                 d->intersectVector.remove(idx);
01283         }
01284         return d->closestIndex(pos, d->intersectVector);
01285     case MovePageDown:
01286         rect.moveTop(rect.top() + d->viewport->height());
01287         if (rect.bottom() > contents.height() - rect.height())
01288             rect.moveBottom(contents.height() - rect.height());
01289     case MoveNext:
01290     case MoveDown:
01291         while (d->intersectVector.isEmpty()) {
01292             rect.translate(0, rect.height());
01293             if (rect.top() >= contents.height()) {
01294 #ifdef QT_KEYPAD_NAVIGATION
01295                 if (QApplication::keypadNavigationEnabled())
01296                     return d->model->index(0, d->column, d->root);
01297 #endif
01298                 return current;
01299             }
01300             if (rect.bottom() > contents.height())
01301                 rect.setBottom(contents.height());
01302             d->intersectingSet(rect);
01303             // don't get current in this set
01304             int idx = d->intersectVector.indexOf(current);
01305             if (idx > -1)
01306                 d->intersectVector.remove(idx);
01307         }
01308         return d->closestIndex(pos, d->intersectVector);
01309     case MoveHome:
01310         return d->model->index(0, d->column, d->root);
01311     case MoveEnd:
01312         return d->model->index(d->batchStartRow - 1, d->column, d->root);
01313     }
01314 
01315     return current;
01316 }
01317 
01324 QRect QListView::rectForIndex(const QModelIndex &index) const
01325 {
01326     Q_D(const QListView);
01327     if (!d->isIndexValid(index)
01328         || index.parent() != d->root
01329         || index.column() != d->column
01330         || isIndexHidden(index))
01331         return QRect();
01332     d->executePostedLayout();
01333     QListViewItem item = d->indexToListViewItem(index);
01334     return d->viewItemRect(item);
01335 }
01336 
01345 void QListView::setPositionForIndex(const QPoint &position, const QModelIndex &index)
01346 {
01347     Q_D(QListView);
01348     if (d->movement == Static
01349         || !d->isIndexValid(index)
01350         || index.parent() != d->root
01351         || index.column() != d->column)
01352         return;
01353     d->executePostedLayout();
01354     if (index.row() >= d->items.count())
01355         return;
01356     d->setDirtyRegion(visualRect(index)); // update old position
01357     d->moveItem(index.row(), position);
01358     d->setDirtyRegion(visualRect(index)); // update new position
01359 }
01360 
01364 void QListView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
01365 {
01366     Q_D(QListView);
01367     if (!d->selectionModel)
01368         return;
01369 
01370     d->intersectingSet(rect.translated(horizontalOffset(), verticalOffset()));
01371 
01372     QItemSelection selection;
01373     QModelIndex tl;
01374     QModelIndex br;
01375 
01376     if (rect.width() == 1 && rect.height() == 1 && !d->intersectVector.isEmpty()) {
01377         tl = br = d->intersectVector.last(); // special case for mouse press; only select the top item
01378     } else {
01379         if (state() == DragSelectingState) { // visual selection mode (rubberband selection)
01380             QVector<QModelIndex>::iterator it = d->intersectVector.begin();
01381             for (; it != d->intersectVector.end(); ++it) {
01382                 if (!tl.isValid() && !br.isValid()) {
01383                     tl = br = *it;
01384                 } else if ((*it).row() == (tl.row() - 1)) {
01385                     tl = *it; // expand current range
01386                 } else if ((*it).row() == (br.row() + 1)) {
01387                     br = (*it); // expand current range
01388                 } else {
01389                     selection.select(tl, br); // select current range
01390                     tl = br = *it; // start new range
01391                 }
01392             }
01393         } else { // logical selection mode (key and mouse click selection)
01394             QVector<QModelIndex>::iterator it = d->intersectVector.begin();
01395             for (; it != d->intersectVector.end(); ++it) {
01396                 if (!tl.isValid() && !br.isValid())
01397                     tl = br = *it;
01398                 else if ((*it).row() < tl.row())
01399                     tl = (*it);
01400                 else if ((*it).row() > br.row())
01401                     br = (*it);
01402             }
01403         }
01404     }
01405 
01406     if (tl.isValid() && br.isValid())
01407         selection.select(tl, br);
01408     d->selectionModel->select(selection, command);
01409 }
01410 
01414 QRegion QListView::visualRegionForSelection(const QItemSelection &selection) const
01415 {
01416     Q_D(const QListView);
01417     // ### NOTE: this is a potential bottleneck in non-static mode
01418     int c = d->column;
01419     QRegion selectionRegion;
01420     for (int i = 0; i < selection.count(); ++i) {
01421         if (!selection.at(i).isValid())
01422             continue;
01423         QModelIndex parent = selection.at(i).topLeft().parent();
01424         int t = selection.at(i).topLeft().row();
01425         int b = selection.at(i).bottomRight().row();
01426         if (d->movement != Static || d->wrap) { // in non-static mode, we have to go through all selected items
01427             for (int r = t; r <= b; ++r)
01428                 selectionRegion += QRegion(visualRect(d->model->index(r, c, parent)));
01429         } else { // in static mode, we can optimize a bit
01430             QRect rect(visualRect(d->model->index(t, c, parent)).topLeft(),
01431                        visualRect(d->model->index(b, c, parent)).bottomRight());
01432             selectionRegion += QRegion(rect);
01433         }
01434     }
01435 
01436     return selectionRegion;
01437 }
01438 
01442 QModelIndexList QListView::selectedIndexes() const
01443 {
01444     Q_D(const QListView);
01445     QModelIndexList viewSelected;
01446     QModelIndexList modelSelected;
01447     if (d->selectionModel)
01448         modelSelected = d->selectionModel->selectedIndexes();
01449     for (int i = 0; i < modelSelected.count(); ++i) {
01450         QModelIndex index = modelSelected.at(i);
01451         if (!isIndexHidden(index) && index.parent() == d->root && index.column() == d->column)
01452             viewSelected.append(index);
01453     }
01454     return viewSelected;
01455 }
01456 
01462 void QListView::doItemsLayout()
01463 {
01464     Q_D(QListView);
01465     d->layoutChildren(); // make sure the viewport has the right size
01466     d->prepareItemsLayout();
01467     if (d->model->columnCount(d->root) > 0) { // no columns means no contents
01468         if (layoutMode() == SinglePass)
01469             d->doItemsLayout(d->model->rowCount(d->root)); // layout everything
01470         else if (!d->batchLayoutTimer.isActive())
01471             d->batchLayoutTimer.start(0, this); // do a new batch as fast as possible
01472     }
01473     QAbstractItemView::doItemsLayout();
01474 }
01475 
01479 void QListView::updateGeometries()
01480 {
01481     Q_D(QListView);
01482     if (d->model->rowCount(d->root) <= 0 || d->model->columnCount(d->root) <= 0) {
01483         horizontalScrollBar()->setRange(0, 0);
01484         verticalScrollBar()->setRange(0, 0);
01485     } else {
01486         QModelIndex index = d->model->index(0, d->column, d->root);
01487         QStyleOptionViewItemV2 option = d->viewOptionsV2();
01488         QSize step = d->itemSize(option, index);
01489 
01490         QSize csize = d->contentsSize;
01491         QSize vsize = d->viewport->size();
01492         QSize max = maximumViewportSize();
01493         if (max.width() >= d->contentsSize.width() && max.height() >= d->contentsSize.height())
01494             vsize = max;
01495 
01496         // ### reorder the logic
01497 
01498         const bool vertical = verticalScrollMode() == QAbstractItemView::ScrollPerItem;
01499         const bool horizontal = horizontalScrollMode() == QAbstractItemView::ScrollPerItem;
01500 
01501         if (d->flow == TopToBottom) {
01502             if (horizontal && d->wrap && d->movement == Static) {
01503                 int steps = d->segmentPositions.count();
01504                 int pageSteps = d->perItemScrollingPageSteps(vsize.width(), csize.width());
01505                 horizontalScrollBar()->setSingleStep(1);
01506                 horizontalScrollBar()->setPageStep(pageSteps);
01507                 horizontalScrollBar()->setRange(0, steps - pageSteps);
01508             } else {
01509                 horizontalScrollBar()->setSingleStep(step.width() + d->spacing);
01510                 horizontalScrollBar()->setPageStep(vsize.width());
01511                 horizontalScrollBar()->setRange(0, d->contentsSize.width() - vsize.width());
01512             }
01513             if (vertical && !d->wrap && d->movement == Static) {
01514                 int steps = d->flowPositions.count();
01515                 int pageSteps = d->perItemScrollingPageSteps(vsize.height(), csize.height());
01516                 verticalScrollBar()->setSingleStep(1);
01517                 verticalScrollBar()->setPageStep(pageSteps);
01518                 verticalScrollBar()->setRange(0, steps - pageSteps);
01519     //            } else if (vertical && d->wrap && d->movement == Static) {
01520                 // ### wrapped scrolling in flow direction
01521             } else {
01522                 verticalScrollBar()->setSingleStep(step.height() + d->spacing);
01523                 verticalScrollBar()->setPageStep(vsize.height());
01524                 verticalScrollBar()->setRange(0, d->contentsSize.height() - vsize.height());
01525             }
01526         } else { // LeftToRight
01527             if (horizontal && !d->wrap && d->movement == Static) {
01528                 int steps = d->flowPositions.count();
01529                 int pageSteps = d->perItemScrollingPageSteps(vsize.width(), csize.width());
01530                 horizontalScrollBar()->setSingleStep(1);
01531                 horizontalScrollBar()->setPageStep(pageSteps);
01532                 horizontalScrollBar()->setRange(0, steps - pageSteps);
01533 //            } else if (horizontal && d->wrap && d->movement == Static) {
01534                 // ### wrapped scrolling in flow direction
01535             } else {
01536                 horizontalScrollBar()->setSingleStep(step.width() + d->spacing);
01537                 horizontalScrollBar()->setPageStep(vsize.width());
01538                 horizontalScrollBar()->setRange(0, d->contentsSize.width() - vsize.width());
01539             }
01540             if (vertical && d->wrap && d->movement == Static) {
01541                 int steps = d->segmentPositions.count();
01542                 int pageSteps = d->perItemScrollingPageSteps(vsize.height(), csize.height());
01543                 verticalScrollBar()->setSingleStep(1);
01544                 verticalScrollBar()->setPageStep(pageSteps);
01545                 verticalScrollBar()->setRange(0, steps - pageSteps);
01546             } else {
01547                 verticalScrollBar()->setSingleStep(step.height() + d->spacing);
01548                 verticalScrollBar()->setPageStep(vsize.height());
01549                 verticalScrollBar()->setRange(0, d->contentsSize.height() - vsize.height());
01550             }
01551         }
01552     }
01553     // if the scrollbars are turned off, we resize the contents to the viewport
01554     if (d->movement == Static && !d->wrap) {
01555         if (d->flow == TopToBottom) {
01556             if (horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff)
01557                 d->contentsSize = QSize(viewport()->width(), contentsSize().height());
01558         } else { // LeftToRight
01559             if (verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff)
01560                 d->contentsSize = QSize(contentsSize().width(), viewport()->height());
01561         }
01562     }
01563 
01564     QAbstractItemView::updateGeometries();
01565 }
01566 
01570 bool QListView::isIndexHidden(const QModelIndex &index) const
01571 {
01572     Q_D(const QListView);
01573     return (d->hiddenRows.contains(index.row())
01574             && (index.parent() == d->root)
01575             && index.column() == d->column);
01576 }
01577 
01582 void QListView::setModelColumn(int column)
01583 {
01584     Q_D(QListView);
01585     if (column < 0 || column >= d->model->columnCount(d->root))
01586         return;
01587     d->column = column;
01588     d->doDelayedItemsLayout();
01589 }
01590 
01591 int QListView::modelColumn() const
01592 {
01593     Q_D(const QListView);
01594     return d->column;
01595 }
01596 
01606 void QListView::setUniformItemSizes(bool enable)
01607 {
01608     Q_D(QListView);
01609     d->uniformItemSizes = enable;
01610 }
01611 
01612 bool QListView::uniformItemSizes() const
01613 {
01614     Q_D(const QListView);
01615     return d->uniformItemSizes;
01616 }
01617 
01627 void QListView::setWordWrap(bool on)
01628 {
01629     Q_D(QListView);
01630     if (d->wrapItemText == on)
01631         return;
01632     d->wrapItemText = on;
01633     d->doDelayedItemsLayout();
01634 }
01635 
01636 bool QListView::wordWrap() const
01637 {
01638     Q_D(const QListView);
01639     return d->wrapItemText;
01640 }
01641 
01645 bool QListView::event(QEvent *e)
01646 {
01647     return QAbstractItemView::event(e);
01648 }
01649 
01650 /*
01651  * private object implementation
01652  */
01653 
01654 QListViewPrivate::QListViewPrivate()
01655     : QAbstractItemViewPrivate(),
01656       layoutMode(QListView::SinglePass),
01657       modeProperties(0),
01658       batchStartRow(0),
01659       batchSavedDeltaSeg(0),
01660       batchSavedPosition(0),
01661       column(0),
01662       uniformItemSizes(false),
01663       batchSize(100),
01664       wrapItemText(false)
01665 {}
01666 
01667 void QListViewPrivate::clear()
01668 {
01669     // initialization of data structs
01670     batchStartRow = 0;
01671     batchSavedPosition = 0;
01672     batchSavedDeltaSeg = 0;
01673     cachedItemSize = QSize();
01674     tree.destroy();
01675     items.clear();
01676     moved.clear();
01677     flowPositions.clear();
01678     segmentPositions.clear();
01679     segmentStartRows.clear();
01680 }
01681 
01682 void QListViewPrivate::prepareItemsLayout()
01683 {
01684     Q_Q(QListView);
01685     clear();
01686     layoutBounds = QRect(QPoint(0,0), q->maximumViewportSize());
01687 
01688     int verticalMargin = q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, q->verticalScrollBar());
01689     int horizontalMargin = q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, q->horizontalScrollBar());
01690     layoutBounds.adjust(0, 0, -verticalMargin, -horizontalMargin);
01691 
01692     int rowCount = model->rowCount(root);
01693     int colCount = model->columnCount(root);
01694     if (colCount <= 0)
01695         rowCount = 0; // no contents
01696     if (movement == QListView::Static) {
01697         flowPositions.resize(rowCount);
01698     } else {
01699         tree.create(qMax(rowCount - hiddenRows.count(), 0));
01700     }
01701 }
01702 
01703 QPoint QListViewPrivate::initStaticLayout(const QRect &bounds, int spacing, int first)
01704 {
01705     int x, y;
01706     if (first == 0) {
01707         flowPositions.clear();
01708         segmentPositions.clear();
01709         segmentStartRows.clear();
01710         x = bounds.left() + spacing;
01711         y = bounds.top() + spacing;
01712         segmentPositions.append(flow == QListView::LeftToRight ? y : x);
01713         segmentStartRows.append(0);
01714     } else if (wrap) {
01715         if (flow == QListView::LeftToRight) {
01716             x = batchSavedPosition;
01717             y = segmentPositions.last();
01718         } else { // flow == QListView::TopToBottom
01719             x = segmentPositions.last();
01720             y = batchSavedPosition;
01721         }
01722     } else { // not first and not wrap
01723         if (flow == QListView::LeftToRight) {
01724             x = batchSavedPosition;
01725             y = bounds.top() + spacing;
01726         } else { // flow == QListView::TopToBottom
01727             x = bounds.left() + spacing;
01728             y = batchSavedPosition;
01729         }
01730     }
01731     return QPoint(x, y);
01732 }
01733 
01734 QPoint QListViewPrivate::initDynamicLayout(const QRect &bounds, int spacing, int first)
01735 {
01736     int x, y;
01737     if (first == 0) {
01738         x = bounds.x() + spacing;
01739         y = bounds.y() + spacing;
01740         items.reserve(model->rowCount(root) - hiddenRows.count());
01741     } else {
01742         const QListViewItem item = items.at(first - 1);
01743         x = item.x;
01744         y = item.y;
01745         if (flow == QListView::LeftToRight)
01746             x += (gridSize.isValid() ? gridSize.width() : item.w) + spacing;
01747         else
01748             y += (gridSize.isValid() ? gridSize.height() : item.h) + spacing;
01749     }
01750     return QPoint(x, y);
01751 }
01752 
01753 void QListViewPrivate::initBspTree(const QSize &contents)
01754 {
01755     // remove all items from the tree
01756     int leafCount = tree.leafCount();
01757     for (int l = 0; l < leafCount; ++l)
01758         tree.leaf(l).clear();
01759     // we have to get the bounding rect of the items before we can initialize the tree
01760     QBspTree::Node::Type type = QBspTree::Node::Both; // 2D
01761     // simple heuristics to get better bsp
01762     if (contents.height() / contents.width() >= 3)
01763         type = QBspTree::Node::HorizontalPlane;
01764     else if (contents.width() / contents.height() >= 3)
01765         type = QBspTree::Node::VerticalPlane;
01766     // build tree for the bounding rect (not just the contents rect)
01767     tree.init(QRect(0, 0, contents.width(), contents.height()), type);
01768 }
01769 
01773 bool QListViewPrivate::doItemsLayout(int delta)
01774 {
01775     int max = model->rowCount(root) - 1;
01776     int first = batchStartRow;
01777     int last = qMin(first + delta - 1, max);
01778 
01779     if (max < 0)
01780         return true; // nothing to do
01781 
01782     if (movement == QListView::Static) {
01783         doStaticLayout(layoutBounds, first, last);
01784     } else {
01785         if (last >= items.count())
01786             createItems(last + 1);
01787         doDynamicLayout(layoutBounds, first, last);
01788     }
01789 
01790     if (batchStartRow >= max) { // stop items layout
01791         flowPositions.resize(flowPositions.count());
01792         segmentPositions.resize(segmentPositions.count());
01793         segmentStartRows.resize(segmentStartRows.count());
01794         return true; // done
01795     }
01796     return false; // not done
01797 }
01798 
01802 void QListViewPrivate::doItemsLayout(const QRect &bounds,
01803                                      const QModelIndex &first,
01804                                      const QModelIndex &last)
01805 {
01806     if (first.row() >= last.row() || !first.isValid() || !last.isValid())
01807         return;
01808     if (movement == QListView::Static)
01809         doStaticLayout(bounds, first.row(), last.row());
01810     else
01811         doDynamicLayout(bounds, first.row(), last.row());
01812 }
01813 
01817 void QListViewPrivate::doStaticLayout(const QRect &bounds, int first, int last)
01818 {
01819     const bool useItemSize = !gridSize.isValid();
01820     const int gap = useItemSize ? spacing : 0; // if we are using a grid ,we don't use spacing
01821     const QPoint topLeft = initStaticLayout(bounds, gap, first);
01822     const QStyleOptionViewItemV2 option = viewOptionsV2();
01823 
01824     // The static layout data structures are as follows:
01825     // One vector contains the coordinate in the direction of layout flow.
01826     // Another vector contains the coordinates of the segments.
01827     // A third vector contains the index (model row) of the first item
01828     // of each segment.
01829 
01830     int segStartPosition;
01831     int segEndPosition;
01832     int deltaFlowPosition;
01833     int deltaSegPosition;
01834     int deltaSegHint;
01835     int flowPosition;
01836     int segPosition;
01837 
01838     if (flow == QListView::LeftToRight) {
01839         segStartPosition = bounds.left();
01840         segEndPosition = bounds.width();
01841         flowPosition = topLeft.x();
01842         segPosition = topLeft.y();
01843         deltaFlowPosition = gridSize.width(); // dx
01844         deltaSegPosition = useItemSize ? batchSavedDeltaSeg : gridSize.height(); // dy
01845         deltaSegHint = gridSize.height();
01846     } else { // flow == QListView::TopToBottom
01847         segStartPosition = bounds.top();
01848         segEndPosition = bounds.height();
01849         flowPosition = topLeft.y();
01850         segPosition = topLeft.x();
01851         deltaFlowPosition = gridSize.height(); // dy
01852         deltaSegPosition = useItemSize ? batchSavedDeltaSeg : gridSize.width(); // dx
01853         deltaSegHint = gridSize.width();
01854     }
01855 
01856     for (int row = first; row <= last; ++row) {
01857         if (hiddenRows.contains(row)) {
01858             flowPositions.append(flowPosition);
01859         } else {
01860             // if we are not using a grid, we need to find the deltas
01861             if (useItemSize) {
01862                 QSize hint = itemSize(option, model->index(row, column, root));
01863                 if (flow == QListView::LeftToRight) {
01864                     deltaFlowPosition = hint.width() + gap;
01865                     deltaSegHint = hint.height() + gap;
01866                 } else { // TopToBottom
01867                     deltaFlowPosition = hint.height() + gap;
01868                     deltaSegHint = hint.width() + gap;
01869                 }
01870             }
01871             // create new segment
01872             if (wrap && (flowPosition + deltaFlowPosition >= segEndPosition)) {
01873                 flowPosition = gap + segStartPosition;
01874                 segPosition += deltaSegPosition;
01875                 segmentPositions.append(segPosition);
01876                 segmentStartRows.append(row);
01877                 deltaSegPosition = 0;
01878             }
01879             // save the flow position of this item
01880             flowPositions.append(flowPosition);
01881             // prepare for the next item
01882             deltaSegPosition = qMax(deltaSegHint, deltaSegPosition);
01883             flowPosition += gap + deltaFlowPosition;
01884         }
01885     }
01886     // used when laying out next batch
01887     batchSavedPosition = flowPosition;
01888     batchSavedDeltaSeg = deltaSegPosition;
01889     batchStartRow = last + 1;
01890     // set the contents size
01891     QRect rect = bounds;
01892     if (flow == QListView::LeftToRight) {
01893         rect.setRight(segmentPositions.count() == 1 ? flowPosition : bounds.right());
01894         rect.setBottom(segPosition + deltaSegPosition);
01895     } else { // TopToBottom
01896         rect.setRight(segPosition + deltaSegPosition);
01897         rect.setBottom(segmentPositions.count() == 1 ? flowPosition : bounds.bottom());
01898     }
01899     contentsSize = QSize(rect.right(), rect.bottom());
01900     // if the new items are visble, update the viewport
01901     QRect changedRect(topLeft, rect.bottomRight());
01902     if (clipRect().intersects(changedRect))
01903         viewport->update();
01904 }
01905 
01909 void QListViewPrivate::doDynamicLayout(const QRect &bounds, int first, int last)
01910 {
01911     const bool useItemSize = !gridSize.isValid();
01912     const int gap = useItemSize ? spacing : 0;
01913     const QPoint topLeft = initDynamicLayout(bounds, spacing, first);
01914 
01915     int segStartPosition;
01916     int segEndPosition;
01917     int deltaFlowPosition;
01918     int deltaSegPosition;
01919     int deltaSegHint;
01920     int flowPosition;
01921     int segPosition;
01922 
01923     if (flow == QListView::LeftToRight) {
01924         segStartPosition = bounds.left() + spacing;
01925         segEndPosition = bounds.right();
01926         deltaFlowPosition = gridSize.width(); // dx
01927         deltaSegPosition = (useItemSize ? batchSavedDeltaSeg : gridSize.height()); // dy
01928         deltaSegHint = gridSize.height();
01929         flowPosition = topLeft.x();
01930         segPosition = topLeft.y();
01931     } else { // flow == QListView::TopToBottom
01932         segStartPosition = bounds.top() + spacing;
01933         segEndPosition = bounds.bottom();
01934         deltaFlowPosition = gridSize.height(); // dy
01935         deltaSegPosition = (useItemSize ? batchSavedDeltaSeg : gridSize.width()); // dx
01936         deltaSegHint = gridSize.width();
01937         flowPosition = topLeft.y();
01938         segPosition = topLeft.x();
01939     }
01940 
01941     if (moved.count() != items.count())
01942         moved.resize(items.count());
01943 
01944     QRect rect(QPoint(0, 0), topLeft);
01945     QListViewItem *item = 0;
01946     for (int row = first; row <= last; ++row) {
01947         item = &items[row];
01948         if (hiddenRows.contains(row)) {
01949             item->invalidate();
01950         } else {
01951             // if we are not using a grid, we need to find the deltas
01952             if (useItemSize) {
01953                 if (flow == QListView::LeftToRight)
01954                     deltaFlowPosition = item->w + gap;
01955                 else
01956                     deltaFlowPosition = item->h + gap;
01957             } else {
01958                 item->w = qMin<int>(gridSize.width(), item->w);
01959                 item->h = qMin<int>(gridSize.height(), item->h);
01960             }
01961 
01962             // create new segment
01963             if (wrap
01964                 && flowPosition + deltaFlowPosition > segEndPosition
01965                 && flowPosition > segStartPosition) {
01966                 flowPosition = segStartPosition;
01967                 segPosition += deltaSegPosition;
01968                 if (useItemSize)
01969                     deltaSegPosition = 0;
01970             }
01971             // We must delay calculation of the seg adjustment, as this item
01972             // may have caused a wrap to occur
01973             if (useItemSize) {
01974                 if (flow == QListView::LeftToRight)
01975                     deltaSegHint = item->h + gap;
01976                 else
01977                     deltaSegHint = item->w + gap;
01978                 deltaSegPosition = qMax(deltaSegPosition, deltaSegHint);
01979             }
01980 
01981             // set the position of the item
01982             if (!moved.testBit(row)) {
01983                 if (flow == QListView::LeftToRight) {
01984                     if (useItemSize) {
01985                         item->x = flowPosition;
01986                         item->y = segPosition;
01987                     } else { // use grid
01988                         item->x = flowPosition + ((deltaFlowPosition - item->w) / 2);
01989                         item->y = segPosition +  ((deltaSegPosition - item->h) / 2);
01990                     }
01991                 } else { // TopToBottom
01992                     if (useItemSize) {
01993                         item->y = flowPosition;
01994                         item->x = segPosition;
01995                     } else {
01996                         item->y = flowPosition + ((deltaFlowPosition - item->h) / 2);
01997                         item->x = segPosition +  ((deltaSegPosition - item->w) / 2);
01998                     }
01999                 }
02000             }
02001 
02002             // let the contents contain the new item
02003             if (useItemSize)
02004                 rect |= item->rect();
02005             else if (flow == QListView::LeftToRight)
02006                 rect |= QRect(flowPosition, segPosition, deltaFlowPosition, deltaSegPosition);
02007             else // flow == TopToBottom
02008                 rect |= QRect(segPosition, flowPosition, deltaSegPosition, deltaFlowPosition);
02009 
02010             // prepare for next item
02011             flowPosition += deltaFlowPosition; // current position + item width + gap
02012         }
02013     }
02014     batchSavedDeltaSeg = deltaSegPosition;
02015     batchStartRow = last + 1;
02016     bool done = (last >= model->rowCount(root) - 1);
02017     // resize the content area
02018     if (done || !bounds.contains(item->rect()))
02019         contentsSize = QSize(rect.width(), rect.height());
02020     // resize tree
02021     int insertFrom = first;
02022     if (done || first == 0) {
02023         initBspTree(rect.size());
02024         insertFrom = 0;
02025     }
02026     // insert items in tree
02027     for (int row = insertFrom; row <= last; ++row)
02028         tree.insertLeaf(items.at(row).rect(), row);
02029     // if the new items are visble, update the viewport
02030     QRect changedRect(topLeft, rect.bottomRight());
02031     if (clipRect().intersects(changedRect))
02032         viewport->update();
02033 }
02034 
02041 void QListViewPrivate::intersectingStaticSet(const QRect &area) const
02042 {
02043     intersectVector.clear();
02044     int segStartPosition;
02045     int segEndPosition;
02046     int flowStartPosition;
02047     int flowEndPosition;
02048     if (flow == QListView::LeftToRight) {
02049         segStartPosition = area.top();
02050         segEndPosition = area.bottom();
02051         flowStartPosition = area.left();
02052         flowEndPosition = area.right();
02053     } else {
02054         segStartPosition = area.left();
02055         segEndPosition = area.right();
02056         flowStartPosition = area.top();
02057         flowEndPosition = area.bottom();
02058     }
02059     if (segmentPositions.isEmpty() || flowPositions.isEmpty())
02060         return;
02061     const int segLast = segmentPositions.count() - 1;
02062     Q_ASSERT(segLast > -1);
02063     int seg = qBinarySearch<int>(segmentPositions, segStartPosition, 0, segLast);
02064     for (; seg <= segLast && segmentPositions.at(seg) <= segEndPosition; ++seg) {
02065         int first = segmentStartRows.at(seg);
02066         int last = (seg < segLast ? segmentStartRows.at(seg + 1) : batchStartRow) - 1;
02067         int row = qBinarySearch<int>(flowPositions, flowStartPosition, first, last);
02068         for (; row <= last && flowPositions.at(row) <= flowEndPosition; ++row) {
02069             if (hiddenRows.contains(row))
02070                 continue;
02071             QModelIndex index = model->index(row, column, root);
02072             if (index.isValid())
02073                 intersectVector.append(index);
02074             else
02075                 qWarning("intersectingStaticSet: row %d was invalid", row);
02076         }
02077     }
02078 }
02079 
02080 void QListViewPrivate::intersectingDynamicSet(const QRect &area) const
02081 {
02082     intersectVector.clear();
02083     QListViewPrivate *that = const_cast<QListViewPrivate*>(this);
02084     QBspTree::Data data(static_cast<void*>(that));
02085     that->tree.climbTree(area, &QListViewPrivate::addLeaf, data);
02086 }
02087 
02088 void QListViewPrivate::createItems(int to)
02089 {
02090     int count = items.count();
02091     QSize size;
02092     QStyleOptionViewItemV2 option = viewOptionsV2();
02093     for (int row = count; row < to; ++row) {
02094         size = itemSize(option, model->index(row, column, root));
02095         QListViewItem item(QRect(0, 0, size.width(), size.height()), row); // default pos
02096         items.append(item);
02097     }
02098 }
02099 
02100 void QListViewPrivate::drawItems(QPainter *painter, const QVector<QModelIndex> &indexes) const
02101 {
02102     QStyleOptionViewItemV2 option = viewOptionsV2();
02103     option.state &= ~QStyle::State_MouseOver;
02104     QVector<QModelIndex>::const_iterator it = indexes.begin();
02105     QListViewItem item = indexToListViewItem(*it);
02106     for (; it != indexes.end(); ++it) {
02107         item = indexToListViewItem(*it);
02108         option.rect = viewItemRect(item);
02109         delegateForIndex(*it)->paint(painter, option, *it);
02110     }
02111 }
02112 
02113 QRect QListViewPrivate::itemsRect(const QVector<QModelIndex> &indexes) const
02114 {
02115     QVector<QModelIndex>::const_iterator it = indexes.begin();
02116     QListViewItem item = indexToListViewItem(*it);
02117     QRect rect(item.x, item.y, item.w, item.h);
02118     for (; it != indexes.end(); ++it) {
02119         item = indexToListViewItem(*it);
02120         rect |= viewItemRect(item);
02121     }
02122     return rect;
02123 }
02124 
02125 QListViewItem QListViewPrivate::indexToListViewItem(const QModelIndex &index) const
02126 {
02127     if (!index.isValid() || hiddenRows.contains(index.row()))
02128         return QListViewItem();
02129 
02130     if (movement != QListView::Static)
02131         if (index.row() < items.count())
02132             return items.at(index.row());
02133         else
02134             return QListViewItem();
02135 
02136     // movement == Static
02137     if (flowPositions.isEmpty()
02138         || segmentPositions.isEmpty()
02139         || index.row() >= flowPositions.count())
02140         return QListViewItem();
02141 
02142     const int segment = qBinarySearch<int>(segmentStartRows, index.row(),
02143                                            0, segmentStartRows.count() - 1);
02144 
02145 
02146     QSize size = (uniformItemSizes && cachedItemSize.isValid())
02147                  ? cachedItemSize : itemSize(viewOptionsV2(), index);
02148 
02149     QPoint pos;
02150     if (flow == QListView::LeftToRight) {
02151         pos.setX(flowPositions.at(index.row()));
02152         pos.setY(segmentPositions.at(segment));
02153     } else { // TopToBottom
02154         pos.setY(flowPositions.at(index.row()));
02155         pos.setX(segmentPositions.at(segment));
02156         if (wrap) { // make the items as wide as the segment
02157             int right = (segment + 1 >= segmentPositions.count()
02158                      ? contentsSize.width()
02159                      : segmentPositions.at(segment + 1));
02160             size.setWidth(right - pos.x());
02161         }
02162     }
02163 
02164     return QListViewItem(QRect(pos, size), index.row());
02165 }
02166 
02167 int QListViewPrivate::itemIndex(const QListViewItem &item) const
02168 {
02169     int i = item.indexHint;
02170     if (movement == QListView::Static || items.at(i) == item)
02171         return i;
02172     if (i >= items.count())
02173         i = items.count() - 1;
02174 
02175     int j = i;
02176     int c = items.count();
02177     bool a = true;
02178     bool b = true;
02179 
02180     while (a || b) {
02181         if (a) {
02182             if (items.at(i) == item) {
02183                 items.at(i).indexHint = i;
02184                 return i;
02185             }
02186             a = ++i < c;
02187         }
02188         if (b) {
02189             if (items.at(j) == item) {
02190                 items.at(j).indexHint = j;
02191                 return j;
02192             }
02193             b = --j > -1;
02194         }
02195     }
02196     return -1;
02197 }
02198 
02199 void QListViewPrivate::addLeaf(QVector<int> &leaf, const QRect &area,
02200                                uint visited, QBspTree::Data data)
02201 {
02202     QListViewItem *vi;
02203     QListViewPrivate *_this = static_cast<QListViewPrivate *>(data.ptr);
02204     for (int i = 0; i < leaf.count(); ++i) {
02205         int idx = leaf.at(i);
02206         if (idx < 0 || idx >= _this->items.count())
02207             continue;
02208         vi = &_this->items[idx];
02209         Q_ASSERT(vi);
02210         if (vi->rect().intersects(area) && vi->visited != visited) {
02211             QModelIndex index = _this->listViewItemToIndex(*vi);
02212             Q_ASSERT(index.isValid());
02213             _this->intersectVector.append(index);
02214             vi->visited = visited;
02215         }
02216     }
02217 }
02218 
02219 void QListViewPrivate::insertItem(int index)
02220 {
02221     if (index >= 0 && index < items.count())
02222         tree.insertLeaf(items.at(index).rect(), index);
02223 }
02224 
02225 void QListViewPrivate::removeItem(int index)
02226 {
02227     if (index >= 0 && index < items.count())
02228         tree.removeLeaf(items.at(index).rect(), index);
02229 }
02230 
02231 void QListViewPrivate::moveItem(int index, const QPoint &dest)
02232 {
02233     // does not impact on the bintree itself or the contents rect
02234     QListViewItem *item = &items[index];
02235     QRect rect = item->rect();
02236 
02237     // move the item without removing it from the tree
02238     tree.removeLeaf(rect, index);
02239     item->move(dest);
02240     tree.insertLeaf(QRect(dest, rect.size()), index);
02241 
02242     // resize the contents area
02243     int w = rect.x() + rect.width();
02244     int h = rect.y() + rect.height();
02245     w = w > contentsSize.width() ? w : contentsSize.width();
02246     h = h > contentsSize.height() ? h : contentsSize.height();
02247     contentsSize = QSize(w, h);
02248 
02249     if (moved.count() != items.count())
02250         moved.resize(items.count());
02251     moved.setBit(index, true);
02252 }
02253 
02254 QPoint QListViewPrivate::snapToGrid(const QPoint &pos) const
02255 {
02256     int x = pos.x() - (pos.x() % gridSize.width());
02257     int y = pos.y() - (pos.y() % gridSize.height());
02258     return QPoint(x, y);
02259 }
02260 
02261 QRect QListViewPrivate::mapToViewport(const QRect &rect) const
02262 {
02263     Q_Q(const QListView);
02264     if (!rect.isValid())
02265         return rect;
02266     QRect result = rect;
02267 
02268     // If the listview is in "listbox-mode", the items are as wide as the view.
02269     if (!wrap && movement == QListView::Static) {
02270         QSize vsize = viewport->size();
02271         QSize csize = (q->horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff ? vsize : contentsSize);
02272   if (flow == QListView::TopToBottom) {
02273       if (q_func()->isRightToLeft()) // Adjust the rect by expanding the left edge
02274           result.setLeft(result.right() - qMax(csize.width(), vsize.width()));
02275       else // Adjust the rect by expanding the right edge
02276           result.setWidth(qMax(csize.width(), vsize.width()));
02277   } else { // LeftToRight
02278       result.setHeight(qMax(csize.height(), vsize.height()));
02279   }
02280     }
02281 
02282     int dx = -q->horizontalOffset();
02283     int dy = -q->verticalOffset();
02284     result.adjust(dx, dy, dx, dy);
02285     return result;
02286 }
02287 
02288 QPoint QListViewPrivate::draggedItemsDelta() const
02289 {
02290     if (movement == QListView::Snap) {
02291         QPoint snapdelta = QPoint((offset().x() % gridSize.width()),
02292                                   (offset().y() % gridSize.height()));
02293         return snapToGrid(draggedItemsPos + snapdelta) - snapToGrid(pressedPosition) - snapdelta;
02294     }
02295     return draggedItemsPos - pressedPosition;
02296 }
02297 
02298 QRect QListViewPrivate::draggedItemsRect() const
02299 {
02300     QRect rect = itemsRect(draggedItems);
02301     rect.translate(draggedItemsDelta());
02302     return rect;
02303 }
02304 
02305 QModelIndex QListViewPrivate::closestIndex(const QPoint &target,
02306                                            const QVector<QModelIndex> &candidates) const
02307 {
02308     int distance = 0;
02309     int shortest = -1;
02310     QModelIndex closest;
02311     QVector<QModelIndex>::const_iterator it = candidates.begin();
02312     for (; it != candidates.end(); ++it) {
02313         if (!(*it).isValid())
02314             continue;
02315         distance = (indexToListViewItem(*it).rect().center() - target).manhattanLength();
02316         if (distance < shortest || shortest == -1) {
02317             shortest = distance;
02318             closest = *it;
02319         }
02320     }
02321     return closest;
02322 }
02323 
02324 QSize QListViewPrivate::itemSize(const QStyleOptionViewItem &option, const QModelIndex &index) const
02325 {
02326     if (!uniformItemSizes) {
02327         const QAbstractItemDelegate *delegate = delegateForIndex(index);
02328         return delegate ? delegate->sizeHint(option, index) : QSize();
02329     }
02330     if (!cachedItemSize.isValid()) { // the last item is probaly the largest, so we use its size
02331         int row = model->rowCount(root) - 1;
02332         QModelIndex sample = model->index(row, column, root);
02333         const QAbstractItemDelegate *delegate = delegateForIndex(sample);
02334         cachedItemSize = delegate ? delegate->sizeHint(option, sample) : QSize();
02335     }
02336     return cachedItemSize;
02337 }
02338 
02339 int QListViewPrivate::perItemScrollingPageSteps(int length, int bounds) const
02340 {
02341     const QVector<int> positions = (wrap ? segmentPositions : flowPositions);
02342     if (positions.isEmpty() || bounds <= length)
02343         return positions.count();
02344     if (uniformItemSizes) {
02345         for (int i = 1; i < positions.count(); ++i)
02346             if (positions.at(i) > 0)
02347                 return length / positions.at(i);
02348         return 0; // all items had height 0
02349     }
02350     int pageSteps = 0;
02351     int steps = positions.count() - 1;
02352     int max = qMax(length, bounds);
02353     int min = qMin(length, bounds);
02354     int pos = min - (max - positions.last());
02355     while (pos >= 0 && steps > 0) {
02356         pos -= (positions.at(steps) - positions.at(steps - 1));
02357         ++pageSteps;
02358         --steps;
02359     }
02360     // at this point we know that positions has at least one entry
02361     return qMax(pageSteps, 1);
02362 }
02363 
02364 int QListViewPrivate::perItemScrollToValue(int index, int scrollValue, int viewportSize,
02365                                            QAbstractItemView::ScrollHint hint,
02366                                            Qt::Orientation orientation) const
02367 {
02368     if (index < 0)
02369         return scrollValue;
02370     if (!wrap) {
02371         const int flowCount = flowPositions.count() - 2;
02372         const int topIndex = scrollValue;
02373         const int topCoordinate = flowPositions.at(topIndex);
02374         int bottomIndex = topIndex;
02375         int bottomCoordinate = topCoordinate;
02376         while ((bottomCoordinate - topCoordinate) <= (viewportSize)
02377                && bottomIndex <= flowCount)
02378             bottomCoordinate = flowPositions.at(++bottomIndex);
02379         const int itemCount = bottomIndex - topIndex - 1;
02380         switch (hint) {
02381         case QAbstractItemView::PositionAtTop:
02382             return index;
02383         case QAbstractItemView::PositionAtBottom:
02384             return index - itemCount + 1; // ###
02385         case QAbstractItemView::PositionAtCenter:
02386             return index - (itemCount / 2);
02387         default:
02388             break;
02389         }
02390     } else { // wrapping
02391         Qt::Orientation flowOrientation =
02392             (flow == QListView::LeftToRight ? Qt::Horizontal : Qt::Vertical);
02393         if (flowOrientation == orientation) { // scrolling in the "flow" direction
02394             // ### wrapped scrolling in the flow direction
02395             return flowPositions.at(index); // ### always pixel based for now
02396         } else if (!segmentStartRows.isEmpty()) { // we are scrolling in the "segment" direction
02397             int segment = qBinarySearch<int>(segmentStartRows, index,
02398                                              0, segmentStartRows.count() - 1);
02399             const int segmentPositionCount = segmentPositions.count() - 2;
02400             const int leftSegment = segment;
02401             const int leftCoordinate = segmentPositions.at(leftSegment);
02402             int rightSegment = leftSegment;
02403             int rightCoordinate = leftCoordinate;
02404             while ((rightCoordinate - leftCoordinate) < (viewportSize - 1)
02405                    && rightSegment < segmentPositionCount)
02406                 rightCoordinate = segmentPositions.at(++rightSegment);
02407             const int segmentCount = rightSegment - leftSegment - 1;
02408             switch (hint) {
02409             case QAbstractItemView::PositionAtTop:
02410                 return segment;
02411             case QAbstractItemView::PositionAtBottom:
02412                 return segment - segmentCount + 1; // ###
02413             case QAbstractItemView::PositionAtCenter:
02414                 return segment - (segmentCount / 2);
02415             default:
02416                 break;
02417             }
02418         }
02419     }
02420     return scrollValue;
02421 }
02422 
02423 QStyleOptionViewItemV2 QListViewPrivate::viewOptionsV2() const
02424 {
02425     Q_Q(const QListView);
02426     QStyleOptionViewItemV2 option = q->viewOptions();
02427     if (wrapItemText)
02428         option.features = QStyleOptionViewItemV2::WrapText;
02429     return option;
02430 }
02431 
02432 #endif // QT_NO_LISTVIEW

Generated on Thu Mar 15 11:54:50 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1