ganeswidgets/src/HgContainer.cpp
changeset 6 1cdcc61142d2
parent 5 4fa04caf0f43
child 7 5ebec3429918
equal deleted inserted replaced
5:4fa04caf0f43 6:1cdcc61142d2
     1 /*
       
     2 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:
       
    15 *
       
    16 */
       
    17 
       
    18 #include <QGesture>
       
    19 #include <QPainter>
       
    20 #include <QTimer>
       
    21 #include <HbMainWindow>
       
    22 #include "HgContainer.h"
       
    23 #include "hgmediawallrenderer.h"
       
    24 #include "hgquad.h"
       
    25 #include "hgvgquadrenderer.h"
       
    26 #include "hgvgimage.h"
       
    27 #include "hgwidgetitem.h"
       
    28 #include "trace.h"
       
    29 
       
    30 #include <HbCheckBox>
       
    31 #include <HbGridViewItem>
       
    32 #include <HbGridView>
       
    33 #include <HbIconItem>
       
    34 #include <QAbstractItemModel>
       
    35 #include <HbTapGesture>
       
    36 #include "hglongpressvisualizer.h"
       
    37 
       
    38 static const qreal KSpringKScrolling(50.0);
       
    39 static const qreal KSpringKScrollBar(10.0);
       
    40 static const qreal KSpringDampingScrolling(20.0);
       
    41 static const qreal KSpringDampingScrollBar(5.0);
       
    42 static const qreal KFramesToZeroVelocity(60.0);
       
    43 static const int   KLongTapDuration(400);
       
    44 
       
    45 
       
    46 HgContainer::HgContainer(QGraphicsItem* parent) :
       
    47     HbWidget(parent),
       
    48     mQuadRenderer(0),
       
    49     mRenderer(0),
       
    50     mTapCount(0),
       
    51     mAnimateUsingScrollBar(false),
       
    52     mSelectionMode(HgWidget::NoSelection),
       
    53     mSelectionModel(0),
       
    54     mMarkImageOn(0),
       
    55     mMarkImageOff(0),
       
    56     mSpringVelAtDragStart(0),
       
    57     mDragged(false),
       
    58     mFramesDragged(0),
       
    59     mHitItemView(NULL),
       
    60     mLongPressVisualizer(NULL),
       
    61     mLongPressTimer(NULL),
       
    62     mHitItemIndex(-1),
       
    63     mItemSizePolicy(HgWidget::ItemSizeAutomatic),
       
    64     mOrientation(Qt::Vertical),
       
    65     mDelayedScrollToIndex(),
       
    66     mIgnoreGestureAction(false)
       
    67 {
       
    68     FUNC_LOG;
       
    69 
       
    70     grabGesture(Qt::PanGesture);
       
    71     grabGesture(Qt::TapGesture);
       
    72 }
       
    73 
       
    74 HgContainer::~HgContainer()
       
    75 {
       
    76     FUNC_LOG;
       
    77 
       
    78     qDeleteAll(mItems);
       
    79     mItems.clear();
       
    80     delete mMarkImageOn;
       
    81     delete mMarkImageOff;
       
    82     delete mRenderer;
       
    83 }
       
    84 
       
    85 void HgContainer::setItemCount(int itemCount)
       
    86 {
       
    87     FUNC_LOG;
       
    88 
       
    89     qDeleteAll(mItems);
       
    90     mItems.clear();
       
    91     for (int i=0; i<itemCount; i++) {
       
    92         HgWidgetItem* item = new HgWidgetItem(mQuadRenderer);
       
    93         mItems.append(item);
       
    94     }
       
    95 }
       
    96 
       
    97 int HgContainer::itemCount() const
       
    98 {
       
    99     return mItems.count();
       
   100 }
       
   101 
       
   102 int HgContainer::rowCount() const
       
   103 {
       
   104     return mRenderer ? mRenderer->getRowCount() : 0;
       
   105 }
       
   106 
       
   107 QList<HgWidgetItem*> HgContainer::items() const
       
   108 {
       
   109     return mItems;
       
   110 }
       
   111 
       
   112 HgWidgetItem* HgContainer::itemByIndex(const QModelIndex& index) const
       
   113 {
       
   114     foreach (HgWidgetItem* item, mItems) {
       
   115         if (item->modelIndex() == index)
       
   116             return item;
       
   117     }
       
   118 
       
   119     return 0;
       
   120 }
       
   121 
       
   122 HgWidgetItem* HgContainer::itemByIndex(const int& index) const
       
   123 {
       
   124     if (mItems.count() > index && index >= 0)
       
   125         return mItems.at(index);
       
   126     else
       
   127         return 0;
       
   128 }
       
   129 
       
   130 /*!
       
   131     Changes the selection model of the container.
       
   132     Ownership is not transferred.
       
   133     Widget is redrawn to make new selection visible.
       
   134 */
       
   135 void HgContainer::setSelectionModel(QItemSelectionModel *selectionModel, const QModelIndex &defaultItem)
       
   136 {
       
   137     FUNC_LOG;
       
   138     HANDLE_ERROR_NULL(selectionModel); // Parameter is always a valid QItemSelectionModel
       
   139 
       
   140     if (mSelectionModel == selectionModel) return;
       
   141 
       
   142     bool defaultCurrentSet(false);
       
   143 
       
   144     if (!selectionModel->currentIndex().isValid()) { // If there is valid current item, do not change it
       
   145         if (!mSelectionModel && defaultItem.isValid()) { // mSelectionModel is 0 when called first time
       
   146             selectionModel->setCurrentIndex(defaultItem, QItemSelectionModel::Current);
       
   147             defaultCurrentSet = true;
       
   148         }
       
   149         else if (mSelectionModel && mSelectionModel->currentIndex().isValid()) {
       
   150             selectionModel->setCurrentIndex(mSelectionModel->currentIndex(),
       
   151             QItemSelectionModel::Current);
       
   152         }
       
   153     }
       
   154 
       
   155     mSelectionModel = selectionModel;
       
   156 
       
   157     if (mSelectionModel->currentIndex().isValid() && !defaultCurrentSet) {
       
   158         scrollTo(mSelectionModel->currentIndex());
       
   159     }
       
   160     else {
       
   161         update();
       
   162     }
       
   163 }
       
   164 
       
   165 /*!
       
   166     Returns the selection model of the container.
       
   167     Ownership is not transferred.
       
   168 */
       
   169 QItemSelectionModel *HgContainer::selectionModel() const
       
   170 {
       
   171     FUNC_LOG;
       
   172 
       
   173     return mSelectionModel;
       
   174 }
       
   175 
       
   176 /*!
       
   177     Changes the selection mode of the container (no selection/multiselection).
       
   178 */
       
   179 void HgContainer::setSelectionMode(HgWidget::SelectionMode mode, bool resetSelection)
       
   180 {
       
   181     FUNC_LOG;
       
   182 
       
   183     if (mSelectionMode != mode) {
       
   184         mSelectionMode = mode;
       
   185 
       
   186         if (mSelectionModel && resetSelection) {
       
   187             mSelectionModel->clearSelection();
       
   188             update();
       
   189         }
       
   190     }
       
   191 }
       
   192 
       
   193 /*!
       
   194     Returns the selection mode of the container (no selection/multiselection).
       
   195 */
       
   196 HgWidget::SelectionMode HgContainer::selectionMode() const
       
   197 {
       
   198     FUNC_LOG;
       
   199 
       
   200     return mSelectionMode;
       
   201 }
       
   202 
       
   203 void HgContainer::dimensions(qreal &screenSize, qreal &worldSize)
       
   204 {
       
   205     const QRectF containerRect(rect());
       
   206 
       
   207     if (scrollDirection()== Qt::Vertical) {
       
   208         // assume we are in portrait mode, ofcource this might not be the case
       
   209         screenSize = containerRect.height()/(mRenderer->getImageSize().height() + mRenderer->getSpacing().height());
       
   210         worldSize = worldWidth();
       
   211     }
       
   212     else{
       
   213         screenSize = containerRect.width()/(mRenderer->getImageSize().width() + mRenderer->getSpacing().width());
       
   214         worldSize = worldWidth();
       
   215     }
       
   216 }
       
   217 
       
   218 Qt::Orientation HgContainer::orientation() const
       
   219 {
       
   220     FUNC_LOG;
       
   221 
       
   222     return mOrientation;
       
   223 }
       
   224 
       
   225 void HgContainer::setOrientation(Qt::Orientation orientation, bool animate)
       
   226 {
       
   227     FUNC_LOG;
       
   228 
       
   229     mOrientation = orientation;
       
   230     mRenderer->setOrientation(orientation);
       
   231     mRenderer->setScrollDirection(orientation, animate);
       
   232     if (mSpring.isActive()) {
       
   233         // Need to stop scrolling.
       
   234         mSpring.cancel();
       
   235     }
       
   236 }
       
   237 
       
   238 void HgContainer::scrollToPosition(qreal value, bool animate)
       
   239 {
       
   240     FUNC_LOG;
       
   241 
       
   242     scrollToPosition(QPointF(value*worldWidth(), 0), animate);
       
   243 }
       
   244 
       
   245 void HgContainer::scrollToPosition(const QPointF& pos, bool animate)
       
   246 {
       
   247     FUNC_LOG;
       
   248 
       
   249     mAnimateUsingScrollBar = animate;
       
   250     initSpringForScrollBar();
       
   251 
       
   252     if (animate)
       
   253         mSpring.animateToPos(pos);
       
   254     else
       
   255         mSpring.gotoPos(pos);
       
   256 }
       
   257 
       
   258 void HgContainer::scrollTo(const QModelIndex &index)
       
   259 {
       
   260     FUNC_LOG;
       
   261     INFO("Scrolling to" << index);
       
   262 
       
   263     if (index.isValid() && mRenderer->getRowCount() > 0 ) {
       
   264 
       
   265         QRectF containerRect(rect());
       
   266         if (containerRect.isNull()) {
       
   267             // Container hasn't been resized yet. We need to know the container
       
   268             // size before we can calculate if index we are scrolling to is valid.
       
   269             // Store scrollTo index and scrolling is performed when container is resized.
       
   270             mDelayedScrollToIndex = index;
       
   271             return;
       
   272         }
       
   273 
       
   274         // Container has some size. We can try to calculate if scrollto index is valid.
       
   275         // ScrollTo index will be the top item in grid and left item on coverflow.
       
   276 
       
   277         if (!mRenderer->coverflowModeEnabled()) {
       
   278             // Grid case
       
   279             int itemsOnScreen = 0;
       
   280             if (scrollDirection()== Qt::Vertical) {
       
   281                 const int rowHeight = mRenderer->getImageSize().height() + mRenderer->getSpacing().height();
       
   282                 itemsOnScreen = containerRect.height()/rowHeight;
       
   283                 if ((int)containerRect.height()%rowHeight) {
       
   284                     itemsOnScreen++;
       
   285                 }
       
   286                 itemsOnScreen *= rowCount();
       
   287                 if (itemsOnScreen + index.row() > mItems.count()) {
       
   288                     int newItem = mItems.count()-itemsOnScreen;
       
   289 
       
   290                     if (mItems.count()%rowCount())
       
   291                         newItem += rowCount() - (mItems.count()%rowCount());
       
   292                     if (newItem < 0)
       
   293                         newItem = 0;
       
   294 
       
   295                     scrollToPosition(QPointF(newItem/mRenderer->getRowCount(),0), false);
       
   296                 } else {
       
   297                     scrollToPosition(QPointF(index.row()/mRenderer->getRowCount(), 0), false);
       
   298                 }
       
   299             } else {
       
   300                 // Scrolldirection is horizontal
       
   301                 const int rowWidth = mRenderer->getImageSize().width() + mRenderer->getSpacing().width();
       
   302                 itemsOnScreen = containerRect.width()/rowWidth;
       
   303                 if ((int)containerRect.height()%rowWidth) {
       
   304                     itemsOnScreen++;
       
   305                 }
       
   306                 itemsOnScreen *= rowCount();
       
   307                 if (itemsOnScreen + index.row() > mItems.count()) {
       
   308                     int newItem = mItems.count()-itemsOnScreen;
       
   309 
       
   310                     if (mItems.count()%rowCount())
       
   311                         newItem += rowCount() - (mItems.count()%rowCount());
       
   312                     if (newItem < 0) newItem = 0;
       
   313 
       
   314                     scrollToPosition(QPointF(newItem/mRenderer->getRowCount(),0), false);
       
   315                 } else {
       
   316                     scrollToPosition(QPointF(index.row()/mRenderer->getRowCount(), 0), false);
       
   317                 }
       
   318             }
       
   319             updateBySpringPosition();
       
   320         } else {
       
   321             // Coverflow case. TODO, this will need some finetuning.
       
   322             scrollToPosition(QPointF(index.row()/mRenderer->getRowCount(), 0), false);
       
   323             updateBySpringPosition();
       
   324         }
       
   325     }
       
   326 }
       
   327 
       
   328 void HgContainer::itemDataChanged(const QModelIndex &firstIndex, const QModelIndex &lastIndex)
       
   329 {
       
   330     FUNC_LOG;
       
   331 
       
   332     // TODO, fix this
       
   333     int columns = firstIndex.model()->columnCount(QModelIndex());
       
   334 
       
   335     // Check this!!
       
   336     int index = columns*firstIndex.row()+firstIndex.column();
       
   337     int index2 = columns*lastIndex.row()+lastIndex.column();
       
   338 
       
   339     // convert indeces to match one dimensional item array
       
   340     itemDataChanged( index, index2 );
       
   341 }
       
   342 
       
   343 void HgContainer::addItems(int start, int end)
       
   344 {
       
   345     FUNC_LOG;
       
   346     HANDLE_ERROR_NULL(mSelectionModel);
       
   347 
       
   348     int first = qBound(0, start, mItems.count()-1);
       
   349     int last = qBound(0, end, mItems.count()-1);
       
   350     for (int i = 0; i <= end-start; i++) {
       
   351         HgWidgetItem* item = new HgWidgetItem(mQuadRenderer);
       
   352         mItems.insert(start, item);
       
   353     }
       
   354     scrollTo(mSelectionModel->currentIndex());
       
   355 }
       
   356 
       
   357 void HgContainer::removeItems(int start, int end)
       
   358 {
       
   359     FUNC_LOG;
       
   360     HANDLE_ERROR_NULL(mSelectionModel);
       
   361 
       
   362     int first = qBound(0, start, mItems.count()-1);
       
   363     int last = qBound(0, end, mItems.count()-1);
       
   364     for (int i = last; i >= first; i--) {
       
   365         delete mItems.at(i);
       
   366         mItems.removeAt(i);
       
   367     }
       
   368     scrollTo(mSelectionModel->currentIndex());
       
   369 }
       
   370 
       
   371 void HgContainer::moveItems(int start, int end, int destination)
       
   372 {
       
   373     FUNC_LOG;
       
   374     HANDLE_ERROR_NULL(mSelectionModel);
       
   375 
       
   376     int first = qBound(0, start, mItems.count()-1);
       
   377     int last = qBound(0, end, mItems.count()-1);
       
   378     int target = qBound(0, destination, mItems.count()-1);
       
   379 
       
   380     if (target < first) {
       
   381         for (int i = 0; i <= last-first; i++) {
       
   382             mItems.move(first+i, target+i);
       
   383         }
       
   384     }
       
   385     else if (target > last) {
       
   386         for (int i = 0; i <= last-first; i++) {
       
   387             mItems.move(last-i, target);
       
   388         }
       
   389     }
       
   390     // else do nothing
       
   391     scrollTo(mSelectionModel->currentIndex());
       
   392 }
       
   393 
       
   394 int HgContainer::imageCount() const
       
   395 {
       
   396     return mItems.count();
       
   397 }
       
   398 
       
   399 const HgImage *HgContainer::image(int index) const
       
   400 {
       
   401     return mItems[index]->image();
       
   402 }
       
   403 
       
   404 int HgContainer::flags(int index) const
       
   405 {
       
   406     if (index >= 0 && index < itemCount()) {
       
   407         if (mSelectionMode != HgWidget::NoSelection) {
       
   408             if (mSelectionModel && mSelectionModel->isSelected(mSelectionModel->model()->index(index, 0))) {
       
   409                 return 1; // TODO: Assign flag to mark indicator
       
   410             } else
       
   411                 return 2;
       
   412         }
       
   413     }
       
   414     return 0;
       
   415 }
       
   416 
       
   417 const HgImage *HgContainer::indicator(int flags) const
       
   418 {
       
   419     if (flags & 1) {
       
   420         return mMarkImageOn;
       
   421     }
       
   422     else if (flags & 2) {
       
   423         return mMarkImageOff;
       
   424     }
       
   425 
       
   426     return 0;
       
   427 }
       
   428 
       
   429 void HgContainer::updateBySpringPosition()
       
   430 {
       
   431     // spring works always in one dimension, that is, x coord.
       
   432     qreal pos = mSpring.pos().x();
       
   433 
       
   434     onScrollPositionChanged(pos);
       
   435 
       
   436     emit scrollPositionChanged(pos, mAnimateUsingScrollBar);
       
   437     update();
       
   438 }
       
   439 
       
   440 void HgContainer::redraw()
       
   441 {
       
   442     update();
       
   443 }
       
   444 
       
   445 void HgContainer::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
       
   446 {
       
   447     Q_UNUSED(option)
       
   448     Q_UNUSED(widget)
       
   449 
       
   450     // update spring position at paint if needed,
       
   451     // this is hack for scrollbar, since dragging it
       
   452     // causes also paint events in here
       
   453     if (mSpring.updatePositionIfNeeded())
       
   454     {
       
   455         qreal pos = mSpring.pos().x();
       
   456         onScrollPositionChanged(pos);
       
   457         emit scrollPositionChanged(pos, true);
       
   458     }
       
   459 
       
   460     QPainter::RenderHints hints = painter->renderHints();
       
   461     painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
       
   462 
       
   463 
       
   464     // interpolate spring velocity towards zero, this is done
       
   465     // so that spring velocity for rendering doesn't drop directly to
       
   466     // zero when dragging starts
       
   467     qreal springVel = mSpring.velocity().x();
       
   468     if (mDragged) {
       
   469         qreal t = qBound(mFramesDragged / KFramesToZeroVelocity, 0.0f, 1.0f);
       
   470         springVel = mSpringVelAtDragStart * (1.0f - t);
       
   471         mFramesDragged++;
       
   472     }
       
   473 
       
   474     // setup rendering and draw the current view
       
   475     mRenderer->setCameraDistance(getCameraDistance(springVel));
       
   476     mRenderer->setCameraRotationY(getCameraRotationY(springVel));
       
   477     mRenderer->draw(mSpring.startPos(), mSpring.pos(), mSpring.endPos(),
       
   478                     springVel, painter, sceneTransform(), rect());
       
   479 
       
   480     painter->setRenderHint(QPainter::SmoothPixmapTransform, false);
       
   481 }
       
   482 
       
   483 void HgContainer::resizeEvent(QGraphicsSceneResizeEvent *event)
       
   484 {
       
   485     FUNC_LOG;
       
   486 
       
   487     HbWidget::resizeEvent(event);
       
   488 
       
   489     if (mDelayedScrollToIndex.isValid()) {
       
   490         scrollTo(mDelayedScrollToIndex);
       
   491         // set scrollto index to invalid value.
       
   492         mDelayedScrollToIndex = QModelIndex();
       
   493     }
       
   494 }
       
   495 
       
   496 // this needs to be implemented for gesture framework to work
       
   497 void HgContainer::mousePressEvent(QGraphicsSceneMouseEvent *event)
       
   498 {
       
   499     Q_UNUSED(event);
       
   500 }
       
   501 
       
   502 void HgContainer::gestureEvent(QGestureEvent *event)
       
   503 {
       
   504     FUNC_LOG;
       
   505 
       
   506     if (mItems.count() == 0) {
       
   507         // we have no items so no need to handle the gesture.
       
   508         event->ignore();
       
   509         return;
       
   510     }
       
   511     
       
   512     bool eventHandled(false);
       
   513     // Event may contain more than one gesture type
       
   514     HbTapGesture *tap = 0;
       
   515     if (QGesture *gesture = event->gesture(Qt::TapGesture)) {
       
   516         tap = static_cast<HbTapGesture *>(event->gesture(Qt::TapGesture));
       
   517         if (tap->tapStyleHint() == HbTapGesture::TapAndHold) {
       
   518             eventHandled = handleLongTap(tap->state(),
       
   519                     mapFromScene(event->mapToGraphicsScene(tap->hotSpot())));
       
   520         
       
   521         } else {
       
   522             eventHandled = handleTap(tap->state(),
       
   523                     mapFromScene(event->mapToGraphicsScene(tap->hotSpot())));
       
   524         }
       
   525     }
       
   526     if (QGesture *pan = event->gesture(Qt::PanGesture)) {
       
   527         eventHandled = handlePanning(static_cast<QPanGesture*>(pan));
       
   528     } else if( mIgnoreGestureAction && tap && tap->state() == Qt::GestureCanceled ) {
       
   529         // user has tapped or long pressed in grid while scrolling so we need to
       
   530         // stop the 3d effect.
       
   531         mSpring.resetVelocity();
       
   532         update();
       
   533         mIgnoreGestureAction = false;
       
   534     }
       
   535 
       
   536     eventHandled ? event->accept() : event->ignore();    
       
   537 }
       
   538 
       
   539 void HgContainer::init(Qt::Orientation scrollDirection)
       
   540 {
       
   541     FUNC_LOG;
       
   542 
       
   543     mRenderer = createRenderer(scrollDirection);
       
   544     mOrientation = scrollDirection;
       
   545 
       
   546     mQuadRenderer = mRenderer->getRenderer();
       
   547 
       
   548     // Fetch icons for marking mode (on and off states).
       
   549 
       
   550     mMarkImageOn = mQuadRenderer->createNativeImage();
       
   551     HANDLE_ERROR_NULL(mMarkImageOn);
       
   552     mMarkImageOff = mQuadRenderer->createNativeImage();
       
   553     HANDLE_ERROR_NULL(mMarkImageOff);
       
   554 
       
   555     // Since there is no way to create the icons directly currently
       
   556     // lets create HbCheckBox and ask primitives from it.
       
   557     HbCheckBox* checkBox = new HbCheckBox();
       
   558     checkBox->setCheckState(Qt::Checked);
       
   559     QGraphicsItem *icon = checkBox->HbWidget::primitive("icon");
       
   560     HbIconItem *iconItem = 0;
       
   561     if (icon) {
       
   562         iconItem = static_cast<HbIconItem*>(icon);    
       
   563         if (mMarkImageOn) {
       
   564             mMarkImageOn->setPixmap(iconItem->icon().pixmap());
       
   565         }
       
   566     }
       
   567     checkBox->setCheckState(Qt::Unchecked);
       
   568     icon = checkBox->HbWidget::primitive("icon");    
       
   569     if (icon) {
       
   570         iconItem = static_cast<HbIconItem*>(icon);
       
   571         if (mMarkImageOff) {
       
   572             mMarkImageOff->setPixmap(iconItem->icon().pixmap());
       
   573         }
       
   574     }    
       
   575     delete checkBox;
       
   576 
       
   577     connect(&mSpring, SIGNAL(updated()), SLOT(updateBySpringPosition()));
       
   578     connect(&mSpring, SIGNAL(started()), SIGNAL(scrollingStarted()));
       
   579     connect(&mSpring, SIGNAL(started()), SLOT(onScrollingStarted()));
       
   580     connect(&mSpring, SIGNAL(ended()), SIGNAL(scrollingEnded()));
       
   581     connect(&mSpring, SIGNAL(ended()), SLOT(onScrollingEnded()));
       
   582     connect(mRenderer, SIGNAL(renderingNeeded()), SLOT(redraw()));
       
   583 
       
   584 }
       
   585 
       
   586 qreal HgContainer::worldWidth() const
       
   587 {
       
   588     return (qreal)mRenderer->getWorldWidth();
       
   589 }
       
   590 
       
   591 void HgContainer::initSpringForScrollBar()
       
   592 {
       
   593     FUNC_LOG;
       
   594 
       
   595     mSpring.setDamping(KSpringDampingScrollBar);
       
   596     mSpring.setK(KSpringKScrollBar);
       
   597 }
       
   598 
       
   599 void HgContainer::initSpringForScrolling()
       
   600 {
       
   601     FUNC_LOG;
       
   602 
       
   603     mSpring.setDamping(KSpringDampingScrolling);
       
   604     mSpring.setK(KSpringKScrolling);
       
   605 }
       
   606 
       
   607 void HgContainer::boundSpring()
       
   608 {
       
   609     FUNC_LOG;
       
   610 
       
   611     qreal x = mSpring.endPos().x();
       
   612     x = qBound(qreal(0), x, worldWidth());
       
   613     if (mRenderer->coverflowModeEnabled()) {
       
   614         qreal i = floorf(x);
       
   615         x = (x - i > 0.5f) ? ceilf(x) : i;
       
   616         mSpring.animateToPos(QPointF(x, 0));
       
   617     }
       
   618 
       
   619     mSpring.animateToPos(QPointF(x, 0));
       
   620 
       
   621 }
       
   622 
       
   623 bool HgContainer::handlePanning(QPanGesture *gesture)
       
   624 {
       
   625     mAnimateUsingScrollBar = false;
       
   626     initSpringForScrolling();
       
   627 
       
   628     qreal pos = mSpring.pos().x();
       
   629     qreal delta(0);
       
   630     qreal itemSide(0);
       
   631 
       
   632     if (mOrientation == mRenderer->getScrollDirection()) {
       
   633         delta = gesture->delta().y();
       
   634     }
       
   635     else {
       
   636         delta = gesture->delta().x();
       
   637     }
       
   638 
       
   639     if (mRenderer->getScrollDirection() == Qt::Vertical)
       
   640         itemSide = mRenderer->getImageSize().height()+mRenderer->getSpacing().height();
       
   641     else
       
   642         itemSide = mRenderer->getImageSize().width()+mRenderer->getSpacing().width();
       
   643 
       
   644     if (gesture->state() == Qt::GestureStarted) {
       
   645         mOffsetAtDragStart = gesture->offset();
       
   646     }
       
   647     else if (gesture->state() == Qt::GestureUpdated) {
       
   648         QPointF offset = gesture->offset();
       
   649         QPointF offsetDelta = offset - mOffsetAtDragStart;
       
   650         if (!mDragged && (qAbs(offsetDelta.x()) > 8 ||
       
   651             qAbs(offsetDelta.y()) > 8)) {
       
   652             mDragged = true;
       
   653             mDrag.reset(delta, mSpring.pos().x());
       
   654             mDragged = true;
       
   655             mSpringVelAtDragStart = mSpring.velocity().x();
       
   656             mFramesDragged = 0;
       
   657         }
       
   658 
       
   659         if (mDragged)
       
   660         {
       
   661             emit scrollingStarted();
       
   662 
       
   663             qreal newPosition = mDrag.update(delta, pos, itemSide);
       
   664             if (qAbs(newPosition - mSpring.pos().x()) > 0.01f) {
       
   665                 mSpring.gotoPos(QPointF(newPosition, 0));
       
   666                 if (mRenderer->coverflowModeEnabled()) {
       
   667                     emit scrollPositionChanged(newPosition,true);
       
   668                     update();                
       
   669                 } else {
       
   670                     updateBySpringPosition();
       
   671                 }
       
   672             }
       
   673         }
       
   674     }
       
   675     else if (mDragged && gesture->state() == Qt::GestureFinished) {
       
   676         mDrag.update(delta, pos, itemSide);
       
   677         mDragged = false;
       
   678         qreal newPos(0);
       
   679         if (mDrag.finish(pos, mRenderer->coverflowModeEnabled(), newPos)) {
       
   680             mSpring.animateToPos(QPointF(qBound(qreal(0), newPos, worldWidth()), 0));
       
   681             HgWidgetItem* item = itemByIndex(newPos);
       
   682             if (item && item->modelIndex() != mSelectionModel->currentIndex()) {
       
   683             //    mSelectionModel->setCurrentIndex(item->modelIndex(), QItemSelectionModel::Current);
       
   684             }
       
   685         }
       
   686         else {
       
   687             boundSpring();
       
   688         }
       
   689     }
       
   690     else if(!mDragged && gesture->state() == Qt::GestureFinished) {
       
   691         if (!mRenderer->coverflowModeEnabled()) {
       
   692             mSpring.resetVelocity();
       
   693             update();
       
   694         }
       
   695     }
       
   696     else if (gesture->state() == Qt::GestureCanceled) {
       
   697         boundSpring();
       
   698     }
       
   699 
       
   700     return true;
       
   701 }
       
   702 
       
   703 bool HgContainer::handleTap(Qt::GestureState state, const QPointF &pos)
       
   704 {
       
   705     FUNC_LOG;
       
   706     
       
   707     bool handleGesture = false;
       
   708 
       
   709     if (hasItemAt(pos)) {
       
   710         switch (state) 
       
   711             {
       
   712             case Qt::GestureStarted:
       
   713                 {
       
   714                 if (mRenderer->coverflowModeEnabled() || !mSpring.isActive()) {
       
   715                     mIgnoreGestureAction = false;
       
   716                     startLongPressWatcher(pos);
       
   717                 } else if(mSpring.isActive()) {
       
   718                     mSpring.cancel();
       
   719                     mIgnoreGestureAction = true;
       
   720                 }
       
   721                 break;
       
   722                 }
       
   723             case Qt::GestureFinished:
       
   724                 handleGesture = handleItemAction(pos, NormalTap);
       
   725             case Qt::GestureUpdated:
       
   726             case Qt::GestureCanceled:
       
   727             default:
       
   728                 stopLongPressWatcher();
       
   729                 break;
       
   730             }
       
   731         
       
   732         handleGesture = true;
       
   733     } else {
       
   734        mIgnoreGestureAction = true;
       
   735     }    
       
   736     return handleGesture;
       
   737 }
       
   738 
       
   739 bool HgContainer::handleLongTap(Qt::GestureState state, const QPointF &pos)
       
   740 {
       
   741     FUNC_LOG;
       
   742 
       
   743     bool handleGesture = false;
       
   744     
       
   745     if (hasItemAt(pos)) {
       
   746 
       
   747         switch (state) 
       
   748             {
       
   749             case Qt::GestureUpdated:
       
   750                 handleItemAction(pos,LongTap);
       
   751             case Qt::GestureStarted:
       
   752             case Qt::GestureCanceled:
       
   753             case Qt::GestureFinished:
       
   754             default:
       
   755                 stopLongPressWatcher();
       
   756                 break;                
       
   757             }
       
   758         handleGesture = true;
       
   759     } else {
       
   760         mIgnoreGestureAction = true;        
       
   761     }
       
   762 
       
   763     return handleGesture;
       
   764 }
       
   765 
       
   766 /*!
       
   767     Handle tap, lang tap and double tap action.
       
   768     Finds out the item in the tap position and sends out suitable signal,
       
   769     Sets the item as the current item and in multiselection mode toggles the
       
   770     item selection status.
       
   771 */
       
   772 bool HgContainer::handleItemAction(const QPointF &pos, ItemActionType action)
       
   773 {
       
   774     FUNC_LOG;
       
   775 
       
   776     // If there is content, mSelectionModel must always exist - either default or client-provided
       
   777     if (!mSelectionModel) return false;
       
   778 
       
   779     int index = -1;
       
   780     mHitItem = getItemAt(pos, index);
       
   781     if (mHitItem)
       
   782     {
       
   783         HgWidgetItem* item = itemByIndex(index);
       
   784         if (item && action != DoubleTap) {
       
   785             if (action == LongTap) {
       
   786                 INFO("Long tap:" << item->modelIndex().row());
       
   787                 
       
   788                 if (!mRenderer->coverflowModeEnabled()) {
       
   789                     selectItem(index);
       
   790                 } else {
       
   791                     mSelectionModel->setCurrentIndex(item->modelIndex(), QItemSelectionModel::Current);
       
   792                 }
       
   793 
       
   794                 if (!mIgnoreGestureAction) {
       
   795                     emit longPressed(item->modelIndex(), pos);
       
   796                 } else {
       
   797                     mSpring.resetVelocity();
       
   798                     update();
       
   799                     mIgnoreGestureAction = false;
       
   800                 }
       
   801             }
       
   802             else if (mSelectionMode == HgWidget::MultiSelection) {
       
   803                 mSelectionModel->setCurrentIndex(item->modelIndex(), QItemSelectionModel::Current);
       
   804                 INFO("Select:" << item->modelIndex().row());
       
   805                 mSelectionModel->select(item->modelIndex(), QItemSelectionModel::Toggle);
       
   806                 update(); // It would be enough to update the item
       
   807             }
       
   808             else if (mSelectionMode == HgWidget::SingleSelection) {
       
   809                 mSelectionModel->setCurrentIndex(item->modelIndex(), QItemSelectionModel::Current);
       
   810                 INFO("Select:" << item->modelIndex().row());
       
   811                 mSelectionModel->select(item->modelIndex(), QItemSelectionModel::ClearAndSelect);
       
   812                 update(); // It would be enough to update the item
       
   813             }
       
   814             else if (mSelectionMode == HgWidget::ContiguousSelection) {
       
   815                 mSelectionModel->setCurrentIndex(item->modelIndex(), QItemSelectionModel::Current);
       
   816                 QModelIndex newSelected = item->modelIndex();
       
   817                 QModelIndexList oldSelection = mSelectionModel->selectedIndexes();
       
   818                 INFO("Select:" << newSelected.row());
       
   819                 if (oldSelection.count() > 0 && !mSelectionModel->isSelected(newSelected)) {
       
   820                     if (newSelected.row() < oldSelection.front().row()) {
       
   821                         mSelectionModel->select(QItemSelection(newSelected, oldSelection.back()),
       
   822                             QItemSelectionModel::Select);
       
   823                     }
       
   824                     else { // newSelected.row() > oldSelection.back().row()
       
   825                         mSelectionModel->select(QItemSelection(oldSelection.front(), newSelected),
       
   826                             QItemSelectionModel::Select);
       
   827                     }
       
   828                 }
       
   829                 else {
       
   830                     mSelectionModel->select(newSelected, QItemSelectionModel::Select);
       
   831                 }
       
   832                 update(); // It would be enough to update the item
       
   833             }
       
   834             else {
       
   835                 INFO("Tap:" << item->modelIndex().row());
       
   836 
       
   837                 if (mRenderer->coverflowModeEnabled()) {  //coverflow and t-bone modes  
       
   838                     if (qAbs(qreal(index) - mSpring.pos().x()) < 0.01f)
       
   839                     {
       
   840                         mSelectionModel->setCurrentIndex(item->modelIndex(), QItemSelectionModel::Current);
       
   841                         emit activated(item->modelIndex());
       
   842                     }
       
   843                     else
       
   844                     {
       
   845                         mSpring.animateToPos(QPointF(index, 0));
       
   846                     }
       
   847                 }
       
   848                 else {   //grid mode
       
   849                     if (!mIgnoreGestureAction) {
       
   850                         // Current should be topleft item.
       
   851 //                        mSelectionModel->setCurrentIndex(item->modelIndex(), QItemSelectionModel::Current);
       
   852                         selectItem(index);
       
   853                         emit activated(item->modelIndex());                        
       
   854                     } else {
       
   855                         mSpring.resetVelocity();
       
   856                         update();
       
   857                         mIgnoreGestureAction = false;
       
   858                     }
       
   859                 }
       
   860             }
       
   861         }
       
   862 
       
   863         return true;
       
   864     }
       
   865     else {
       
   866         INFO("No quad at pos:" << pos);
       
   867 
       
   868         unselectItem();
       
   869         return false;
       
   870     }
       
   871 }
       
   872 
       
   873 bool HgContainer::getItemPoints(int index, QPolygonF& points)
       
   874 {
       
   875     return mRenderer->getItemPoints(index, points);
       
   876 }
       
   877 
       
   878 QList<QModelIndex> HgContainer::getVisibleItemIndices() const
       
   879 {
       
   880     QList<HgQuad*> quads = mRenderer->getVisibleQuads();
       
   881     QList<QModelIndex> result;
       
   882     for (int i = 0; i < quads.count(); i++) {
       
   883         bool ok;
       
   884         int index = quads.at(i)->userData().toInt(&ok);
       
   885         HgWidgetItem *item = itemByIndex(index);
       
   886         if (item)
       
   887             result.append(item->modelIndex());
       
   888     }
       
   889     qSort(result);
       
   890     return result;
       
   891 }
       
   892 
       
   893 void HgContainer::itemDataChanged(const int &firstIndex, const int &lastIndex)
       
   894 {
       
   895     FUNC_LOG;
       
   896     
       
   897     int firstItemOnScreen = 0, lastItemOnScreen = 0;
       
   898     firstItemOnScreen = mSpring.pos().x();
       
   899     firstItemOnScreen *= rowCount();
       
   900 
       
   901     int itemsOnScreen = mRenderer->getVisibleQuads().count();
       
   902     lastItemOnScreen = firstItemOnScreen+itemsOnScreen;
       
   903 
       
   904     if ( itemsOnScreen == 0 || (firstIndex >= firstItemOnScreen && firstIndex < lastItemOnScreen) ||
       
   905         (lastIndex >= firstItemOnScreen && lastIndex < lastItemOnScreen)) {
       
   906         update();
       
   907     }    
       
   908 }
       
   909 
       
   910 void HgContainer::selectItem(int index)
       
   911 {
       
   912     Q_UNUSED(index)
       
   913     // TODO: replace this with own selection implementation
       
   914 /*    if (index < 0 && index >= mItems.count())
       
   915         return;
       
   916     
       
   917     mHitItemIndex = index;
       
   918     
       
   919     if (mHitItemView)
       
   920     {
       
   921         delete mHitItemView;
       
   922         mHitItemView = NULL;
       
   923     }
       
   924     
       
   925     mHitItemView = new HbGridViewItem(this);
       
   926     mHitItemView->setVisible(false);
       
   927     mHitItemView->setPos(QPointF(0,0));
       
   928     mHitItemView->setPressed(true, false);
       
   929 
       
   930     const QImage& image = mItems[mHitItemIndex]->image()->getQImage();
       
   931     if (image.isNull())
       
   932     {
       
   933         mHitItemView->setVisible(false);
       
   934         return;
       
   935     }
       
   936     
       
   937     QPixmap pixmap = QPixmap::fromImage(image);        
       
   938     HbIcon icon(pixmap.scaled(mRenderer->getImageSize().toSize(), Qt::IgnoreAspectRatio));    
       
   939     QGraphicsItem* item = mHitItemView->style()->createPrimitive(HbStyle::P_GridViewItem_icon, mHitItemView);
       
   940     HbIconItem *iconItem = static_cast<HbIconItem*>(item);
       
   941     iconItem->setAlignment(Qt::AlignCenter);
       
   942     iconItem->setAspectRatioMode(Qt::IgnoreAspectRatio);    
       
   943     iconItem->setIcon(icon);
       
   944 
       
   945     mHitItemView->resize(mRenderer->getImageSize().width(),
       
   946         mRenderer->getImageSize().height());
       
   947         */
       
   948 }
       
   949 
       
   950 void HgContainer::updateSelectedItem()
       
   951 {
       
   952     if (!mHitItemView || mHitItemIndex == -1)
       
   953         return;
       
   954 
       
   955     QPolygonF points;
       
   956     if (!getItemPoints(mHitItemIndex, points))
       
   957     {
       
   958         // the item was not rendered, we must hide
       
   959         // our qt item        
       
   960         mHitItemView->setVisible(false);
       
   961         return;
       
   962     }
       
   963     
       
   964     QRectF bounds = points.boundingRect();
       
   965     if (!(rect().contains(bounds) || rect().intersects(bounds)))
       
   966     {
       
   967         mHitItemView->setVisible(false);
       
   968         return;
       
   969     }
       
   970 
       
   971     QPolygonF img;
       
   972     img.append(QPointF(3,mHitItemView->boundingRect().height()-3));
       
   973     img.append(QPointF(mHitItemView->boundingRect().width()-3,mHitItemView->boundingRect().height()-3));
       
   974     img.append(QPointF(mHitItemView->boundingRect().width()-3,3));
       
   975     img.append(QPointF(3,3));
       
   976 
       
   977     QTransform t;
       
   978     QTransform::quadToQuad(img, points, t);
       
   979 
       
   980     //t.translate(50,50);
       
   981     bool bOk;
       
   982     mHitItemView->setTransform(t * this->transform().inverted(&bOk));
       
   983     mHitItemView->setVisible(true);
       
   984 }
       
   985 
       
   986 void HgContainer::unselectItem()
       
   987 {
       
   988     mHitItemIndex = -1;
       
   989     if (mHitItemView)
       
   990     {
       
   991         mHitItemView->setVisible(false);
       
   992     }
       
   993 }
       
   994 
       
   995 void HgContainer::updateLongPressVisualizer()
       
   996 {
       
   997     int elapsed = mLongTapDuration.elapsed();
       
   998 
       
   999     if (elapsed > 80)
       
  1000     {
       
  1001         int frame = 100.0f * qreal(elapsed - 80) / qreal(KLongTapDuration - 80);
       
  1002         mLongPressVisualizer->setFrame(frame);
       
  1003     }
       
  1004 }
       
  1005 
       
  1006 bool HgContainer::hasItemAt(const QPointF& pos)
       
  1007 {
       
  1008     int dummy;
       
  1009     HgWidgetItem *item = getItemAt(pos, dummy);
       
  1010     if (item) {
       
  1011         return item->modelIndex().isValid();
       
  1012     }
       
  1013     return false;
       
  1014 }
       
  1015 
       
  1016 HgWidgetItem* HgContainer::getItemAt(const QPointF& pos, int& index)
       
  1017 {
       
  1018     HgQuad* quad = mRenderer->getQuadAt(pos);
       
  1019     if (quad)
       
  1020     {
       
  1021         bool ok;
       
  1022         index = quad->userData().toInt(&ok);
       
  1023 
       
  1024         HgWidgetItem* item = itemByIndex(index);
       
  1025         return item;
       
  1026     }
       
  1027     return NULL;
       
  1028 }
       
  1029 
       
  1030 void HgContainer::startLongPressWatcher(const QPointF& pos)
       
  1031 {
       
  1032     if (!mLongPressVisualizer)
       
  1033     {
       
  1034         mLongPressVisualizer = new HgLongPressVisualizer(this);
       
  1035         mLongPressVisualizer->setZValue(zValue()+1);
       
  1036     }
       
  1037 
       
  1038     mLongPressVisualizer->start(pos);
       
  1039 
       
  1040     if (!mLongPressTimer)
       
  1041     {
       
  1042         mLongPressTimer = new QTimer(this);
       
  1043         QObject::connect(mLongPressTimer, SIGNAL(timeout()), this, SLOT(updateLongPressVisualizer()));
       
  1044     }
       
  1045 
       
  1046     mLongPressTimer->start(20);
       
  1047 
       
  1048     mLongTapDuration.start();
       
  1049 }
       
  1050 
       
  1051 void HgContainer::stopLongPressWatcher()
       
  1052 {
       
  1053     if (mLongPressTimer && mLongPressVisualizer)
       
  1054     {
       
  1055         mLongPressTimer->stop();
       
  1056         mLongPressVisualizer->stop();
       
  1057     }
       
  1058 }
       
  1059 
       
  1060 qreal HgContainer::getCameraDistance(qreal springVelocity)
       
  1061 {
       
  1062     Q_UNUSED(springVelocity)
       
  1063     return 0;
       
  1064 }
       
  1065 
       
  1066 qreal HgContainer::getCameraRotationY(qreal springVelocity)
       
  1067 {
       
  1068     Q_UNUSED(springVelocity)
       
  1069     return 0;
       
  1070 }
       
  1071 
       
  1072 void HgContainer::handleTapAction(const QPointF& pos, HgWidgetItem* hitItem, int hitItemIndex)
       
  1073 {
       
  1074     Q_UNUSED(pos)
       
  1075     Q_UNUSED(hitItem)
       
  1076     Q_UNUSED(hitItemIndex)
       
  1077 }
       
  1078 
       
  1079 void HgContainer::handleLongTapAction(const QPointF& pos, HgWidgetItem* hitItem, int hitItemIndex)
       
  1080 {
       
  1081     Q_UNUSED(pos)
       
  1082     Q_UNUSED(hitItem)
       
  1083     Q_UNUSED(hitItemIndex)
       
  1084 }
       
  1085 
       
  1086 void HgContainer::onScrollPositionChanged(qreal pos)
       
  1087 {
       
  1088     Q_UNUSED(pos)
       
  1089 }
       
  1090 
       
  1091 void HgContainer::onScrollingStarted()
       
  1092 {
       
  1093     // By default do nothing
       
  1094 }
       
  1095 
       
  1096 void HgContainer::onScrollingEnded()
       
  1097 {
       
  1098     // By default do nothing
       
  1099 }
       
  1100 
       
  1101 void HgContainer::setDefaultImage(QImage defaultImage)
       
  1102 {
       
  1103     HgQuadRenderer *renderer = mRenderer->getRenderer();
       
  1104     if (renderer) {
       
  1105         QImage scaled = defaultImage.scaled(mRenderer->getImageSize().toSize());
       
  1106         renderer->setDefaultImage(scaled);
       
  1107     }
       
  1108 }
       
  1109 
       
  1110 void HgContainer::setItemSizePolicy(HgWidget::ItemSizePolicy policy)
       
  1111 {
       
  1112     if (policy != mItemSizePolicy)
       
  1113     {
       
  1114         mItemSizePolicy = policy;
       
  1115         
       
  1116         updateItemSizeAndSpacing();
       
  1117     }
       
  1118 }
       
  1119 
       
  1120 HgWidget::ItemSizePolicy HgContainer::itemSizePolicy() const
       
  1121 {
       
  1122     return mItemSizePolicy;
       
  1123 }
       
  1124 
       
  1125 void HgContainer::setItemSize(const QSizeF& size)
       
  1126 {
       
  1127     mUserItemSize = size;
       
  1128     updateItemSizeAndSpacing();
       
  1129 }
       
  1130 
       
  1131 QSizeF HgContainer::itemSize() const
       
  1132 {
       
  1133     return mRenderer->getImageSize();
       
  1134 }
       
  1135 
       
  1136 void HgContainer::setItemSpacing(const QSizeF& spacing)
       
  1137 {
       
  1138     mUserItemSpacing = spacing;
       
  1139     updateItemSizeAndSpacing();
       
  1140 }
       
  1141 
       
  1142 QSizeF HgContainer::itemSpacing() const
       
  1143 {
       
  1144     return mRenderer->getSpacing();
       
  1145 }
       
  1146 
       
  1147 void HgContainer::updateItemSizeAndSpacing()
       
  1148 {    
       
  1149     if (mItemSizePolicy == HgWidget::ItemSizeUserDefined)
       
  1150     {
       
  1151         mRenderer->setImageSize(mUserItemSize);
       
  1152         mRenderer->setSpacing(mUserItemSpacing);
       
  1153     }
       
  1154 }
       
  1155 
       
  1156 QSizeF HgContainer::getAutoItemSize() const
       
  1157 {
       
  1158     return mUserItemSize;
       
  1159 }
       
  1160 
       
  1161 QSizeF HgContainer::getAutoItemSpacing() const
       
  1162 {
       
  1163     return mUserItemSpacing;
       
  1164 }
       
  1165 
       
  1166 Qt::Orientation HgContainer::scrollDirection() const
       
  1167 {
       
  1168     return mRenderer->getScrollDirection();
       
  1169 }
       
  1170 
       
  1171 qreal HgContainer::scrollPosition() const
       
  1172 {
       
  1173     return mSpring.pos().x();
       
  1174 }
       
  1175 
       
  1176 // EOF