calendarui/views/dayview/src/calendaycontentscrollarea.cpp
changeset 45 b6db4fd4947b
child 55 2c54b51f39c4
child 58 ef813d54df51
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/calendarui/views/dayview/src/calendaycontentscrollarea.cpp	Mon Jun 28 15:22:02 2010 +0530
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Nokia Corporation - initial contribution.
+ *
+ * Contributors:
+ *
+ * Description:  CalenDayContentScrollArea implementation.
+ *
+ */
+
+// System includes
+#include <QGraphicsLinearLayout>
+#include <QGesture>
+
+#include <hbinstance.h>
+#include <hbswipegesture.h>
+
+// User includes
+#include "calendaycontentscrollarea.h"
+#include "calendayutils.h"
+
+/*!
+ \class CalenDayContentScrollArea
+ \brief Scrollable container class for content widgets.
+ 
+ It handles horizontal scrolling and swipe or pan gestures.
+ */
+
+/*!
+ \brief Constructor
+ 
+ Configures scroll area settings and resets internal stares of widget.
+ Gets the width of device.
+ 
+ \param parent The parent of scroll area widget
+ */
+CalenDayContentScrollArea::CalenDayContentScrollArea(QGraphicsItem *parent) :
+    HbScrollArea(parent), mPanDayDirection(ECalenPanNotSet), mIsMoving(false),
+    mMoveDirection(ECalenScrollNoDayChange)
+{
+    // Set scroll settings
+    setScrollDirections(Qt::Horizontal);
+    setClampingStyle(StrictClamping);
+    setHorizontalScrollBarPolicy(HbScrollArea::ScrollBarAlwaysOff);
+    
+#ifdef CALENDAYVIEW_PANNING_ENABLED
+    grabGesture(Qt::PanGesture);
+    ungrabGesture(Qt::SwipeGesture);
+#else
+    grabGesture(Qt::SwipeGesture);
+    ungrabGesture(Qt::PanGesture);
+#endif
+
+    // Get the width of content area and orientation of screen
+    mContentWidth = CalenDayUtils::instance()->contentWidth();
+    mOrientation = CalenDayUtils::instance()->orientation();
+
+    // Connect to main window's orientationChanged SIGNAL to handle orientation
+    // switching
+    connect(CalenDayUtils::instance()->mainWindow(), 
+        SIGNAL(orientationChanged(Qt::Orientation)), this,
+        SLOT(orientationChanged(Qt::Orientation)));
+}
+
+/*!
+ \brief Destructor
+ */
+CalenDayContentScrollArea::~CalenDayContentScrollArea()
+{
+}
+
+/*!
+ \brief Scrolls to middle widget.
+ 
+ Scrolling to middle widget is done if needed.
+ Resets internal pan direction flag.
+ */
+void CalenDayContentScrollArea::scrollToMiddleWidget()
+{
+    QPointF currentPosition = contentWidget()->pos();
+    QPointF destPosition = QPointF(mContentWidth, currentPosition.y());
+
+    // Scroll only when x position is wrong
+    if (currentPosition.x() != destPosition.x()) {
+        scrollContentsTo(QPointF(mContentWidth, currentPosition.y()), 0);
+    }
+
+    // Reset pan direction flag and scrolling flag
+    mPanDayDirection = ECalenPanNotSet;
+}
+
+/*!
+ \brief Scrolls the view by the amount indicated by "delta".
+ 
+ Checks the direction of pan gesture and promotes leading direction.
+ 
+ \param delta Move offset
+ \return Returns TRUE if the view was able to scroll, FALSE otherwise
+ */
+bool CalenDayContentScrollArea::scrollByAmount(const QPointF &delta)
+{
+    QPointF newDelta(delta);
+    if (mPanDayDirection == ECalenPanVertical) {
+        newDelta.setX(0);
+    }
+    else
+        if (mPanDayDirection == ECalenPanHorizontal) {
+            newDelta.setY(0);
+        }
+        else {
+            // Pan direction not set
+        }
+
+    return HbScrollArea::scrollByAmount(newDelta);
+}
+
+/*!
+ \brief Handles pan gesture event (horizontal) or swipe gesture. 
+ 
+ Ignores vertical pan gestures.
+ 
+ \param event Gesture event to be handled
+ */
+void CalenDayContentScrollArea::gestureEvent(QGestureEvent *event)
+{
+#ifdef CALENDAYVIEW_PANNING_ENABLED
+    // Process a pan gesture event
+    if (QPanGesture *panGesture = qobject_cast<QPanGesture*> (event->gesture(
+        Qt::PanGesture))) {
+
+        // Checks pan gesture direction
+        checkPanDirection(panGesture);
+
+        // Put the gesture forward before working with finished gesture
+        HbScrollArea::gestureEvent(event);
+
+        // If gesture is finished move the scroll area to next or previous 
+        // widget or resume to gesture start point
+        if (panGesture->state() == Qt::GestureFinished) {
+            // Pan direction should be reseted when scrolling ends
+
+            // Gets the offset of pan gesture.
+            QPointF offset = panGesture->offset();
+
+            // Note: in horizontal orientation x should is treaten as Y, y as X.
+            QPointF movement;
+            if (mOrientation == Qt::Vertical) {
+                movement = offset;
+            }
+            else {
+                movement.setX(offset.y());
+                movement.setY(offset.x());
+            }
+
+            // Gesture was long enough for place movement
+            if (qAbs(movement.x()) > (KCalenHScrollMoveParam * mContentWidth / 100)) {
+                if (movement.x() < 0) {
+                    mMoveDirection = ECalenScrollToNext;
+                    moveTo(QPointF((-mStartPosition.x() + mContentWidth),
+                        -mStartPosition.y()), KCalenScrollDaysTimeout);
+                }
+                else {
+                    mMoveDirection = ECalenScrollToPrev;
+                    moveTo(QPointF(-mStartPosition.x() - mContentWidth,
+                        -mStartPosition.y()), KCalenScrollDaysTimeout);
+                }
+            }
+            // Gesture was short one, reset to gesture start point
+            else {
+                qreal startPos = mStartPosition.x();
+                bool isNegative = false;
+                if (startPos < 0) {
+                    isNegative = true;
+                }
+                startPos = qAbs(startPos);
+                qreal normalizeValue = mContentWidth / 2;
+
+                while (startPos > normalizeValue) {
+                    normalizeValue += mContentWidth;
+                }
+
+                if (isNegative) {
+                    mStartPosition.setX(-(normalizeValue - (mContentWidth / 2)));
+                }
+                else {
+                    mStartPosition.setX(normalizeValue - (mContentWidth / 2));
+                }
+
+                mMoveDirection = ECalenScrollNoDayChange;
+                moveTo(-mStartPosition, KCalenScrollDaysTimeout);
+            }
+        }
+    }
+    else {
+        HbScrollArea::gestureEvent(event);
+    }
+#else
+    // Let the content scroll area ignore pan gestures
+    if (QPanGesture *panGesture = qobject_cast<QPanGesture *> (event->gesture(
+                Qt::PanGesture))) {
+        // do nothing with pan gesture
+    }
+
+    if (HbSwipeGesture *swipeGesture =
+        qobject_cast<HbSwipeGesture *> (event->gesture(Qt::SwipeGesture))) {
+        if (swipeGesture->state() == Qt::GestureStarted) {
+            mStartPosition = contentWidget()->pos();
+            
+            qreal swipeAngle = swipeGesture->sceneSwipeAngle();
+            if (CalenDayUtils::instance()->isHorizontalSwipe(swipeAngle)) {
+                if (QSwipeGesture::Left == 
+                    swipeGesture->sceneHorizontalDirection()) {
+                    mMoveDirection = ECalenScrollToNext;
+                    moveTo(QPointF((-mStartPosition.x() + mContentWidth),
+                            -mStartPosition.y()), KCalenScrollDaysTimeout);
+                }
+                else if (QSwipeGesture::Right == 
+                    swipeGesture->sceneHorizontalDirection()) {
+                    mMoveDirection = ECalenScrollToPrev;
+                    moveTo(QPointF(-mStartPosition.x() - mContentWidth,
+                            -mStartPosition.y()), KCalenScrollDaysTimeout);
+                }
+            }
+        }
+    }
+#endif
+}
+
+/*!
+ \brief Filters pan gesture events.
+ 
+ Filters events if this object has been installed as an event filter for
+ the watched object. Handles horizontal pan gestures (ignores vertical).
+ When moving scroll area all gesture events are blocked.
+ 
+ \param obj Watched object
+ \param event Event to be filtered
+ \return Returns TRUE if event was handled. FALSE otherwise.
+ */
+bool CalenDayContentScrollArea::eventFilter(QObject *obj, QEvent *event)
+{
+    Q_UNUSED(obj);
+
+    bool handled = false;
+
+    // Check if we get a gesture event
+    if (event->type() == QEvent::Gesture) {
+
+        // Blocks handling of gesture events if scrolling started by 
+        // pan gesture is in progress
+        if (mIsMoving) {
+            handled = true;
+        }
+        else {
+            QGestureEvent* gesture = static_cast<QGestureEvent*> (event);
+
+            // Check if we get a pan gesture
+            QPanGesture *panGesture = qobject_cast<QPanGesture*> (
+                gesture->gesture(Qt::PanGesture));
+            if (panGesture) {
+                checkPanDirection(panGesture);
+                if (mPanDayDirection == ECalenPanHorizontal) {
+                    gestureEvent(gesture);
+                    handled = true;
+                }
+            }
+        }
+    }
+
+    return handled;
+}
+
+/*!
+ \brief Overriden event handler.
+ 
+ Handles events:
+ - gesture/focus events blocked when horizontal scrolling is in progress
+ - layout request event - scrolls to middle widget if current position is wrong
+ 
+ \param e Event to be handled
+ \return Returns TRUE if event was handled. FALSE otherwise.
+ */
+bool CalenDayContentScrollArea::event(QEvent *e)
+{
+    bool result = false;
+
+    // Blocks base class handler for certain events if scrolling started
+    // by pan gesture is in progress
+    if (mIsMoving && (e->type() == QEvent::Gesture || e->type()
+        == QEvent::GestureOverride || e->type() == QEvent::FocusOut
+        || e->type() == QEvent::FocusIn)) {
+        result = true;
+    }
+    if (!result) {
+        // Call base class handler
+        result = HbScrollArea::event(e);
+        
+        // Scroll to middle widget when layout request
+        if (e->type() == QEvent::LayoutRequest) {
+            scrollToMiddleWidget();
+        }  
+    }
+
+    return result;
+}
+
+/*!
+ \brief Checks the direction of pan gesture.
+ 
+ Changes the scrolling style according to movement direction, 
+ stores the orientation of the pan gesture.
+ Function is used when switching widgets by panning gesture is enabled.
+ 
+ \param panGesture Pan gesture event
+ */
+void CalenDayContentScrollArea::checkPanDirection(QPanGesture *panGesture)
+{
+    // Gets the offset of pan gesture.
+    QPointF offset = panGesture->offset();
+
+    // Note: in horizontal orientation x should is treaten as Y, y as X.
+    QPointF movement;
+    if (mOrientation == Qt::Vertical) {
+        movement = offset;
+    }
+    else {
+        movement.setX(offset.y());
+        movement.setY(offset.x());
+    }
+
+    // If gesture is started check leading movement direction
+    if (panGesture->state() == Qt::GestureStarted) {
+        if (qAbs(movement.x()) < qAbs(movement.y())) {
+            mPanDayDirection = ECalenPanVertical;
+        }
+        else {
+            mStartPosition = contentWidget()->pos();
+            mPanDayDirection = ECalenPanHorizontal;
+        }
+    }
+}
+
+/*!
+ \brief Scrolls the contents to the newPosition in a given time.
+ 
+ Sets the flag to indicate that scrolling is in progress. Use this function
+ for scrolling with timeout > 0 to block gesture and focus events during
+ scroll area movement.
+ 
+ \param newPosition Destination position
+ \param time Time of scroll movement
+ */
+void CalenDayContentScrollArea::moveTo(const QPointF &newPosition, int time)
+{
+    // Connect to scrollingEnded SIGNAL to get feedback when scrolling ends
+    connect(this, SIGNAL(scrollingEnded()), this, SLOT(moveFinished()));
+    
+    // Scroll the content to new position and set isMoving flag
+    scrollContentsTo(newPosition, time);
+    mIsMoving = true;
+    
+    // Emit signal that moving has just started
+    if (mMoveDirection != ECalenScrollNoDayChange) {
+        emit scrollAreaMoveStarted(mMoveDirection);
+    }
+}
+
+/*!
+ \brief Slot which is called when moving of scroll area is finished.
+ 
+ Resets internal isMoving flag.
+ */
+void CalenDayContentScrollArea::moveFinished()
+{
+    // Disconnect from signal, move is finished now
+    disconnect(this, SIGNAL(scrollingEnded()), this, SLOT(moveFinished()));
+    mIsMoving = false;
+    
+    // Emit signal that moving has just finished and reset direction
+    if (mMoveDirection != ECalenScrollNoDayChange) {
+        emit scrollAreaMoveFinished(mMoveDirection);
+        mMoveDirection = ECalenScrollNoDayChange;
+    }
+}
+
+/*!
+ \brief Slot which is called whenever the orientation of the device changes.
+ 
+ Stores screen width and orientation in private members.
+ 
+ \param orientation Current device orientation
+ */
+void CalenDayContentScrollArea::orientationChanged(Qt::Orientation orientation)
+{
+    // Update the width of content area
+    mContentWidth = CalenDayUtils::instance()->contentWidth();
+    mOrientation = orientation;
+    
+    // Reset flag related to moving
+    mPanDayDirection = ECalenPanNotSet;
+    mMoveDirection = ECalenScrollNoDayChange;
+    mIsMoving = false;
+}
+
+// End of File