src/declarative/graphicsitems/qdeclarativepathview.cpp
changeset 30 5dc02b23752f
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
       
     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 QtDeclarative 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 "private/qdeclarativepathview_p.h"
       
    43 #include "private/qdeclarativepathview_p_p.h"
       
    44 
       
    45 #include <qdeclarativestate_p.h>
       
    46 #include <qdeclarativeopenmetaobject_p.h>
       
    47 #include <QDebug>
       
    48 #include <QEvent>
       
    49 #include <qlistmodelinterface_p.h>
       
    50 #include <QGraphicsSceneEvent>
       
    51 
       
    52 #include <qmath.h>
       
    53 #include <math.h>
       
    54 
       
    55 QT_BEGIN_NAMESPACE
       
    56 
       
    57 inline qreal qmlMod(qreal x, qreal y)
       
    58 {
       
    59 #ifdef QT_USE_MATH_H_FLOATS
       
    60     if(sizeof(qreal) == sizeof(float))
       
    61         return fmodf(float(x), float(y));
       
    62     else
       
    63 #endif
       
    64         return fmod(x, y);
       
    65 }
       
    66 
       
    67 static QDeclarativeOpenMetaObjectType *qPathViewAttachedType = 0;
       
    68 
       
    69 QDeclarativePathViewAttached::QDeclarativePathViewAttached(QObject *parent)
       
    70 : QObject(parent), m_view(0), m_onPath(false), m_isCurrent(false)
       
    71 {
       
    72     if (qPathViewAttachedType) {
       
    73         m_metaobject = new QDeclarativeOpenMetaObject(this, qPathViewAttachedType);
       
    74         m_metaobject->setCached(true);
       
    75     } else {
       
    76         m_metaobject = new QDeclarativeOpenMetaObject(this);
       
    77     }
       
    78 }
       
    79 
       
    80 QDeclarativePathViewAttached::~QDeclarativePathViewAttached()
       
    81 {
       
    82 }
       
    83 
       
    84 QVariant QDeclarativePathViewAttached::value(const QByteArray &name) const
       
    85 {
       
    86     return m_metaobject->value(name);
       
    87 }
       
    88 void QDeclarativePathViewAttached::setValue(const QByteArray &name, const QVariant &val)
       
    89 {
       
    90     m_metaobject->setValue(name, val);
       
    91 }
       
    92 
       
    93 QDeclarativeItem *QDeclarativePathViewPrivate::getItem(int modelIndex)
       
    94 {
       
    95     Q_Q(QDeclarativePathView);
       
    96     requestedIndex = modelIndex;
       
    97     QDeclarativeItem *item = model->item(modelIndex, false);
       
    98     if (item) {
       
    99         if (!attType) {
       
   100             // pre-create one metatype to share with all attached objects
       
   101             attType = new QDeclarativeOpenMetaObjectType(&QDeclarativePathViewAttached::staticMetaObject, qmlEngine(q));
       
   102             foreach(const QString &attr, path->attributes())
       
   103                 attType->createProperty(attr.toUtf8());
       
   104         }
       
   105         qPathViewAttachedType = attType;
       
   106         QDeclarativePathViewAttached *att = static_cast<QDeclarativePathViewAttached *>(qmlAttachedPropertiesObject<QDeclarativePathView>(item));
       
   107         qPathViewAttachedType = 0;
       
   108         if (att) {
       
   109             att->m_view = q;
       
   110             att->setOnPath(true);
       
   111         }
       
   112         item->setParentItem(q);
       
   113     }
       
   114     requestedIndex = -1;
       
   115     return item;
       
   116 }
       
   117 
       
   118 void QDeclarativePathViewPrivate::releaseItem(QDeclarativeItem *item)
       
   119 {
       
   120     if (!item || !model)
       
   121         return;
       
   122     if (QDeclarativePathViewAttached *att = attached(item))
       
   123         att->setOnPath(false);
       
   124     model->release(item);
       
   125 }
       
   126 
       
   127 QDeclarativePathViewAttached *QDeclarativePathViewPrivate::attached(QDeclarativeItem *item)
       
   128 {
       
   129     return static_cast<QDeclarativePathViewAttached *>(qmlAttachedPropertiesObject<QDeclarativePathView>(item, false));
       
   130 }
       
   131 
       
   132 void QDeclarativePathViewPrivate::clear()
       
   133 {
       
   134     for (int i=0; i<items.count(); i++){
       
   135         QDeclarativeItem *p = items[i];
       
   136         releaseItem(p);
       
   137     }
       
   138     items.clear();
       
   139 }
       
   140 
       
   141 void QDeclarativePathViewPrivate::updateMappedRange()
       
   142 {
       
   143     if (model && pathItems != -1 && pathItems < model->count())
       
   144         mappedRange = qreal(pathItems)/model->count();
       
   145     else
       
   146         mappedRange = 1.0;
       
   147 }
       
   148 
       
   149 qreal QDeclarativePathViewPrivate::positionOfIndex(qreal index) const
       
   150 {
       
   151     qreal pos = -1.0;
       
   152 
       
   153     if (model && index >= 0 && index < model->count()) {
       
   154         qreal start = 0.0;
       
   155         if (haveHighlightRange && highlightRangeMode != QDeclarativePathView::NoHighlightRange)
       
   156             start = highlightRangeStart;
       
   157         qreal globalPos = index + offset;
       
   158         globalPos = qmlMod(globalPos, qreal(model->count())) / model->count();
       
   159         if (pathItems != -1 && pathItems < model->count()) {
       
   160             globalPos += start * mappedRange;
       
   161             globalPos = qmlMod(globalPos, 1.0);
       
   162             if (globalPos < mappedRange)
       
   163                 pos = globalPos / mappedRange;
       
   164         } else {
       
   165             pos = qmlMod(globalPos + start, 1.0);
       
   166         }
       
   167     }
       
   168 
       
   169     return pos;
       
   170 }
       
   171 
       
   172 void QDeclarativePathViewPrivate::createHighlight()
       
   173 {
       
   174     Q_Q(QDeclarativePathView);
       
   175     if (!q->isComponentComplete())
       
   176         return;
       
   177 
       
   178     bool changed = false;
       
   179     if (highlightItem) {
       
   180         delete highlightItem;
       
   181         highlightItem = 0;
       
   182         changed = true;
       
   183     }
       
   184 
       
   185     QDeclarativeItem *item = 0;
       
   186     if (highlightComponent) {
       
   187         QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
       
   188         QObject *nobj = highlightComponent->create(highlightContext);
       
   189         if (nobj) {
       
   190             QDeclarative_setParent_noEvent(highlightContext, nobj);
       
   191             item = qobject_cast<QDeclarativeItem *>(nobj);
       
   192             if (!item)
       
   193                 delete nobj;
       
   194         } else {
       
   195             delete highlightContext;
       
   196         }
       
   197     } else {
       
   198         item = new QDeclarativeItem;
       
   199     }
       
   200     if (item) {
       
   201         QDeclarative_setParent_noEvent(item, q);
       
   202         item->setParentItem(q);
       
   203         highlightItem = item;
       
   204         changed = true;
       
   205     }
       
   206     if (changed)
       
   207         emit q->highlightItemChanged();
       
   208 }
       
   209 
       
   210 void QDeclarativePathViewPrivate::updateHighlight()
       
   211 {
       
   212     Q_Q(QDeclarativePathView);
       
   213     if (!q->isComponentComplete() || !isValid())
       
   214         return;
       
   215     if (highlightItem) {
       
   216         if (haveHighlightRange && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) {
       
   217             updateItem(highlightItem, highlightRangeStart);
       
   218         } else {
       
   219             qreal target = currentIndex;
       
   220 
       
   221             tl.reset(moveHighlight);
       
   222             moveHighlight.setValue(highlightPosition);
       
   223 
       
   224             const int duration = highlightMoveDuration;
       
   225 
       
   226             if (target - highlightPosition > model->count()/2) {
       
   227                 highlightUp = false;
       
   228                 qreal distance = model->count() - target + highlightPosition;
       
   229                 tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance));
       
   230                 tl.set(moveHighlight, model->count()-0.01);
       
   231                 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (model->count()-target) / distance));
       
   232             } else if (target - highlightPosition <= -model->count()/2) {
       
   233                 highlightUp = true;
       
   234                 qreal distance = model->count() - highlightPosition + target;
       
   235                 tl.move(moveHighlight, model->count()-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (model->count()-highlightPosition) / distance));
       
   236                 tl.set(moveHighlight, 0.0);
       
   237                 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance));
       
   238             } else {
       
   239                 highlightUp = highlightPosition - target < 0;
       
   240                 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration);
       
   241             }
       
   242         }
       
   243     }
       
   244 }
       
   245 
       
   246 void QDeclarativePathViewPrivate::setHighlightPosition(qreal pos)
       
   247 {
       
   248     if (pos != highlightPosition) {
       
   249         qreal start = 0.0;
       
   250         qreal end = 1.0;
       
   251         if (haveHighlightRange && highlightRangeMode != QDeclarativePathView::NoHighlightRange) {
       
   252             start = highlightRangeStart;
       
   253             end = highlightRangeEnd;
       
   254         }
       
   255 
       
   256         qreal range = qreal(model->count());
       
   257         // calc normalized position of highlight relative to offset
       
   258         qreal relativeHighlight = qmlMod(pos + offset, range) / range;
       
   259 
       
   260         if (!highlightUp && relativeHighlight > end * mappedRange) {
       
   261             qreal diff = 1.0 - relativeHighlight;
       
   262             setOffset(offset + diff * range);
       
   263         } else if (highlightUp && relativeHighlight >= (end - start) * mappedRange) {
       
   264             qreal diff = relativeHighlight - (end - start) * mappedRange;
       
   265             setOffset(offset - diff * range - 0.00001);
       
   266         }
       
   267 
       
   268         highlightPosition = pos;
       
   269         qreal pathPos = positionOfIndex(pos);
       
   270         updateItem(highlightItem, pathPos);
       
   271         if (QDeclarativePathViewAttached *att = attached(highlightItem))
       
   272             att->setOnPath(pathPos != -1.0);
       
   273     }
       
   274 }
       
   275 
       
   276 void QDeclarativePathViewPrivate::updateItem(QDeclarativeItem *item, qreal percent)
       
   277 {
       
   278     if (QDeclarativePathViewAttached *att = attached(item)) {
       
   279         foreach(const QString &attr, path->attributes())
       
   280             att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
       
   281     }
       
   282     QPointF pf = path->pointAt(percent);
       
   283     item->setX(qRound(pf.x() - item->width()*item->scale()/2));
       
   284     item->setY(qRound(pf.y() - item->height()*item->scale()/2));
       
   285 }
       
   286 
       
   287 void QDeclarativePathViewPrivate::regenerate()
       
   288 {
       
   289     Q_Q(QDeclarativePathView);
       
   290     if (!q->isComponentComplete())
       
   291         return;
       
   292 
       
   293     clear();
       
   294 
       
   295     if (!isValid())
       
   296         return;
       
   297 
       
   298     firstIndex = -1;
       
   299     updateMappedRange();
       
   300     q->refill();
       
   301 }
       
   302 
       
   303 /*!
       
   304     \qmlclass PathView QDeclarativePathView
       
   305     \since 4.7
       
   306     \brief The PathView element lays out model-provided items on a path.
       
   307     \inherits Item
       
   308 
       
   309     The model is typically provided by a QAbstractListModel "C++ model object", but can also be created directly in QML.
       
   310 
       
   311     The \l delegate is instantiated for each item on the \l path.
       
   312     The items may be flicked to move them along the path.
       
   313 
       
   314     \snippet doc/src/snippets/declarative/pathview/pathview.qml 0
       
   315 
       
   316     \image pathview.gif
       
   317 
       
   318     Delegates are instantiated as needed and may be destroyed at any time.
       
   319     State should \e never be stored in a delegate.
       
   320 
       
   321     \bold Note that views do not enable \e clip automatically.  If the view
       
   322     is not clipped by another item or the screen, it will be necessary
       
   323     to set \e {clip: true} in order to have the out of view items clipped
       
   324     nicely.
       
   325 
       
   326     \sa Path
       
   327 */
       
   328 
       
   329 QDeclarativePathView::QDeclarativePathView(QDeclarativeItem *parent)
       
   330   : QDeclarativeItem(*(new QDeclarativePathViewPrivate), parent)
       
   331 {
       
   332     Q_D(QDeclarativePathView);
       
   333     d->init();
       
   334 }
       
   335 
       
   336 QDeclarativePathView::~QDeclarativePathView()
       
   337 {
       
   338     Q_D(QDeclarativePathView);
       
   339     d->clear();
       
   340     if (d->attType)
       
   341         d->attType->release();
       
   342     if (d->ownModel)
       
   343         delete d->model;
       
   344 }
       
   345 
       
   346 /*!
       
   347     \qmlattachedproperty bool PathView::onPath
       
   348     This attached property holds whether the item is currently on the path.
       
   349 
       
   350     If a pathItemCount has been set, it is possible that some items may
       
   351     be instantiated, but not considered to be currently on the path.
       
   352     Usually, these items would be set invisible, for example:
       
   353 
       
   354     \code
       
   355     Component {
       
   356         Rectangle {
       
   357             visible: PathView.onPath
       
   358             ...
       
   359         }
       
   360     }
       
   361     \endcode
       
   362 
       
   363     It is attached to each instance of the delegate.
       
   364 */
       
   365 
       
   366 /*!
       
   367     \qmlattachedproperty bool PathView::isCurrentItem
       
   368     This attached property is true if this delegate is the current item; otherwise false.
       
   369 
       
   370     It is attached to each instance of the delegate.
       
   371 
       
   372     This property may be used to adjust the appearance of the current item.
       
   373 */
       
   374 
       
   375 /*!
       
   376     \qmlproperty model PathView::model
       
   377     This property holds the model providing data for the view.
       
   378 
       
   379     The model provides a set of data that is used to create the items for the view.
       
   380     For large or dynamic datasets the model is usually provided by a C++ model object.
       
   381     Models can also be created directly in XML, using the ListModel element.
       
   382 
       
   383     \sa {qmlmodels}{Data Models}
       
   384 */
       
   385 QVariant QDeclarativePathView::model() const
       
   386 {
       
   387     Q_D(const QDeclarativePathView);
       
   388     return d->modelVariant;
       
   389 }
       
   390 
       
   391 void QDeclarativePathView::setModel(const QVariant &model)
       
   392 {
       
   393     Q_D(QDeclarativePathView);
       
   394     if (d->modelVariant == model)
       
   395         return;
       
   396 
       
   397     if (d->model) {
       
   398         disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
       
   399         disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
       
   400         disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
       
   401         disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
       
   402         disconnect(d->model, SIGNAL(createdItem(int, QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
       
   403         for (int i=0; i<d->items.count(); i++){
       
   404             QDeclarativeItem *p = d->items[i];
       
   405             d->model->release(p);
       
   406         }
       
   407         d->items.clear();
       
   408     }
       
   409 
       
   410     d->modelVariant = model;
       
   411     QObject *object = qvariant_cast<QObject*>(model);
       
   412     QDeclarativeVisualModel *vim = 0;
       
   413     if (object && (vim = qobject_cast<QDeclarativeVisualModel *>(object))) {
       
   414         if (d->ownModel) {
       
   415             delete d->model;
       
   416             d->ownModel = false;
       
   417         }
       
   418         d->model = vim;
       
   419     } else {
       
   420         if (!d->ownModel) {
       
   421             d->model = new QDeclarativeVisualDataModel(qmlContext(this), this);
       
   422             d->ownModel = true;
       
   423         }
       
   424         if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
       
   425             dataModel->setModel(model);
       
   426     }
       
   427     if (d->model) {
       
   428         connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
       
   429         connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
       
   430         connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
       
   431         connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
       
   432         connect(d->model, SIGNAL(createdItem(int, QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
       
   433     }
       
   434     d->offset = qmlMod(d->offset, qreal(d->model->count()));
       
   435     if (d->offset < 0)
       
   436         d->offset = d->model->count() + d->offset;
       
   437     d->regenerate();
       
   438     d->fixOffset();
       
   439     emit countChanged();
       
   440     emit modelChanged();
       
   441 }
       
   442 
       
   443 /*!
       
   444     \qmlproperty int PathView::count
       
   445     This property holds the number of items in the model.
       
   446 */
       
   447 int QDeclarativePathView::count() const
       
   448 {
       
   449     Q_D(const QDeclarativePathView);
       
   450     return d->model ? d->model->count() : 0;
       
   451 }
       
   452 
       
   453 /*!
       
   454     \qmlproperty Path PathView::path
       
   455     \default
       
   456     This property holds the path used to lay out the items.
       
   457     For more information see the \l Path documentation.
       
   458 */
       
   459 QDeclarativePath *QDeclarativePathView::path() const
       
   460 {
       
   461     Q_D(const QDeclarativePathView);
       
   462     return d->path;
       
   463 }
       
   464 
       
   465 void QDeclarativePathView::setPath(QDeclarativePath *path)
       
   466 {
       
   467     Q_D(QDeclarativePathView);
       
   468     if (d->path == path)
       
   469         return;
       
   470     if (d->path)
       
   471         disconnect(d->path, SIGNAL(changed()), this, SLOT(refill()));
       
   472     d->path = path;
       
   473     connect(d->path, SIGNAL(changed()), this, SLOT(refill()));
       
   474     if (d->isValid() && isComponentComplete()) {
       
   475         d->clear();
       
   476         if (d->attType) {
       
   477             d->attType->release();
       
   478             d->attType = 0;
       
   479         }
       
   480         d->regenerate();
       
   481     }
       
   482     emit pathChanged();
       
   483 }
       
   484 
       
   485 /*!
       
   486     \qmlproperty int PathView::currentIndex
       
   487     This property holds the index of the current item.
       
   488 */
       
   489 int QDeclarativePathView::currentIndex() const
       
   490 {
       
   491     Q_D(const QDeclarativePathView);
       
   492     return d->currentIndex;
       
   493 }
       
   494 
       
   495 void QDeclarativePathView::setCurrentIndex(int idx)
       
   496 {
       
   497     Q_D(QDeclarativePathView);
       
   498     if (d->model && d->model->count())
       
   499         idx = qAbs(idx % d->model->count());
       
   500     if (d->model && idx != d->currentIndex) {
       
   501         if (d->model->count()) {
       
   502             int itemIndex = (d->currentIndex - d->firstIndex + d->model->count()) % d->model->count();
       
   503             if (itemIndex < d->items.count()) {
       
   504                 if (QDeclarativeItem *item = d->items.at(itemIndex)) {
       
   505                     if (QDeclarativePathViewAttached *att = d->attached(item))
       
   506                         att->setIsCurrentItem(false);
       
   507                 }
       
   508             }
       
   509         }
       
   510         d->currentItem = 0;
       
   511         d->moveReason = QDeclarativePathViewPrivate::SetIndex;
       
   512         d->currentIndex = idx;
       
   513         if (d->model->count()) {
       
   514             if (d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange)
       
   515                 d->snapToCurrent();
       
   516             int itemIndex = (idx - d->firstIndex + d->model->count()) % d->model->count();
       
   517             if (itemIndex < d->items.count()) {
       
   518                 d->currentItem = d->items.at(itemIndex);
       
   519                 d->currentItem->setFocus(true);
       
   520                 if (QDeclarativePathViewAttached *att = d->attached(d->currentItem))
       
   521                     att->setIsCurrentItem(true);
       
   522             }
       
   523             d->currentItemOffset = d->positionOfIndex(d->currentIndex);
       
   524             d->updateHighlight();
       
   525         }
       
   526         emit currentIndexChanged();
       
   527     }
       
   528 }
       
   529 
       
   530 /*!
       
   531     \qmlmethod PathView::incrementCurrentIndex()
       
   532 
       
   533     Increments the current index.
       
   534 */
       
   535 void QDeclarativePathView::incrementCurrentIndex()
       
   536 {
       
   537     setCurrentIndex(currentIndex()+1);
       
   538 }
       
   539 
       
   540 
       
   541 /*!
       
   542     \qmlmethod PathView::decrementCurrentIndex()
       
   543 
       
   544     Decrements the current index.
       
   545 */
       
   546 void QDeclarativePathView::decrementCurrentIndex()
       
   547 {
       
   548     Q_D(QDeclarativePathView);
       
   549     if (d->model && d->model->count()) {
       
   550         int idx = currentIndex()-1;
       
   551         if (idx < 0)
       
   552             idx = d->model->count() - 1;
       
   553         setCurrentIndex(idx);
       
   554     }
       
   555 }
       
   556 
       
   557 /*!
       
   558     \qmlproperty real PathView::offset
       
   559 
       
   560     The offset specifies how far along the path the items are from their initial positions.
       
   561     This is a real number that ranges from 0.0 to the count of items in the model.
       
   562 */
       
   563 qreal QDeclarativePathView::offset() const
       
   564 {
       
   565     Q_D(const QDeclarativePathView);
       
   566     return d->offset;
       
   567 }
       
   568 
       
   569 void QDeclarativePathView::setOffset(qreal offset)
       
   570 {
       
   571     Q_D(QDeclarativePathView);
       
   572     d->setOffset(offset);
       
   573     d->updateCurrent();
       
   574 }
       
   575 
       
   576 void QDeclarativePathViewPrivate::setOffset(qreal o)
       
   577 {
       
   578     Q_Q(QDeclarativePathView);
       
   579     if (offset != o) {
       
   580         if (isValid() && q->isComponentComplete()) {
       
   581             offset = qmlMod(o, qreal(model->count()));
       
   582             if (offset < 0)
       
   583                 offset += qreal(model->count());
       
   584             q->refill();
       
   585         } else {
       
   586             offset = o;
       
   587         }
       
   588         emit q->offsetChanged();
       
   589     }
       
   590 }
       
   591 
       
   592 /*!
       
   593     \qmlproperty Component PathView::highlight
       
   594     This property holds the component to use as the highlight.
       
   595 
       
   596     An instance of the highlight component will be created for each view.
       
   597     The geometry of the resultant component instance will be managed by the view
       
   598     so as to stay with the current item.
       
   599 
       
   600     The below example demonstrates how to make a simple highlight.  Note the use
       
   601     of the PathView.onPath property to ensure that the highlight is hidden
       
   602     when flicked off of the path.
       
   603 
       
   604     \code
       
   605     Component {
       
   606         Rectangle {
       
   607             visible: PathView.onPath
       
   608             ...
       
   609         }
       
   610     }
       
   611     \endcode
       
   612 
       
   613     \sa highlightItem, highlightRangeMode
       
   614 */
       
   615 
       
   616 QDeclarativeComponent *QDeclarativePathView::highlight() const
       
   617 {
       
   618     Q_D(const QDeclarativePathView);
       
   619     return d->highlightComponent;
       
   620 }
       
   621 
       
   622 void QDeclarativePathView::setHighlight(QDeclarativeComponent *highlight)
       
   623 {
       
   624     Q_D(QDeclarativePathView);
       
   625     if (highlight != d->highlightComponent) {
       
   626         d->highlightComponent = highlight;
       
   627         d->createHighlight();
       
   628         d->updateHighlight();
       
   629         emit highlightChanged();
       
   630     }
       
   631 }
       
   632 
       
   633 /*!
       
   634   \qmlproperty Item PathView::highlightItem
       
   635 
       
   636   \c highlightItem holds the highlight item, which was created
       
   637   from the \l highlight component.
       
   638 
       
   639   \sa highlight
       
   640 */
       
   641 QDeclarativeItem *QDeclarativePathView::highlightItem()
       
   642 {
       
   643     Q_D(const QDeclarativePathView);
       
   644     return d->highlightItem;
       
   645 }
       
   646 /*!
       
   647     \qmlproperty real PathView::preferredHighlightBegin
       
   648     \qmlproperty real PathView::preferredHighlightEnd
       
   649     \qmlproperty enumeration PathView::highlightRangeMode
       
   650 
       
   651     These properties set the preferred range of the highlight (current item)
       
   652     within the view.  The preferred values must be in the range 0.0-1.0.
       
   653 
       
   654     If highlightRangeMode is set to \e PathView.ApplyRange the view will
       
   655     attempt to maintain the highlight within the range, however
       
   656     the highlight can move outside of the range at the ends of the path
       
   657     or due to a mouse interaction.
       
   658 
       
   659     If highlightRangeMode is set to \e PathView.StrictlyEnforceRange the highlight will never
       
   660     move outside of the range.  This means that the current item will change
       
   661     if a keyboard or mouse action would cause the highlight to move
       
   662     outside of the range.
       
   663 
       
   664     Note that this is the correct way to influence where the
       
   665     current item ends up when the view moves. For example, if you want the
       
   666     currently selected item to be in the middle of the path, then set the
       
   667     highlight range to be 0.5,0.5 and highlightRangeMode to PathView.StrictlyEnforceRange.
       
   668     Then, when the path scrolls,
       
   669     the currently selected item will be the item at that position. This also applies to
       
   670     when the currently selected item changes - it will scroll to within the preferred
       
   671     highlight range. Furthermore, the behaviour of the current item index will occur
       
   672     whether or not a highlight exists.
       
   673 
       
   674     The default value is \e PathView.StrictlyEnforceRange.
       
   675 
       
   676     Note that a valid range requires preferredHighlightEnd to be greater
       
   677     than or equal to preferredHighlightBegin.
       
   678 */
       
   679 qreal QDeclarativePathView::preferredHighlightBegin() const
       
   680 {
       
   681     Q_D(const QDeclarativePathView);
       
   682     return d->highlightRangeStart;
       
   683 }
       
   684 
       
   685 void QDeclarativePathView::setPreferredHighlightBegin(qreal start)
       
   686 {
       
   687     Q_D(QDeclarativePathView);
       
   688     if (d->highlightRangeStart == start || start < 0 || start > 1.0)
       
   689         return;
       
   690     d->highlightRangeStart = start;
       
   691     d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
       
   692     emit preferredHighlightBeginChanged();
       
   693 }
       
   694 
       
   695 qreal QDeclarativePathView::preferredHighlightEnd() const
       
   696 {
       
   697     Q_D(const QDeclarativePathView);
       
   698     return d->highlightRangeEnd;
       
   699 }
       
   700 
       
   701 void QDeclarativePathView::setPreferredHighlightEnd(qreal end)
       
   702 {
       
   703     Q_D(QDeclarativePathView);
       
   704     if (d->highlightRangeEnd == end || end < 0 || end > 1.0)
       
   705         return;
       
   706     d->highlightRangeEnd = end;
       
   707     d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
       
   708     emit preferredHighlightEndChanged();
       
   709 }
       
   710 
       
   711 QDeclarativePathView::HighlightRangeMode QDeclarativePathView::highlightRangeMode() const
       
   712 {
       
   713     Q_D(const QDeclarativePathView);
       
   714     return d->highlightRangeMode;
       
   715 }
       
   716 
       
   717 void QDeclarativePathView::setHighlightRangeMode(HighlightRangeMode mode)
       
   718 {
       
   719     Q_D(QDeclarativePathView);
       
   720     if (d->highlightRangeMode == mode)
       
   721         return;
       
   722     d->highlightRangeMode = mode;
       
   723     d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
       
   724     emit highlightRangeModeChanged();
       
   725 }
       
   726 
       
   727 
       
   728 /*!
       
   729     \qmlproperty int PathView::highlightMoveDuration
       
   730     This property holds the move animation duration of the highlight delegate.
       
   731 
       
   732     If the highlightRangeMode is StrictlyEnforceRange then this property
       
   733     determines the speed that the items move along the path.
       
   734 
       
   735     The default value for the duration is 300ms.
       
   736 */
       
   737 int QDeclarativePathView::highlightMoveDuration() const
       
   738 {
       
   739     Q_D(const QDeclarativePathView);
       
   740     return d->highlightMoveDuration;
       
   741 }
       
   742 
       
   743 void QDeclarativePathView::setHighlightMoveDuration(int duration)
       
   744 {
       
   745     Q_D(QDeclarativePathView);
       
   746     if (d->highlightMoveDuration == duration)
       
   747         return;
       
   748     d->highlightMoveDuration = duration;
       
   749     emit highlightMoveDurationChanged();
       
   750 }
       
   751 
       
   752 /*!
       
   753     \qmlproperty real PathView::dragMargin
       
   754     This property holds the maximum distance from the path that initiate mouse dragging.
       
   755 
       
   756     By default the path can only be dragged by clicking on an item.  If
       
   757     dragMargin is greater than zero, a drag can be initiated by clicking
       
   758     within dragMargin pixels of the path.
       
   759 */
       
   760 qreal QDeclarativePathView::dragMargin() const
       
   761 {
       
   762     Q_D(const QDeclarativePathView);
       
   763     return d->dragMargin;
       
   764 }
       
   765 
       
   766 void QDeclarativePathView::setDragMargin(qreal dragMargin)
       
   767 {
       
   768     Q_D(QDeclarativePathView);
       
   769     if (d->dragMargin == dragMargin)
       
   770         return;
       
   771     d->dragMargin = dragMargin;
       
   772     emit dragMarginChanged();
       
   773 }
       
   774 
       
   775 /*!
       
   776     \qmlproperty real PathView::flickDeceleration
       
   777     This property holds the rate at which a flick will decelerate.
       
   778 
       
   779     The default is 100.
       
   780 */
       
   781 qreal QDeclarativePathView::flickDeceleration() const
       
   782 {
       
   783     Q_D(const QDeclarativePathView);
       
   784     return d->deceleration;
       
   785 }
       
   786 
       
   787 void QDeclarativePathView::setFlickDeceleration(qreal dec)
       
   788 {
       
   789     Q_D(QDeclarativePathView);
       
   790     if (d->deceleration == dec)
       
   791         return;
       
   792     d->deceleration = dec;
       
   793     emit flickDecelerationChanged();
       
   794 }
       
   795 
       
   796 /*!
       
   797     \qmlproperty bool PathView::interactive
       
   798 
       
   799     A user cannot drag or flick a PathView that is not interactive.
       
   800 
       
   801     This property is useful for temporarily disabling flicking. This allows
       
   802     special interaction with PathView's children.
       
   803 */
       
   804 bool QDeclarativePathView::isInteractive() const
       
   805 {
       
   806     Q_D(const QDeclarativePathView);
       
   807     return d->interactive;
       
   808 }
       
   809 
       
   810 void QDeclarativePathView::setInteractive(bool interactive)
       
   811 {
       
   812     Q_D(QDeclarativePathView);
       
   813     if (interactive != d->interactive) {
       
   814         d->interactive = interactive;
       
   815         if (!interactive)
       
   816             d->tl.clear();
       
   817         emit interactiveChanged();
       
   818     }
       
   819 }
       
   820 
       
   821 /*!
       
   822     \qmlproperty Component PathView::delegate
       
   823 
       
   824     The delegate provides a template defining each item instantiated by the view.
       
   825     The index is exposed as an accessible \c index property.  Properties of the
       
   826     model are also available depending upon the type of \l {qmlmodels}{Data Model}.
       
   827 
       
   828     The number of elements in the delegate has a direct effect on the
       
   829     flicking performance of the view when pathItemCount is specified.  If at all possible, place functionality
       
   830     that is not needed for the normal display of the delegate in a \l Loader which
       
   831     can load additional elements when needed.
       
   832 
       
   833     Note that the PathView will layout the items based on the size of the root
       
   834     item in the delegate.
       
   835 
       
   836     Here is an example delegate:
       
   837     \snippet doc/src/snippets/declarative/pathview/pathview.qml 1
       
   838 */
       
   839 QDeclarativeComponent *QDeclarativePathView::delegate() const
       
   840 {
       
   841     Q_D(const QDeclarativePathView);
       
   842      if (d->model) {
       
   843         if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
       
   844             return dataModel->delegate();
       
   845     }
       
   846 
       
   847     return 0;
       
   848 }
       
   849 
       
   850 void QDeclarativePathView::setDelegate(QDeclarativeComponent *delegate)
       
   851 {
       
   852     Q_D(QDeclarativePathView);
       
   853     if (delegate == this->delegate())
       
   854         return;
       
   855     if (!d->ownModel) {
       
   856         d->model = new QDeclarativeVisualDataModel(qmlContext(this));
       
   857         d->ownModel = true;
       
   858     }
       
   859     if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) {
       
   860         dataModel->setDelegate(delegate);
       
   861         d->regenerate();
       
   862         emit delegateChanged();
       
   863     }
       
   864 }
       
   865 
       
   866 /*!
       
   867   \qmlproperty int PathView::pathItemCount
       
   868   This property holds the number of items visible on the path at any one time.
       
   869 */
       
   870 int QDeclarativePathView::pathItemCount() const
       
   871 {
       
   872     Q_D(const QDeclarativePathView);
       
   873     return d->pathItems;
       
   874 }
       
   875 
       
   876 void QDeclarativePathView::setPathItemCount(int i)
       
   877 {
       
   878     Q_D(QDeclarativePathView);
       
   879     if (i == d->pathItems)
       
   880         return;
       
   881     if (i < 1)
       
   882         i = 1;
       
   883     d->pathItems = i;
       
   884     if (d->isValid() && isComponentComplete()) {
       
   885         d->regenerate();
       
   886     }
       
   887     emit pathItemCountChanged();
       
   888 }
       
   889 
       
   890 QPointF QDeclarativePathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
       
   891 {
       
   892     //XXX maybe do recursively at increasing resolution.
       
   893     qreal mindist = 1e10; // big number
       
   894     QPointF nearPoint = path->pointAt(0);
       
   895     qreal nearPc = 0;
       
   896     for (qreal i=1; i < 1000; i++) {
       
   897         QPointF pt = path->pointAt(i/1000.0);
       
   898         QPointF diff = pt - point;
       
   899         qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
       
   900         if (dist < mindist) {
       
   901             nearPoint = pt;
       
   902             nearPc = i;
       
   903             mindist = dist;
       
   904         }
       
   905     }
       
   906 
       
   907     if (nearPercent)
       
   908         *nearPercent = nearPc / 1000.0;
       
   909 
       
   910     return nearPoint;
       
   911 }
       
   912 
       
   913 
       
   914 void QDeclarativePathView::mousePressEvent(QGraphicsSceneMouseEvent *event)
       
   915 {
       
   916     Q_D(QDeclarativePathView);
       
   917     if (!d->interactive || !d->items.count())
       
   918         return;
       
   919     QPointF scenePoint = mapToScene(event->pos());
       
   920     int idx = 0;
       
   921     for (; idx < d->items.count(); ++idx) {
       
   922         QRectF rect = d->items.at(idx)->boundingRect();
       
   923         rect = d->items.at(idx)->mapToScene(rect).boundingRect();
       
   924         if (rect.contains(scenePoint))
       
   925             break;
       
   926     }
       
   927     if (idx == d->items.count() && d->dragMargin == 0.)  // didn't click on an item
       
   928         return;
       
   929 
       
   930     d->startPoint = d->pointNear(event->pos(), &d->startPc);
       
   931     if (idx == d->items.count()) {
       
   932         qreal distance = qAbs(event->pos().x() - d->startPoint.x()) + qAbs(event->pos().y() - d->startPoint.y());
       
   933         if (distance > d->dragMargin)
       
   934             return;
       
   935     }
       
   936 
       
   937     d->stealMouse = false;
       
   938     d->lastElapsed = 0;
       
   939     d->lastDist = 0;
       
   940     QDeclarativeItemPrivate::start(d->lastPosTime);
       
   941     d->tl.clear();
       
   942 }
       
   943 
       
   944 void QDeclarativePathView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
       
   945 {
       
   946     Q_D(QDeclarativePathView);
       
   947     if (!d->interactive || d->lastPosTime.isNull())
       
   948         return;
       
   949 
       
   950     if (!d->stealMouse) {
       
   951         QPointF delta = event->pos() - d->startPoint;
       
   952         if (qAbs(delta.x()) > QApplication::startDragDistance() && qAbs(delta.y()) > QApplication::startDragDistance())
       
   953             d->stealMouse = true;
       
   954     }
       
   955 
       
   956     if (d->stealMouse) {
       
   957         d->moveReason = QDeclarativePathViewPrivate::Mouse;
       
   958         qreal newPc;
       
   959         d->pointNear(event->pos(), &newPc);
       
   960         qreal diff = (newPc - d->startPc)*d->model->count()*d->mappedRange;
       
   961         if (diff) {
       
   962             setOffset(d->offset + diff);
       
   963 
       
   964             if (diff > d->model->count()/2)
       
   965                 diff -= d->model->count();
       
   966             else if (diff < -d->model->count()/2)
       
   967                 diff += d->model->count();
       
   968 
       
   969             d->lastElapsed = QDeclarativeItemPrivate::restart(d->lastPosTime);
       
   970             d->lastDist = diff;
       
   971             d->startPc = newPc;
       
   972         }
       
   973     }
       
   974 }
       
   975 
       
   976 void QDeclarativePathView::mouseReleaseEvent(QGraphicsSceneMouseEvent *)
       
   977 {
       
   978     Q_D(QDeclarativePathView);
       
   979     d->stealMouse = false;
       
   980     setKeepMouseGrab(false);
       
   981     if (!d->interactive || d->lastPosTime.isNull())
       
   982         return;
       
   983 
       
   984     qreal elapsed = qreal(d->lastElapsed + QDeclarativeItemPrivate::elapsed(d->lastPosTime)) / 1000.;
       
   985     qreal velocity = elapsed > 0. ? d->lastDist / elapsed : 0;
       
   986     if (d->model && d->model->count() && qAbs(velocity) > 1.) {
       
   987         qreal count = d->pathItems == -1 ? d->model->count() : d->pathItems;
       
   988         if (qAbs(velocity) > count * 2) // limit velocity
       
   989             velocity = (velocity > 0 ? count : -count) * 2;
       
   990         // Calculate the distance to be travelled
       
   991         qreal v2 = velocity*velocity;
       
   992         qreal accel = d->deceleration/10;
       
   993         // + 0.25 to encourage moving at least one item in the flick direction
       
   994         qreal dist = qMin(qreal(d->model->count()-1), qreal(v2 / (accel * 2.0) + 0.25));
       
   995         if (d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) {
       
   996             // round to nearest item.
       
   997             if (velocity > 0.)
       
   998                 dist = qRound(dist + d->offset) - d->offset;
       
   999             else
       
  1000                 dist = qRound(dist - d->offset) + d->offset;
       
  1001             // Calculate accel required to stop on item boundary
       
  1002             if (dist <= 0.) {
       
  1003                 dist = 0.;
       
  1004                 accel = 0.;
       
  1005             } else {
       
  1006                 accel = v2 / (2.0f * qAbs(dist));
       
  1007             }
       
  1008         }
       
  1009         d->moveOffset.setValue(d->offset);
       
  1010         d->tl.accel(d->moveOffset, velocity, accel, dist);
       
  1011         d->tl.callback(QDeclarativeTimeLineCallback(&d->moveOffset, d->fixOffsetCallback, d));
       
  1012     } else {
       
  1013         d->fixOffset();
       
  1014     }
       
  1015 
       
  1016     d->lastPosTime = QTime();
       
  1017     ungrabMouse();
       
  1018 }
       
  1019 
       
  1020 bool QDeclarativePathView::sendMouseEvent(QGraphicsSceneMouseEvent *event)
       
  1021 {
       
  1022     Q_D(QDeclarativePathView);
       
  1023     QGraphicsSceneMouseEvent mouseEvent(event->type());
       
  1024     QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect();
       
  1025     QGraphicsScene *s = scene();
       
  1026     QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()) : 0;
       
  1027     if ((d->stealMouse || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
       
  1028         mouseEvent.setAccepted(false);
       
  1029         for (int i = 0x1; i <= 0x10; i <<= 1) {
       
  1030             if (event->buttons() & i) {
       
  1031                 Qt::MouseButton button = Qt::MouseButton(i);
       
  1032                 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
       
  1033             }
       
  1034         }
       
  1035         mouseEvent.setScenePos(event->scenePos());
       
  1036         mouseEvent.setLastScenePos(event->lastScenePos());
       
  1037         mouseEvent.setPos(mapFromScene(event->scenePos()));
       
  1038         mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
       
  1039 
       
  1040         switch(mouseEvent.type()) {
       
  1041         case QEvent::GraphicsSceneMouseMove:
       
  1042             mouseMoveEvent(&mouseEvent);
       
  1043             break;
       
  1044         case QEvent::GraphicsSceneMousePress:
       
  1045             mousePressEvent(&mouseEvent);
       
  1046             break;
       
  1047         case QEvent::GraphicsSceneMouseRelease:
       
  1048             mouseReleaseEvent(&mouseEvent);
       
  1049             break;
       
  1050         default:
       
  1051             break;
       
  1052         }
       
  1053         grabber = qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem());
       
  1054         if (grabber && d->stealMouse && !grabber->keepMouseGrab() && grabber != this)
       
  1055             grabMouse();
       
  1056 
       
  1057         return d->stealMouse;
       
  1058     } else if (!d->lastPosTime.isNull()) {
       
  1059         d->lastPosTime = QTime();
       
  1060     }
       
  1061     return false;
       
  1062 }
       
  1063 
       
  1064 bool QDeclarativePathView::sceneEventFilter(QGraphicsItem *i, QEvent *e)
       
  1065 {
       
  1066     Q_D(QDeclarativePathView);
       
  1067     if (!isVisible() || !d->interactive)
       
  1068         return QDeclarativeItem::sceneEventFilter(i, e);
       
  1069 
       
  1070     switch (e->type()) {
       
  1071     case QEvent::GraphicsSceneMousePress:
       
  1072     case QEvent::GraphicsSceneMouseMove:
       
  1073     case QEvent::GraphicsSceneMouseRelease:
       
  1074         {
       
  1075             bool ret = sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
       
  1076             if (e->type() == QEvent::GraphicsSceneMouseRelease)
       
  1077                 return ret;
       
  1078             break;
       
  1079         }
       
  1080     default:
       
  1081         break;
       
  1082     }
       
  1083 
       
  1084     return QDeclarativeItem::sceneEventFilter(i, e);
       
  1085 }
       
  1086 
       
  1087 void QDeclarativePathView::componentComplete()
       
  1088 {
       
  1089     Q_D(QDeclarativePathView);
       
  1090     QDeclarativeItem::componentComplete();
       
  1091     d->createHighlight();
       
  1092     // It is possible that a refill has already happended to to Path
       
  1093     // bindings being handled in the componentComplete().  If so
       
  1094     // don't do it again.
       
  1095     if (d->items.count() == 0)
       
  1096         d->regenerate();
       
  1097     d->updateHighlight();
       
  1098 }
       
  1099 
       
  1100 void QDeclarativePathView::refill()
       
  1101 {
       
  1102     Q_D(QDeclarativePathView);
       
  1103     if (!d->isValid() || !isComponentComplete())
       
  1104         return;
       
  1105 
       
  1106     bool currentVisible = false;
       
  1107 
       
  1108     // first move existing items and remove items off path
       
  1109     int idx = d->firstIndex;
       
  1110     QList<QDeclarativeItem*>::iterator it = d->items.begin();
       
  1111     while (it != d->items.end()) {
       
  1112         qreal pos = d->positionOfIndex(idx);
       
  1113         QDeclarativeItem *item = *it;
       
  1114         if (pos >= 0.0) {
       
  1115             d->updateItem(item, pos);
       
  1116             if (idx == d->currentIndex) {
       
  1117                 currentVisible = true;
       
  1118                 d->currentItemOffset = pos;
       
  1119             }
       
  1120             ++it;
       
  1121         } else {
       
  1122 //            qDebug() << "release";
       
  1123             d->updateItem(item, 1.0);
       
  1124             d->releaseItem(item);
       
  1125             if (it == d->items.begin()) {
       
  1126                 if (++d->firstIndex >= d->model->count())
       
  1127                     d->firstIndex = 0;
       
  1128             }
       
  1129             it = d->items.erase(it);
       
  1130         }
       
  1131         ++idx;
       
  1132         if (idx >= d->model->count())
       
  1133             idx = 0;
       
  1134     }
       
  1135 
       
  1136     // add items to beginning and end
       
  1137     int count = d->pathItems == -1 ? d->model->count() : qMin(d->pathItems, d->model->count());
       
  1138     if (d->items.count() < count) {
       
  1139         int idx = qRound(d->model->count() - d->offset) % d->model->count();
       
  1140         qreal startPos = 0.0;
       
  1141         if (d->haveHighlightRange && d->highlightRangeMode != QDeclarativePathView::NoHighlightRange)
       
  1142             startPos = d->highlightRangeStart;
       
  1143         if (d->firstIndex >= 0) {
       
  1144             startPos = d->positionOfIndex(d->firstIndex);
       
  1145             idx = (d->firstIndex + d->items.count()) % d->model->count();
       
  1146         }
       
  1147         qreal pos = d->positionOfIndex(idx);
       
  1148         while ((pos > startPos || !d->items.count()) && d->items.count() < count) {
       
  1149 //            qDebug() << "append" << idx;
       
  1150             QDeclarativeItem *item = d->getItem(idx);
       
  1151             if (d->model->completePending())
       
  1152                 item->setZValue(idx+1);
       
  1153             if (d->currentIndex == idx) {
       
  1154                 item->setFocus(true);
       
  1155                 if (QDeclarativePathViewAttached *att = d->attached(item))
       
  1156                     att->setIsCurrentItem(true);
       
  1157                 currentVisible = true;
       
  1158                 d->currentItemOffset = pos;
       
  1159                 d->currentItem = item;
       
  1160             }
       
  1161             if (d->items.count() == 0)
       
  1162                 d->firstIndex = idx;
       
  1163             d->items.append(item);
       
  1164             d->updateItem(item, pos);
       
  1165             if (d->model->completePending())
       
  1166                 d->model->completeItem();
       
  1167             ++idx;
       
  1168             if (idx >= d->model->count())
       
  1169                 idx = 0;
       
  1170             pos = d->positionOfIndex(idx);
       
  1171         }
       
  1172 
       
  1173         idx = d->firstIndex - 1;
       
  1174         if (idx < 0)
       
  1175             idx = d->model->count() - 1;
       
  1176         pos = d->positionOfIndex(idx);
       
  1177         while (pos >= 0.0 && pos < startPos) {
       
  1178 //            qDebug() << "prepend" << idx;
       
  1179             QDeclarativeItem *item = d->getItem(idx);
       
  1180             if (d->model->completePending())
       
  1181                 item->setZValue(idx+1);
       
  1182             if (d->currentIndex == idx) {
       
  1183                 item->setFocus(true);
       
  1184                 if (QDeclarativePathViewAttached *att = d->attached(item))
       
  1185                     att->setIsCurrentItem(true);
       
  1186                 currentVisible = true;
       
  1187                 d->currentItemOffset = pos;
       
  1188                 d->currentItem = item;
       
  1189             }
       
  1190             d->items.prepend(item);
       
  1191             d->updateItem(item, pos);
       
  1192             if (d->model->completePending())
       
  1193                 d->model->completeItem();
       
  1194             d->firstIndex = idx;
       
  1195             idx = d->firstIndex - 1;
       
  1196             if (idx < 0)
       
  1197                 idx = d->model->count() - 1;
       
  1198             pos = d->positionOfIndex(idx);
       
  1199         }
       
  1200     }
       
  1201 
       
  1202     if (!currentVisible)
       
  1203         d->currentItemOffset = 1.0;
       
  1204 
       
  1205     if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) {
       
  1206         d->updateItem(d->highlightItem, d->highlightRangeStart);
       
  1207         if (QDeclarativePathViewAttached *att = d->attached(d->highlightItem))
       
  1208             att->setOnPath(true);
       
  1209     } else if (d->highlightItem && d->moveReason != QDeclarativePathViewPrivate::SetIndex) {
       
  1210         d->updateItem(d->highlightItem, d->currentItemOffset);
       
  1211         if (QDeclarativePathViewAttached *att = d->attached(d->highlightItem))
       
  1212             att->setOnPath(currentVisible);
       
  1213     }
       
  1214 }
       
  1215 
       
  1216 void QDeclarativePathView::itemsInserted(int modelIndex, int count)
       
  1217 {
       
  1218     //XXX support animated insertion
       
  1219     Q_D(QDeclarativePathView);
       
  1220     if (!d->isValid() || !isComponentComplete())
       
  1221         return;
       
  1222 
       
  1223     QList<QDeclarativeItem *> removedItems = d->items;
       
  1224     d->items.clear();
       
  1225     if (modelIndex <= d->currentIndex) {
       
  1226         d->currentIndex += count;
       
  1227         emit currentIndexChanged();
       
  1228     }
       
  1229     d->regenerate();
       
  1230     while (removedItems.count())
       
  1231         d->releaseItem(removedItems.takeLast());
       
  1232     d->updateCurrent();
       
  1233     emit countChanged();
       
  1234 }
       
  1235 
       
  1236 void QDeclarativePathView::itemsRemoved(int modelIndex, int count)
       
  1237 {
       
  1238     //XXX support animated removal
       
  1239     Q_D(QDeclarativePathView);
       
  1240     if (!d->isValid() || !isComponentComplete())
       
  1241         return;
       
  1242 
       
  1243     // fix current
       
  1244     bool currentChanged = false;
       
  1245     if (d->currentIndex >= modelIndex + count) {
       
  1246         d->currentIndex -= count;
       
  1247         currentChanged = true;
       
  1248     } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
       
  1249         // current item has been removed.
       
  1250         d->currentIndex = qMin(modelIndex, d->model->count()-1);
       
  1251         if (d->currentItem) {
       
  1252             if (QDeclarativePathViewAttached *att = d->attached(d->currentItem))
       
  1253                 att->setIsCurrentItem(true);
       
  1254         }
       
  1255         currentChanged = true;
       
  1256     }
       
  1257 
       
  1258     QList<QDeclarativeItem *> removedItems = d->items;
       
  1259     d->items.clear();
       
  1260     if (d->offset >= d->model->count())
       
  1261         d->offset = d->model->count() - 1;
       
  1262 
       
  1263     d->regenerate();
       
  1264     while (removedItems.count())
       
  1265         d->releaseItem(removedItems.takeLast());
       
  1266     d->updateCurrent();
       
  1267     if (currentChanged)
       
  1268         emit currentIndexChanged();
       
  1269     emit countChanged();
       
  1270 }
       
  1271 
       
  1272 void QDeclarativePathView::itemsMoved(int /*from*/, int /*to*/, int /*count*/)
       
  1273 {
       
  1274     Q_D(QDeclarativePathView);
       
  1275     if (!d->isValid() || !isComponentComplete())
       
  1276         return;
       
  1277 
       
  1278     QList<QDeclarativeItem *> removedItems = d->items;
       
  1279     d->items.clear();
       
  1280     d->regenerate();
       
  1281     while (removedItems.count())
       
  1282         d->releaseItem(removedItems.takeLast());
       
  1283 
       
  1284     // Fix current index
       
  1285     if (d->currentIndex >= 0 && d->currentItem) {
       
  1286         int oldCurrent = d->currentIndex;
       
  1287         d->currentIndex = d->model->indexOf(d->currentItem, this);
       
  1288         if (oldCurrent != d->currentIndex)
       
  1289             emit currentIndexChanged();
       
  1290     }
       
  1291     d->updateCurrent();
       
  1292 }
       
  1293 
       
  1294 void QDeclarativePathView::modelReset()
       
  1295 {
       
  1296     Q_D(QDeclarativePathView);
       
  1297     d->regenerate();
       
  1298     emit countChanged();
       
  1299 }
       
  1300 
       
  1301 void QDeclarativePathView::createdItem(int index, QDeclarativeItem *item)
       
  1302 {
       
  1303     Q_D(QDeclarativePathView);
       
  1304     if (d->requestedIndex != index) {
       
  1305         if (!d->attType) {
       
  1306             // pre-create one metatype to share with all attached objects
       
  1307             d->attType = new QDeclarativeOpenMetaObjectType(&QDeclarativePathViewAttached::staticMetaObject, qmlEngine(this));
       
  1308             foreach(const QString &attr, d->path->attributes())
       
  1309                 d->attType->createProperty(attr.toUtf8());
       
  1310         }
       
  1311         qPathViewAttachedType = d->attType;
       
  1312         QDeclarativePathViewAttached *att = static_cast<QDeclarativePathViewAttached *>(qmlAttachedPropertiesObject<QDeclarativePathView>(item));
       
  1313         qPathViewAttachedType = 0;
       
  1314         if (att) {
       
  1315             att->m_view = this;
       
  1316             att->setOnPath(false);
       
  1317         }
       
  1318         item->setParentItem(this);
       
  1319         d->updateItem(item, index < d->firstIndex ? 0.0 : 1.0);
       
  1320     }
       
  1321 }
       
  1322 
       
  1323 void QDeclarativePathView::destroyingItem(QDeclarativeItem *item)
       
  1324 {
       
  1325     Q_UNUSED(item);
       
  1326 }
       
  1327 
       
  1328 void QDeclarativePathView::ticked()
       
  1329 {
       
  1330     Q_D(QDeclarativePathView);
       
  1331     d->updateCurrent();
       
  1332 }
       
  1333 
       
  1334 // find the item closest to the snap position
       
  1335 int QDeclarativePathViewPrivate::calcCurrentIndex()
       
  1336 {
       
  1337     int current = -1;
       
  1338     if (model && items.count()) {
       
  1339         offset = qmlMod(offset, model->count());
       
  1340         if (offset < 0)
       
  1341             offset += model->count();
       
  1342         current = qRound(qAbs(qmlMod(model->count() - offset, model->count())));
       
  1343         current = current % model->count();
       
  1344     }
       
  1345 
       
  1346     return current;
       
  1347 }
       
  1348 
       
  1349 void QDeclarativePathViewPrivate::updateCurrent()
       
  1350 {
       
  1351     Q_Q(QDeclarativePathView);
       
  1352     if (moveReason != Mouse)
       
  1353         return;
       
  1354     if (!haveHighlightRange || highlightRangeMode != QDeclarativePathView::StrictlyEnforceRange)
       
  1355         return;
       
  1356 
       
  1357     int idx = calcCurrentIndex();
       
  1358     if (model && idx != currentIndex) {
       
  1359         int itemIndex = (currentIndex - firstIndex + model->count()) % model->count();
       
  1360         if (itemIndex < items.count()) {
       
  1361             if (QDeclarativeItem *item = items.at(itemIndex)) {
       
  1362                 if (QDeclarativePathViewAttached *att = attached(item))
       
  1363                     att->setIsCurrentItem(false);
       
  1364             }
       
  1365         }
       
  1366         currentIndex = idx;
       
  1367         currentItem = 0;
       
  1368         itemIndex = (idx - firstIndex + model->count()) % model->count();
       
  1369         if (itemIndex < items.count()) {
       
  1370             currentItem = items.at(itemIndex);
       
  1371             currentItem->setFocus(true);
       
  1372             if (QDeclarativePathViewAttached *att = attached(currentItem))
       
  1373                 att->setIsCurrentItem(true);
       
  1374         }
       
  1375         emit q->currentIndexChanged();
       
  1376     }
       
  1377 }
       
  1378 
       
  1379 void QDeclarativePathViewPrivate::fixOffsetCallback(void *d)
       
  1380 {
       
  1381     ((QDeclarativePathViewPrivate *)d)->fixOffset();
       
  1382 }
       
  1383 
       
  1384 void QDeclarativePathViewPrivate::fixOffset()
       
  1385 {
       
  1386     Q_Q(QDeclarativePathView);
       
  1387     if (model && items.count()) {
       
  1388         if (haveHighlightRange && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) {
       
  1389             int curr = calcCurrentIndex();
       
  1390             if (curr != currentIndex)
       
  1391                 q->setCurrentIndex(curr);
       
  1392             else
       
  1393                 snapToCurrent();
       
  1394         }
       
  1395     }
       
  1396 }
       
  1397 
       
  1398 void QDeclarativePathViewPrivate::snapToCurrent()
       
  1399 {
       
  1400     if (!model || model->count() <= 0)
       
  1401         return;
       
  1402 
       
  1403     qreal targetOffset = model->count() - currentIndex;
       
  1404 
       
  1405     moveReason = Other;
       
  1406     tl.reset(moveOffset);
       
  1407     moveOffset.setValue(offset);
       
  1408 
       
  1409     const int duration = highlightMoveDuration;
       
  1410 
       
  1411     if (targetOffset - offset > model->count()/2) {
       
  1412         qreal distance = model->count() - targetOffset + offset;
       
  1413         tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance));
       
  1414         tl.set(moveOffset, model->count());
       
  1415         tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * (model->count()-targetOffset) / distance));
       
  1416     } else if (targetOffset - offset <= -model->count()/2) {
       
  1417         qreal distance = model->count() - offset + targetOffset;
       
  1418         tl.move(moveOffset, model->count(), QEasingCurve(QEasingCurve::InQuad), int(duration * (model->count()-offset) / distance));
       
  1419         tl.set(moveOffset, 0.0);
       
  1420         tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance));
       
  1421     } else {
       
  1422         tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
       
  1423     }
       
  1424 }
       
  1425 
       
  1426 QDeclarativePathViewAttached *QDeclarativePathView::qmlAttachedProperties(QObject *obj)
       
  1427 {
       
  1428     return new QDeclarativePathViewAttached(obj);
       
  1429 }
       
  1430 
       
  1431 QT_END_NAMESPACE
       
  1432