calendarui/views/dayview/src/calendaycontentscrollarea.cpp
changeset 45 b6db4fd4947b
child 55 2c54b51f39c4
child 58 ef813d54df51
equal deleted inserted replaced
23:fd30d51f876b 45:b6db4fd4947b
       
     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:  CalenDayContentScrollArea implementation.
       
    15  *
       
    16  */
       
    17 
       
    18 // System includes
       
    19 #include <QGraphicsLinearLayout>
       
    20 #include <QGesture>
       
    21 
       
    22 #include <hbinstance.h>
       
    23 #include <hbswipegesture.h>
       
    24 
       
    25 // User includes
       
    26 #include "calendaycontentscrollarea.h"
       
    27 #include "calendayutils.h"
       
    28 
       
    29 /*!
       
    30  \class CalenDayContentScrollArea
       
    31  \brief Scrollable container class for content widgets.
       
    32  
       
    33  It handles horizontal scrolling and swipe or pan gestures.
       
    34  */
       
    35 
       
    36 /*!
       
    37  \brief Constructor
       
    38  
       
    39  Configures scroll area settings and resets internal stares of widget.
       
    40  Gets the width of device.
       
    41  
       
    42  \param parent The parent of scroll area widget
       
    43  */
       
    44 CalenDayContentScrollArea::CalenDayContentScrollArea(QGraphicsItem *parent) :
       
    45     HbScrollArea(parent), mPanDayDirection(ECalenPanNotSet), mIsMoving(false),
       
    46     mMoveDirection(ECalenScrollNoDayChange)
       
    47 {
       
    48     // Set scroll settings
       
    49     setScrollDirections(Qt::Horizontal);
       
    50     setClampingStyle(StrictClamping);
       
    51     setHorizontalScrollBarPolicy(HbScrollArea::ScrollBarAlwaysOff);
       
    52     
       
    53 #ifdef CALENDAYVIEW_PANNING_ENABLED
       
    54     grabGesture(Qt::PanGesture);
       
    55     ungrabGesture(Qt::SwipeGesture);
       
    56 #else
       
    57     grabGesture(Qt::SwipeGesture);
       
    58     ungrabGesture(Qt::PanGesture);
       
    59 #endif
       
    60 
       
    61     // Get the width of content area and orientation of screen
       
    62     mContentWidth = CalenDayUtils::instance()->contentWidth();
       
    63     mOrientation = CalenDayUtils::instance()->orientation();
       
    64 
       
    65     // Connect to main window's orientationChanged SIGNAL to handle orientation
       
    66     // switching
       
    67     connect(CalenDayUtils::instance()->mainWindow(), 
       
    68         SIGNAL(orientationChanged(Qt::Orientation)), this,
       
    69         SLOT(orientationChanged(Qt::Orientation)));
       
    70 }
       
    71 
       
    72 /*!
       
    73  \brief Destructor
       
    74  */
       
    75 CalenDayContentScrollArea::~CalenDayContentScrollArea()
       
    76 {
       
    77 }
       
    78 
       
    79 /*!
       
    80  \brief Scrolls to middle widget.
       
    81  
       
    82  Scrolling to middle widget is done if needed.
       
    83  Resets internal pan direction flag.
       
    84  */
       
    85 void CalenDayContentScrollArea::scrollToMiddleWidget()
       
    86 {
       
    87     QPointF currentPosition = contentWidget()->pos();
       
    88     QPointF destPosition = QPointF(mContentWidth, currentPosition.y());
       
    89 
       
    90     // Scroll only when x position is wrong
       
    91     if (currentPosition.x() != destPosition.x()) {
       
    92         scrollContentsTo(QPointF(mContentWidth, currentPosition.y()), 0);
       
    93     }
       
    94 
       
    95     // Reset pan direction flag and scrolling flag
       
    96     mPanDayDirection = ECalenPanNotSet;
       
    97 }
       
    98 
       
    99 /*!
       
   100  \brief Scrolls the view by the amount indicated by "delta".
       
   101  
       
   102  Checks the direction of pan gesture and promotes leading direction.
       
   103  
       
   104  \param delta Move offset
       
   105  \return Returns TRUE if the view was able to scroll, FALSE otherwise
       
   106  */
       
   107 bool CalenDayContentScrollArea::scrollByAmount(const QPointF &delta)
       
   108 {
       
   109     QPointF newDelta(delta);
       
   110     if (mPanDayDirection == ECalenPanVertical) {
       
   111         newDelta.setX(0);
       
   112     }
       
   113     else
       
   114         if (mPanDayDirection == ECalenPanHorizontal) {
       
   115             newDelta.setY(0);
       
   116         }
       
   117         else {
       
   118             // Pan direction not set
       
   119         }
       
   120 
       
   121     return HbScrollArea::scrollByAmount(newDelta);
       
   122 }
       
   123 
       
   124 /*!
       
   125  \brief Handles pan gesture event (horizontal) or swipe gesture. 
       
   126  
       
   127  Ignores vertical pan gestures.
       
   128  
       
   129  \param event Gesture event to be handled
       
   130  */
       
   131 void CalenDayContentScrollArea::gestureEvent(QGestureEvent *event)
       
   132 {
       
   133 #ifdef CALENDAYVIEW_PANNING_ENABLED
       
   134     // Process a pan gesture event
       
   135     if (QPanGesture *panGesture = qobject_cast<QPanGesture*> (event->gesture(
       
   136         Qt::PanGesture))) {
       
   137 
       
   138         // Checks pan gesture direction
       
   139         checkPanDirection(panGesture);
       
   140 
       
   141         // Put the gesture forward before working with finished gesture
       
   142         HbScrollArea::gestureEvent(event);
       
   143 
       
   144         // If gesture is finished move the scroll area to next or previous 
       
   145         // widget or resume to gesture start point
       
   146         if (panGesture->state() == Qt::GestureFinished) {
       
   147             // Pan direction should be reseted when scrolling ends
       
   148 
       
   149             // Gets the offset of pan gesture.
       
   150             QPointF offset = panGesture->offset();
       
   151 
       
   152             // Note: in horizontal orientation x should is treaten as Y, y as X.
       
   153             QPointF movement;
       
   154             if (mOrientation == Qt::Vertical) {
       
   155                 movement = offset;
       
   156             }
       
   157             else {
       
   158                 movement.setX(offset.y());
       
   159                 movement.setY(offset.x());
       
   160             }
       
   161 
       
   162             // Gesture was long enough for place movement
       
   163             if (qAbs(movement.x()) > (KCalenHScrollMoveParam * mContentWidth / 100)) {
       
   164                 if (movement.x() < 0) {
       
   165                     mMoveDirection = ECalenScrollToNext;
       
   166                     moveTo(QPointF((-mStartPosition.x() + mContentWidth),
       
   167                         -mStartPosition.y()), KCalenScrollDaysTimeout);
       
   168                 }
       
   169                 else {
       
   170                     mMoveDirection = ECalenScrollToPrev;
       
   171                     moveTo(QPointF(-mStartPosition.x() - mContentWidth,
       
   172                         -mStartPosition.y()), KCalenScrollDaysTimeout);
       
   173                 }
       
   174             }
       
   175             // Gesture was short one, reset to gesture start point
       
   176             else {
       
   177                 qreal startPos = mStartPosition.x();
       
   178                 bool isNegative = false;
       
   179                 if (startPos < 0) {
       
   180                     isNegative = true;
       
   181                 }
       
   182                 startPos = qAbs(startPos);
       
   183                 qreal normalizeValue = mContentWidth / 2;
       
   184 
       
   185                 while (startPos > normalizeValue) {
       
   186                     normalizeValue += mContentWidth;
       
   187                 }
       
   188 
       
   189                 if (isNegative) {
       
   190                     mStartPosition.setX(-(normalizeValue - (mContentWidth / 2)));
       
   191                 }
       
   192                 else {
       
   193                     mStartPosition.setX(normalizeValue - (mContentWidth / 2));
       
   194                 }
       
   195 
       
   196                 mMoveDirection = ECalenScrollNoDayChange;
       
   197                 moveTo(-mStartPosition, KCalenScrollDaysTimeout);
       
   198             }
       
   199         }
       
   200     }
       
   201     else {
       
   202         HbScrollArea::gestureEvent(event);
       
   203     }
       
   204 #else
       
   205     // Let the content scroll area ignore pan gestures
       
   206     if (QPanGesture *panGesture = qobject_cast<QPanGesture *> (event->gesture(
       
   207                 Qt::PanGesture))) {
       
   208         // do nothing with pan gesture
       
   209     }
       
   210 
       
   211     if (HbSwipeGesture *swipeGesture =
       
   212         qobject_cast<HbSwipeGesture *> (event->gesture(Qt::SwipeGesture))) {
       
   213         if (swipeGesture->state() == Qt::GestureStarted) {
       
   214             mStartPosition = contentWidget()->pos();
       
   215             
       
   216             qreal swipeAngle = swipeGesture->sceneSwipeAngle();
       
   217             if (CalenDayUtils::instance()->isHorizontalSwipe(swipeAngle)) {
       
   218                 if (QSwipeGesture::Left == 
       
   219                     swipeGesture->sceneHorizontalDirection()) {
       
   220                     mMoveDirection = ECalenScrollToNext;
       
   221                     moveTo(QPointF((-mStartPosition.x() + mContentWidth),
       
   222                             -mStartPosition.y()), KCalenScrollDaysTimeout);
       
   223                 }
       
   224                 else if (QSwipeGesture::Right == 
       
   225                     swipeGesture->sceneHorizontalDirection()) {
       
   226                     mMoveDirection = ECalenScrollToPrev;
       
   227                     moveTo(QPointF(-mStartPosition.x() - mContentWidth,
       
   228                             -mStartPosition.y()), KCalenScrollDaysTimeout);
       
   229                 }
       
   230             }
       
   231         }
       
   232     }
       
   233 #endif
       
   234 }
       
   235 
       
   236 /*!
       
   237  \brief Filters pan gesture events.
       
   238  
       
   239  Filters events if this object has been installed as an event filter for
       
   240  the watched object. Handles horizontal pan gestures (ignores vertical).
       
   241  When moving scroll area all gesture events are blocked.
       
   242  
       
   243  \param obj Watched object
       
   244  \param event Event to be filtered
       
   245  \return Returns TRUE if event was handled. FALSE otherwise.
       
   246  */
       
   247 bool CalenDayContentScrollArea::eventFilter(QObject *obj, QEvent *event)
       
   248 {
       
   249     Q_UNUSED(obj);
       
   250 
       
   251     bool handled = false;
       
   252 
       
   253     // Check if we get a gesture event
       
   254     if (event->type() == QEvent::Gesture) {
       
   255 
       
   256         // Blocks handling of gesture events if scrolling started by 
       
   257         // pan gesture is in progress
       
   258         if (mIsMoving) {
       
   259             handled = true;
       
   260         }
       
   261         else {
       
   262             QGestureEvent* gesture = static_cast<QGestureEvent*> (event);
       
   263 
       
   264             // Check if we get a pan gesture
       
   265             QPanGesture *panGesture = qobject_cast<QPanGesture*> (
       
   266                 gesture->gesture(Qt::PanGesture));
       
   267             if (panGesture) {
       
   268                 checkPanDirection(panGesture);
       
   269                 if (mPanDayDirection == ECalenPanHorizontal) {
       
   270                     gestureEvent(gesture);
       
   271                     handled = true;
       
   272                 }
       
   273             }
       
   274         }
       
   275     }
       
   276 
       
   277     return handled;
       
   278 }
       
   279 
       
   280 /*!
       
   281  \brief Overriden event handler.
       
   282  
       
   283  Handles events:
       
   284  - gesture/focus events blocked when horizontal scrolling is in progress
       
   285  - layout request event - scrolls to middle widget if current position is wrong
       
   286  
       
   287  \param e Event to be handled
       
   288  \return Returns TRUE if event was handled. FALSE otherwise.
       
   289  */
       
   290 bool CalenDayContentScrollArea::event(QEvent *e)
       
   291 {
       
   292     bool result = false;
       
   293 
       
   294     // Blocks base class handler for certain events if scrolling started
       
   295     // by pan gesture is in progress
       
   296     if (mIsMoving && (e->type() == QEvent::Gesture || e->type()
       
   297         == QEvent::GestureOverride || e->type() == QEvent::FocusOut
       
   298         || e->type() == QEvent::FocusIn)) {
       
   299         result = true;
       
   300     }
       
   301     if (!result) {
       
   302         // Call base class handler
       
   303         result = HbScrollArea::event(e);
       
   304         
       
   305         // Scroll to middle widget when layout request
       
   306         if (e->type() == QEvent::LayoutRequest) {
       
   307             scrollToMiddleWidget();
       
   308         }  
       
   309     }
       
   310 
       
   311     return result;
       
   312 }
       
   313 
       
   314 /*!
       
   315  \brief Checks the direction of pan gesture.
       
   316  
       
   317  Changes the scrolling style according to movement direction, 
       
   318  stores the orientation of the pan gesture.
       
   319  Function is used when switching widgets by panning gesture is enabled.
       
   320  
       
   321  \param panGesture Pan gesture event
       
   322  */
       
   323 void CalenDayContentScrollArea::checkPanDirection(QPanGesture *panGesture)
       
   324 {
       
   325     // Gets the offset of pan gesture.
       
   326     QPointF offset = panGesture->offset();
       
   327 
       
   328     // Note: in horizontal orientation x should is treaten as Y, y as X.
       
   329     QPointF movement;
       
   330     if (mOrientation == Qt::Vertical) {
       
   331         movement = offset;
       
   332     }
       
   333     else {
       
   334         movement.setX(offset.y());
       
   335         movement.setY(offset.x());
       
   336     }
       
   337 
       
   338     // If gesture is started check leading movement direction
       
   339     if (panGesture->state() == Qt::GestureStarted) {
       
   340         if (qAbs(movement.x()) < qAbs(movement.y())) {
       
   341             mPanDayDirection = ECalenPanVertical;
       
   342         }
       
   343         else {
       
   344             mStartPosition = contentWidget()->pos();
       
   345             mPanDayDirection = ECalenPanHorizontal;
       
   346         }
       
   347     }
       
   348 }
       
   349 
       
   350 /*!
       
   351  \brief Scrolls the contents to the newPosition in a given time.
       
   352  
       
   353  Sets the flag to indicate that scrolling is in progress. Use this function
       
   354  for scrolling with timeout > 0 to block gesture and focus events during
       
   355  scroll area movement.
       
   356  
       
   357  \param newPosition Destination position
       
   358  \param time Time of scroll movement
       
   359  */
       
   360 void CalenDayContentScrollArea::moveTo(const QPointF &newPosition, int time)
       
   361 {
       
   362     // Connect to scrollingEnded SIGNAL to get feedback when scrolling ends
       
   363     connect(this, SIGNAL(scrollingEnded()), this, SLOT(moveFinished()));
       
   364     
       
   365     // Scroll the content to new position and set isMoving flag
       
   366     scrollContentsTo(newPosition, time);
       
   367     mIsMoving = true;
       
   368     
       
   369     // Emit signal that moving has just started
       
   370     if (mMoveDirection != ECalenScrollNoDayChange) {
       
   371         emit scrollAreaMoveStarted(mMoveDirection);
       
   372     }
       
   373 }
       
   374 
       
   375 /*!
       
   376  \brief Slot which is called when moving of scroll area is finished.
       
   377  
       
   378  Resets internal isMoving flag.
       
   379  */
       
   380 void CalenDayContentScrollArea::moveFinished()
       
   381 {
       
   382     // Disconnect from signal, move is finished now
       
   383     disconnect(this, SIGNAL(scrollingEnded()), this, SLOT(moveFinished()));
       
   384     mIsMoving = false;
       
   385     
       
   386     // Emit signal that moving has just finished and reset direction
       
   387     if (mMoveDirection != ECalenScrollNoDayChange) {
       
   388         emit scrollAreaMoveFinished(mMoveDirection);
       
   389         mMoveDirection = ECalenScrollNoDayChange;
       
   390     }
       
   391 }
       
   392 
       
   393 /*!
       
   394  \brief Slot which is called whenever the orientation of the device changes.
       
   395  
       
   396  Stores screen width and orientation in private members.
       
   397  
       
   398  \param orientation Current device orientation
       
   399  */
       
   400 void CalenDayContentScrollArea::orientationChanged(Qt::Orientation orientation)
       
   401 {
       
   402     // Update the width of content area
       
   403     mContentWidth = CalenDayUtils::instance()->contentWidth();
       
   404     mOrientation = orientation;
       
   405     
       
   406     // Reset flag related to moving
       
   407     mPanDayDirection = ECalenPanNotSet;
       
   408     mMoveDirection = ECalenScrollNoDayChange;
       
   409     mIsMoving = false;
       
   410 }
       
   411 
       
   412 // End of File