homescreenapp/stateplugins/hshomescreenstateplugin/src/hsidlestate.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 03 May 2010 12:24:59 +0300
changeset 39 4e8ebe173323
parent 36 cdae8c6c3876
child 46 23b5d6a29cce
permissions -rw-r--r--
Revision: 201015 Kit: 201018

/*
* Copyright (c) 2009 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:
*
*/

#include <QStateMachine>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsLinearLayout>
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>
#include <QApplication>
#include <QDir>

#include <HbMainWindow>
#include <HbView>
#include <HbMenu>
#include <HbAction>
#include <HbIcon>
#include <HbMessageBox>
#include <HbLabel>

#include "hsidlestate.h"
#include "hsidlewidget.h"
#include "hsdomainmodeldatastructures.h"
#include "hsscene.h"
#include "hspage.h"
#include "hswidgethost.h"
#include "hswallpaper.h"
#include "hsselectbackgroundstate.h"
#include "hstrashbinwidget.h"
#include "hspageindicator.h"
#include "hswidgetpositioningonorientationchange.h"
#include "hsmenueventfactory.h"
#include "hshomescreenstatecommon.h"
#include "hstitleresolver.h"
#include "hsmenuservice.h"

// Helper macros for connecting state entry and exit actions.
#define ENTRY_ACTION(state, action) \
    connect(state, SIGNAL(entered()), SLOT(action()));
#define EXIT_ACTION(state, action) \
    connect(state, SIGNAL(exited()), SLOT(action()));

// Helper macros for connecting and disconnecting mouse event handlers.
#define CONNECT_MOUSE_EVENT_HANDLER(signal, slot) \
    connect(mUiWidget, SIGNAL(signal(QGraphicsItem*,QGraphicsSceneMouseEvent*,bool&)), \
        SLOT(slot(QGraphicsItem*,QGraphicsSceneMouseEvent*,bool&)));
#define DISCONNECT_MOUSE_EVENT_HANDLER(signal, slot) \
    disconnect(mUiWidget, SIGNAL(signal(QGraphicsItem*,QGraphicsSceneMouseEvent*,bool&)), \
        this, SLOT(slot(QGraphicsItem*,QGraphicsSceneMouseEvent*,bool&)));


namespace
{
    const char gApplicationLibraryIconName[] = "qtg_mono_applications_all";

    //User adds a new page to home screen
    const char hsLocTextId_OptionsMenu_AddPage[] = "txt_homescreen_opt_add_page";

    //User removes page from home screen
    const char hsLocTextId_OptionsMenu_RemovePage[] = "txt_homescreen_opt_remove_page";

    //Sends request to all widgets to enable data connections
    const char hsLocTextId_OptionsMenu_HsToOnline[] = "txt_homescreen_opt_home_screen_to_online";

    //Sends request to all widgets to disable data connections
    const char hsLocTextId_OptionsMenu_HsToOffline[] = "txt_homescreen_opt_home_screen_to_offline";

    //Home screen canvas menu item for opening picture gallery
    const char hsLocTextId_ContextMenu_ChangeWallpaper[] = "txt_homescreen_list_change_wallpaper";

    //Home screen canvas menu item for opening Application library
    const char hsLocTextId_ContextMenu_AddContent[] = "txt_homescreen_list_add_content";

	//Home screen options menu item for opening Task switcher
    const char hsLocTextId_OptionsMenu_TaskSwitcher[] = "txt_homescreen_opt_task_switcher";
    
    //Heading text in confirmation dialog while removing page with content
    const char hsLocTextId_Title_RemovePage[] = "txt_homescreen_title_remove_page";
    
    //Text in confirmation dialog while removing page with content
    const char hsLocTextId_Confirmation_RemovePage[] = "txt_homescreen_info_page_and_content_will_be_remov";
    
    //Button in confirmation dialog while removing page with content
    const char hsLocTextId_ConfirmationButton_Ok[] = "txt_homescreen_button_ok";
    
    //Button in confirmation dialog while removing page with content
    const char hsLocTextId_ConfirmationButton_Cancel[] = "txt_homescreen_button_cancel";
}

/*!
    \class HsIdleState
    \ingroup group_hshomescreenstateplugin
    \brief Controller part of the home screen idle state.

    Controls the home screen idle state execution. See the
    state chart below for the state structure.

    \image html hsidlestate_statechart.png
*/

/*!
    Constructs a new idle state with the given \a parent.
*/
HsIdleState::HsIdleState(QState *parent)
  : QState(parent),
    mView(0), mNavigationAction(0), mUiWidget(0),
    mTapAndHoldDistance(16),
    mPageChangeZoneWidth(60),
    mTitleResolver(0),
	mZoneAnimation(0),
	mPageChanged(false),	
	mAllowZoneAnimation(true),
	mPageChangeAnimation(0)
{
    setupStates();
    mTimer.setSingleShot(true);
    mTitleResolver = new HsTitleResolver(this);
}

/*!
    Destroys this idle state.
*/
HsIdleState::~HsIdleState()
{
    delete mZoneAnimation;    
}

/*!
    \copydoc QObject::eventFilter(QObject *watched, QEvent *event)
*/
bool HsIdleState::eventFilter(QObject *watched, QEvent *event)
{
    switch (event->type()) {
		case QEvent::ApplicationActivate:
            action_idle_layoutNewWidgets();            
            break;
        default:
            break;
	}   
	return QState::eventFilter(watched, event);
}

/*!
    \fn HsIdleState::event_applicationLibrary()

    This signal initiates a transition to application library.
*/

/*!
    \fn HsIdleState::event_waitInput()  

    This signal initiates a transition to the waitInput state.
*/

/*!
    \fn HsIdleState::event_widgetInteraction()  

    This signal initiates a transition to the widgetInteraction state.
*/

/*!
    \fn HsIdleState::event_sceneInteraction()  

    This signal initiates a transition to the sceneInteraction state.
*/

/*!
    \fn HsIdleState::event_moveWidget()  

    This signal initiates a transition to the moveWidget state.
*/

/*!
    \fn HsIdleState::event_moveScene()  

    This signal initiates a transition to the moveScene state.
*/

/*!
    \fn HsIdleState::event_sceneMenu()  

    This signal initiates a transition to the sceneMenu state.
*/

/*!
    \fn HsIdleState::event_selectSceneWallpaper()  

    This signal initiates a transition to the selectSceneWallpaper state.
*/

/*!
    \fn HsIdleState::event_addPage()  

    This signal initiates a transition to the addPage state.
*/

/*!
    \fn HsIdleState::event_removePage()  

    This signal initiates a transition to the removePage state.
*/

/*!
    \fn HsIdleState::event_toggleConnection()  

    This signal initiates a transition to the toggleConnection state.
*/

/*!
    Creates the internal state structure and connects
    state entry and exit actions.
*/
void HsIdleState::setupStates()
{   
    // States

    QState *state_waitInput = new QState(this);
    setInitialState(state_waitInput);
    QState *state_widgetInteraction = new QState(this);
    QState *state_sceneInteraction = new QState(this);
    QState *state_moveWidget = new QState(this);
    QState *state_moveScene = new QState(this);
    QState *state_sceneMenu = new QState(this);
    HsSelectBackgroundState *state_selectSceneWallpaper = 
        new HsSelectBackgroundState(this);
    QState *state_addPage = new QState(this);
    QState *state_removePage = new QState(this);
    QState *state_toggleConnection = new QState(this);

    // Transitions

    state_waitInput->addTransition(
        this, SIGNAL(event_widgetInteraction()), state_widgetInteraction);
    state_waitInput->addTransition(
        this, SIGNAL(event_sceneInteraction()), state_sceneInteraction);
    state_waitInput->addTransition(
        this, SIGNAL(event_addPage()), state_addPage);
    state_waitInput->addTransition(
        this, SIGNAL(event_removePage()), state_removePage);
    state_waitInput->addTransition(
        this, SIGNAL(event_toggleConnection()), state_toggleConnection);
    state_waitInput->addTransition(
        this, SIGNAL(event_selectSceneWallpaper()), state_selectSceneWallpaper);

    state_widgetInteraction->addTransition(
        this, SIGNAL(event_waitInput()), state_waitInput);
    state_widgetInteraction->addTransition(
        this, SIGNAL(event_moveWidget()), state_moveWidget);

    state_sceneInteraction->addTransition(
        this, SIGNAL(event_waitInput()), state_waitInput);
    state_sceneInteraction->addTransition(
        this, SIGNAL(event_moveScene()), state_moveScene);
    state_sceneInteraction->addTransition(
        this, SIGNAL(event_sceneMenu()), state_sceneMenu);
    
    state_moveWidget->addTransition(
        this, SIGNAL(event_waitInput()), state_waitInput);
    
    state_moveScene->addTransition(
        this, SIGNAL(event_waitInput()), state_waitInput);

    state_sceneMenu->addTransition(
        this, SIGNAL(event_waitInput()), state_waitInput);
    state_sceneMenu->addTransition(
        this, SIGNAL(event_selectSceneWallpaper()), state_selectSceneWallpaper);
    state_sceneMenu->addTransition(
        this, SIGNAL(event_addPage()), state_addPage);

    state_selectSceneWallpaper->addTransition(
        state_selectSceneWallpaper, SIGNAL(event_waitInput()), state_waitInput);

    state_addPage->addTransition(state_waitInput);

    state_removePage->addTransition(state_waitInput);

    state_toggleConnection->addTransition(state_waitInput);

    // Actions

    ENTRY_ACTION(this, action_idle_setupView)
    ENTRY_ACTION(this, action_idle_layoutNewWidgets)
    ENTRY_ACTION(this, action_idle_showActivePage)
    ENTRY_ACTION(this, action_idle_connectOrientationChangeEventHandler)
    ENTRY_ACTION(this, action_idle_setupTitle)
    ENTRY_ACTION(this, action_idle_installEventFilter)
    EXIT_ACTION(this, action_idle_cleanupTitle)
    EXIT_ACTION(this, action_idle_cleanupView)
    EXIT_ACTION(this, action_idle_disconnectOrientationChangeEventHandler)
    EXIT_ACTION(this, action_idle_uninstallEventFilter)

    ENTRY_ACTION(state_waitInput, action_waitInput_updateOptionsMenu)
    ENTRY_ACTION(state_waitInput, action_waitInput_connectMouseEventHandlers)
    EXIT_ACTION(state_waitInput, action_waitInput_disconnectMouseEventHandlers)

    ENTRY_ACTION(state_widgetInteraction, action_widgetInteraction_connectMouseEventHandlers)
    ENTRY_ACTION(state_widgetInteraction, action_widgetInteraction_connectGestureTimers)
    EXIT_ACTION(state_widgetInteraction, action_widgetInteraction_disconnectMouseEventHandlers)
    EXIT_ACTION(state_widgetInteraction, action_widgetInteraction_disconnectGestureTimers)

    ENTRY_ACTION(state_sceneInteraction, action_sceneInteraction_connectMouseEventHandlers)
    ENTRY_ACTION(state_sceneInteraction, action_sceneInteraction_connectGestureTimers)
    EXIT_ACTION(state_sceneInteraction, action_sceneInteraction_disconnectMouseEventHandlers)
    EXIT_ACTION(state_sceneInteraction, action_sceneInteraction_disconnectGestureTimers)

    ENTRY_ACTION(state_moveWidget, action_moveWidget_reparentToControlLayer)
    ENTRY_ACTION(state_moveWidget, action_moveWidget_startWidgetDragAnimation)
    ENTRY_ACTION(state_moveWidget, action_moveWidget_connectMouseEventHandlers)

    EXIT_ACTION(state_moveWidget, action_moveWidget_reparentToPage)
    EXIT_ACTION(state_moveWidget, action_moveWidget_startWidgetDropAnimation)
    EXIT_ACTION(state_moveWidget, action_moveWidget_disconnectMouseEventHandlers)

    ENTRY_ACTION(state_moveScene, action_moveScene_connectMouseEventHandlers)
    EXIT_ACTION(state_moveScene, action_moveScene_disconnectMouseEventHandlers)

    ENTRY_ACTION(state_sceneMenu, action_sceneMenu_showMenu)
    
    ENTRY_ACTION(state_addPage, action_disableUserInteraction)
    ENTRY_ACTION(state_addPage, action_addPage_addPage)
    EXIT_ACTION(state_addPage, action_enableUserInteraction)
    
    ENTRY_ACTION(state_removePage, action_disableUserInteraction)
    ENTRY_ACTION(state_removePage, action_removePage_removePage)
    EXIT_ACTION(state_removePage, action_enableUserInteraction)

    ENTRY_ACTION(state_toggleConnection, action_disableUserInteraction)
    ENTRY_ACTION(state_toggleConnection, action_toggleConnection_toggleConnection)
    EXIT_ACTION(state_toggleConnection, action_enableUserInteraction)
}

/*!
    Computes the page layer x position based on the given \a pageIndex.
*/
qreal HsIdleState::pageLayerXPos(int pageIndex) const
{
    return -pageIndex * HsScene::mainWindow()->layoutRect().width() - 0.5;
}

/*!
    Starts the page change animation based on the given \a targetPageIndex
    and \a duration.
*/
void HsIdleState::startPageChangeAnimation(int targetPageIndex, int duration)
{
    if (!mPageChangeAnimation) {
        mPageChangeAnimation = new QParallelAnimationGroup(this);

        QPropertyAnimation *animation = new QPropertyAnimation(mUiWidget->pageLayer(), "x");
        animation->setEndValue(pageLayerXPos(targetPageIndex));
        animation->setDuration(duration);
        mPageChangeAnimation->addAnimation(animation);

        animation = new QPropertyAnimation(mUiWidget->sceneLayer(), "x");
        animation->setEndValue((parallaxFactor() * pageLayerXPos(targetPageIndex)) - HSBOUNDARYEFFECT / 2);
        animation->setDuration(duration);
        mPageChangeAnimation->addAnimation(animation);
    }
    else {
        if (QAbstractAnimation::Stopped != mPageChangeAnimation->state()) {
            mPageChangeAnimation->stop();
        }
        QAbstractAnimation *animation = mPageChangeAnimation->animationAt(0);
        
        qobject_cast<QPropertyAnimation*>(animation)->setEndValue(pageLayerXPos(targetPageIndex));
        qobject_cast<QPropertyAnimation*>(animation)->setDuration(duration);

        animation = mPageChangeAnimation->animationAt(1);
        qobject_cast<QPropertyAnimation*>(animation)->setEndValue((parallaxFactor() * pageLayerXPos(targetPageIndex)) - HSBOUNDARYEFFECT / 2);
        qobject_cast<QPropertyAnimation*>(animation)->setDuration(duration);        
    }
    mPageChangeAnimation->start();
    mUiWidget->showPageIndicator();
    mUiWidget->setActivePage(targetPageIndex);
}

/*!
    Starts the page change zone animation based on the given a\ duration
*/
void HsIdleState::startPageChangeZoneAnimation(int duration)
{
    HsScene *scene = HsScene::instance();

    int targetPageIndex = scene->activePageIndex();

    if (isInLeftPageChangeZone() && 0 < targetPageIndex) {
        --targetPageIndex;
    } else if (isInRightPageChangeZone() &&
        targetPageIndex < scene->pages().count()) {
        ++targetPageIndex;
    } else {    	
		mAllowZoneAnimation = true;
        return;
    }

    if (targetPageIndex == scene->pages().count()
    	&& scene->pages().last()->widgets().isEmpty()) {
        mAllowZoneAnimation = true;
        return;        
    }
    
    if (!mZoneAnimation) {
        mZoneAnimation = new QPropertyAnimation(mUiWidget->sceneLayer(), "x");
    }

    if (isInLeftPageChangeZone()) {
        mZoneAnimation->setEndValue(((parallaxFactor() * pageLayerXPos(scene->activePageIndex()))-HSBOUNDARYEFFECT/2)+HSBOUNDARYEFFECT/2);
    } else {
        mZoneAnimation->setEndValue(((parallaxFactor() * pageLayerXPos(scene->activePageIndex()))-HSBOUNDARYEFFECT/2)-HSBOUNDARYEFFECT/2);
    }
    mZoneAnimation->setDuration(duration);
    mZoneAnimation->setDirection(QAbstractAnimation::Forward);

    connect(mZoneAnimation,
            SIGNAL(finished()),
            SLOT(zoneAnimationFinished()));
    mZoneAnimation->start();    
    mPageChanged = true;
}

/*!
    Checks if the active widget is located inside the left or right 
    page change zone.
*/
bool HsIdleState::isInPageChangeZone()
{
    qreal widgetXPos = HsScene::instance()->activeWidget()->geometry().center().x();
    qreal pageWidth = HsScene::mainWindow()->layoutRect().width();
    return widgetXPos < mPageChangeZoneWidth ||
           pageWidth - mPageChangeZoneWidth < widgetXPos;
}

/*!
    Checks if the active widget is located inside the left
    page change zone.
*/
bool HsIdleState::isInLeftPageChangeZone()
{
    qreal widgetXPos = HsScene::instance()->activeWidget()->geometry().center().x();
    return widgetXPos < mPageChangeZoneWidth;
}

/*!
    Checks if the active widget is located inside the right
    page change zone.
*/
bool HsIdleState::isInRightPageChangeZone()
{
    qreal widgetXPos = HsScene::instance()->activeWidget()->geometry().center().x();
    qreal pageWidth = HsScene::mainWindow()->layoutRect().width();
    return pageWidth - mPageChangeZoneWidth < widgetXPos;
}

/*!
    Inserts new page at index position \a pageIndex in the scene.
*/
void HsIdleState::addPageToScene(int pageIndex)
{
    HsPageData data;
    data.indexPosition = pageIndex;
    HsPage *page = HsPage::createInstance(data);
    page->load();
    HsScene::instance()->addPage(page);    
    mUiWidget->insertPage(pageIndex, page);
}

/*!
    Computes the parallax factor based on the current scene and
    page layer widths, and page count.
*/
qreal HsIdleState::parallaxFactor() const
{
    qreal clw = mUiWidget->controlLayer()->size().width();
    qreal slw = mUiWidget->sceneLayer()->size().width() - HSBOUNDARYEFFECT;
    int n = HsScene::instance()->pages().count();
    if (n < 2) {
        return 1;
    } else {
        return (slw - clw) / ((n - 1) * clw);
    }
}

/*!
    Disables the main window user interaction.
*/
void HsIdleState::action_disableUserInteraction()
{
    HsScene::mainWindow()->setInteractive(false);
}

/*!
    Enables the main window user interaction.
*/
void HsIdleState::action_enableUserInteraction()
{
    HsScene::mainWindow()->setInteractive(true);
}

/*!
    If called for the first time, setups the idle view.
    Updates the soft key action and sets the idle view 
    as the current view to the main window.
*/
void HsIdleState::action_idle_setupView()
{
    if (!mView) {
        mUiWidget = new HsIdleWidget;
        mView = HsScene::mainWindow()->addView(mUiWidget);
        mView->setContentFullScreen();
        
        mNavigationAction = new HbAction(this);
        mNavigationAction->setIcon(HbIcon(gApplicationLibraryIconName));
        connect(mNavigationAction, SIGNAL(triggered()), SIGNAL(event_applicationLibrary()));
        mView->setNavigationAction(mNavigationAction);
        
        // TODO: Workaround to Qt/Hb layouting bugs.
        QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
    }

    HsScene::mainWindow()->setCurrentView(mView);
}

/*!
    Sets the idle view's title.
*/
void HsIdleState::action_idle_setupTitle()
{
    qDebug() << "HsIdleState::action_idle_setupTitle() - ENTRY";
    onTitleChanged(mTitleResolver->title());
    connect(mTitleResolver, SIGNAL(titleChanged(QString)), SLOT(onTitleChanged(QString)));
    qDebug() << "HsIdleState::action_idle_setupTitle() - EXIT";
}

/*!
    Updates the idle view's title.
*/
void HsIdleState::onTitleChanged(QString title)
{
    qDebug() << "HsIdleState::onTitleChanged() to title: " << title;
    mView->setTitle(title);
}

/*!

*/
void HsIdleState::onAddContentActionTriggered()
{
    machine()->postEvent(
        HsMenuEventFactory::createOpenAppLibraryEvent(AddHsMenuMode));
}

/*!
    Disconnects the idle view's title.
*/
void HsIdleState::action_idle_cleanupTitle()
{
    mTitleResolver->disconnect(this);
}

/*!
    Lays out the active page's new widgets that were added
    from the application library.
*/
void HsIdleState::action_idle_layoutNewWidgets()
{
    HsPage *page = HsScene::instance()->activePage();    
    if (!page) {
        return;
    }

    QList<HsWidgetHost *> widgets = page->newWidgets();     
    if (widgets.isEmpty()) {
        return;
    }

    foreach (HsWidgetHost *widget, widgets) {
        widget->initializeWidget();
        widget->showWidget();
    }

    page->layoutNewWidgets();

}

/*!
    Moves the scene and page layers so that the active
    page is shown.
*/
void HsIdleState::action_idle_showActivePage()
{
    qreal x = pageLayerXPos(HsScene::instance()->activePageIndex());
    mUiWidget->pageLayer()->setX(x);
    mUiWidget->sceneLayer()->setX((parallaxFactor() * x) - HSBOUNDARYEFFECT / 2);
}

/*!
    Connects the orientation change event handler.
*/
void HsIdleState::action_idle_connectOrientationChangeEventHandler()
{
    connect(HsScene::mainWindow(), 
        SIGNAL(orientationChanged(Qt::Orientation)),
        SLOT(onOrientationChanged(Qt::Orientation)));
}

/*!
    Installs the event filter.
*/
void HsIdleState::action_idle_installEventFilter()
{
    QCoreApplication::instance()->installEventFilter(this);
}

/*!
    Cleans up the idle view.
*/
void HsIdleState::action_idle_cleanupView()
{
    if (mUiWidget){
        mUiWidget->clearDelayedPress();
    }
}

/*!
    Disconnects the orientation change event handler.
*/
void HsIdleState::action_idle_disconnectOrientationChangeEventHandler()
{
    disconnect(HsScene::mainWindow(), 
        SIGNAL(orientationChanged(Qt::Orientation)),
        this, SLOT(onOrientationChanged(Qt::Orientation)));
}

/*!
    Disconnects the event filter.
*/
void HsIdleState::action_idle_uninstallEventFilter()
{
    QCoreApplication::instance()->removeEventFilter(this);
}

/*!
    Updates the options menu content.
*/
void HsIdleState::action_waitInput_updateOptionsMenu()
{
    HsScene *scene = HsScene::instance();

    HbMenu *menu = new HbMenu();

    // Task switcher
    menu->addAction(hbTrId(hsLocTextId_OptionsMenu_TaskSwitcher),
        this, SLOT(openTaskSwitcher()));

    // Add content
    menu->addAction(hbTrId(hsLocTextId_ContextMenu_AddContent),
        this, SLOT(onAddContentActionTriggered()));

    // Add page
    if (scene->pages().count() < scene->maximumPageCount()) {
        menu->addAction(hbTrId(hsLocTextId_OptionsMenu_AddPage), 
            this, SIGNAL(event_addPage()));
    }

    // Change wallpaper
    menu->addAction(hbTrId(hsLocTextId_ContextMenu_ChangeWallpaper),
        this, SIGNAL(event_selectSceneWallpaper()));

    // Remove page
    if (scene->activePage()->isRemovable()) {
        menu->addAction(hbTrId(hsLocTextId_OptionsMenu_RemovePage), 
            this, SIGNAL(event_removePage()));
    }
    
    // Online / Offline
    if (scene->isOnline()) {
        menu->addAction(hbTrId(hsLocTextId_OptionsMenu_HsToOffline),
            this, SIGNAL(event_toggleConnection()));
    } else {
        menu->addAction(hbTrId(hsLocTextId_OptionsMenu_HsToOnline),
            this, SIGNAL(event_toggleConnection())); 
    }

    mView->setMenu(menu);
}

/*!
    Connects the waitInput state's mouse event handlers.
*/
void HsIdleState::action_waitInput_connectMouseEventHandlers()
{
    CONNECT_MOUSE_EVENT_HANDLER(mousePressed, waitInput_onMousePressed)
}
 
/*!
    Disconnects the waitInput state's mouse event handlers.
*/
void HsIdleState::action_waitInput_disconnectMouseEventHandlers()
{
    DISCONNECT_MOUSE_EVENT_HANDLER(mousePressed, waitInput_onMousePressed)    
}

/*!
    Connects the widgetInteraction state's mouse event handlers.
*/
void HsIdleState::action_widgetInteraction_connectMouseEventHandlers()
{
    CONNECT_MOUSE_EVENT_HANDLER(mouseMoved, widgetInteraction_onMouseMoved)
    CONNECT_MOUSE_EVENT_HANDLER(mouseReleased, widgetInteraction_onMouseReleased)
}

/*!
    Connects the widgetInteraction state's gesture timers.
*/
void HsIdleState::action_widgetInteraction_connectGestureTimers()
{
    mTimer.setInterval(500);
    connect(&mTimer, SIGNAL(timeout()), 
        SLOT(widgetInteraction_onTapAndHoldTimeout()));
    mTimer.start();

    HsWidgetHost *widget = HsScene::instance()->activeWidget();
    widget->startTapAndHoldAnimation();
}
 
/*!
    Disconnects the widgetInteraction state's mouse event handlers.
*/
void HsIdleState::action_widgetInteraction_disconnectMouseEventHandlers()
{
    DISCONNECT_MOUSE_EVENT_HANDLER(mouseMoved, widgetInteraction_onMouseMoved)
    DISCONNECT_MOUSE_EVENT_HANDLER(mouseReleased, widgetInteraction_onMouseReleased)
}

/*!
    Disconnects the widgetInteraction state's gesture timers.
*/
void HsIdleState::action_widgetInteraction_disconnectGestureTimers()
{
    disconnect(&mTimer, SIGNAL(timeout()), 
        this, SLOT(widgetInteraction_onTapAndHoldTimeout()));

    HsWidgetHost *widget = HsScene::instance()->activeWidget();
    widget->stopTapAndHoldAnimation();
}

/*!
    Connects the sceneInteraction state's mouse event handlers.
*/
void HsIdleState::action_sceneInteraction_connectMouseEventHandlers()
{
    CONNECT_MOUSE_EVENT_HANDLER(mouseMoved, sceneInteraction_onMouseMoved)
    CONNECT_MOUSE_EVENT_HANDLER(mouseReleased, sceneInteraction_onMouseReleased)
}

/*!
    Connects the sceneInteraction state's gesture timers.
*/
void HsIdleState::action_sceneInteraction_connectGestureTimers()
{
    mTimer.setInterval(500);
    connect(&mTimer, SIGNAL(timeout()), 
        SLOT(sceneInteraction_onTapAndHoldTimeout()));
    mTimer.start();
}
 
/*!
    Disconnects the sceneInteraction state's mouse event handlers.
*/
void HsIdleState::action_sceneInteraction_disconnectMouseEventHandlers()
{
    DISCONNECT_MOUSE_EVENT_HANDLER(mouseMoved, sceneInteraction_onMouseMoved)
    DISCONNECT_MOUSE_EVENT_HANDLER(mouseReleased, sceneInteraction_onMouseReleased)
}

/*!
    Disconnects the sceneInteraction state's gesture timers.
*/
void HsIdleState::action_sceneInteraction_disconnectGestureTimers()
{
    disconnect(&mTimer, SIGNAL(timeout()), 
        this, SLOT(sceneInteraction_onTapAndHoldTimeout()));
}

/*!
    Reparents the active widget to the control layer.
*/
void HsIdleState::action_moveWidget_reparentToControlLayer()
{
    HsWidgetHost *widget = HsScene::instance()->activeWidget();
    Q_ASSERT(widget);
    widget->setParentItem(mUiWidget->controlLayer());

    mUiWidget->showTrashBin();
}

/*!
    Starts the widget drag animation for the active widget.
*/
void HsIdleState::action_moveWidget_startWidgetDragAnimation()
{
    HsWidgetHost *widget = HsScene::instance()->activeWidget();
    Q_ASSERT(widget);
    widget->startDragAnimation();
}

/*!
    Connects the moveWidget state's mouse event handlers.
*/
void HsIdleState::action_moveWidget_connectMouseEventHandlers()
{
    CONNECT_MOUSE_EVENT_HANDLER(mouseMoved, moveWidget_onMouseMoved)
    CONNECT_MOUSE_EVENT_HANDLER(mouseReleased, moveWidget_onMouseReleased)
}

/*!
    Reparents the active widget to the active page.
*/
void HsIdleState::action_moveWidget_reparentToPage()
{
    HsWidgetHost *widget = HsScene::instance()->activeWidget();
    if (widget) {
        widget->setParentItem(HsScene::instance()->activePage());
    }
    
    mUiWidget->showPageIndicator();
}

/*!
    Starts the widget drop animation for the active widget.
*/
void HsIdleState::action_moveWidget_startWidgetDropAnimation()
{
    HsWidgetHost *widget = HsScene::instance()->activeWidget();
    if (widget) {
        widget->startDropAnimation();
    }
}

/*!
    Disconnects the moveWidget state's mouse event handlers.
*/
void HsIdleState::action_moveWidget_disconnectMouseEventHandlers()
{
    DISCONNECT_MOUSE_EVENT_HANDLER(mouseMoved, moveWidget_onMouseMoved)
    DISCONNECT_MOUSE_EVENT_HANDLER(mouseReleased, moveWidget_onMouseReleased)
}

/*!
    Connects the moveScene state's mouse event handlers.
*/
void HsIdleState::action_moveScene_connectMouseEventHandlers()
{
    CONNECT_MOUSE_EVENT_HANDLER(mouseMoved, moveScene_onMouseMoved)
    CONNECT_MOUSE_EVENT_HANDLER(mouseReleased, moveScene_onMouseReleased)
}
 
/*!
    Disconnects the moveScene state's mouse event handlers.
*/
void HsIdleState::action_moveScene_disconnectMouseEventHandlers()
{
    DISCONNECT_MOUSE_EVENT_HANDLER(mouseMoved, moveScene_onMouseMoved)
    DISCONNECT_MOUSE_EVENT_HANDLER(mouseReleased, moveScene_onMouseReleased)
}

#ifdef COVERAGE_MEASUREMENT
#pragma CTC SKIP
#endif //COVERAGE_MEASUREMENT
/*!
    Shows the scene menu.
*/
void HsIdleState::action_sceneMenu_showMenu()
{
    HbMenu menu;
    
    HbAction *addContentAction = 
        menu.addAction(hbTrId(hsLocTextId_ContextMenu_AddContent));

    HbAction *addPageAction = 0;
    HsScene *scene = HsScene::instance();
    if (scene->pages().count() < scene->maximumPageCount()) {
        addPageAction = menu.addAction(hbTrId(hsLocTextId_OptionsMenu_AddPage));
    }

    HbAction *changeWallpaperAction = 
        menu.addAction(hbTrId(hsLocTextId_ContextMenu_ChangeWallpaper));

    HbAction *action = menu.exec(mSceneMenuPos);    
    if (action == addContentAction) {
        onAddContentActionTriggered();
    } else if (addPageAction && action == addPageAction) {
        emit event_addPage();
    } else if (action == changeWallpaperAction) {
        emit event_selectSceneWallpaper();
    } else {
        emit event_waitInput();
    }
}
#ifdef COVERAGE_MEASUREMENT
#pragma CTC ENDSKIP
#endif //COVERAGE_MEASUREMENT

/*!
    Adds a new page to the scene.
*/
void HsIdleState::action_addPage_addPage()
{
    HsScene *scene = HsScene::instance();    
    int pageIndex = scene->pages().count();    
    addPageToScene(pageIndex);    
    scene->setActivePageIndex(pageIndex);
    startPageChangeAnimation(pageIndex, 700);
    mUiWidget->pageIndicator()->addItem(pageIndex);
    mUiWidget->showPageIndicator();
}

/*!
    Removes an existing page from the scene.
*/
void HsIdleState::action_removePage_removePage()
{
    HsScene *scene = HsScene::instance();
    HsPage *page = scene->activePage();
    bool deletePage(true);

#ifndef HOMESCREEN_TEST //We don't want to test message box.
	if (!page->widgets().isEmpty()) {
		HbMessageBox box(HbMessageBox::MessageTypeQuestion);
		box.setHeadingWidget(new HbLabel
		                        (hbTrId(hsLocTextId_Title_RemovePage)));
		box.setText(hbTrId(hsLocTextId_Confirmation_RemovePage));
		box.setPrimaryAction(new HbAction(hbTrId(hsLocTextId_ConfirmationButton_Ok)));
		box.setSecondaryAction(new HbAction(hbTrId(hsLocTextId_ConfirmationButton_Cancel)));

        HsScene::mainWindow()->setInteractive(true);
        HbAction *action = box.exec();
		if (action != box.primaryAction()) {
		    deletePage = false;
		}
	}
#endif //HOMESCREEN_TEST

	if (deletePage) {
		int pageIndex = scene->activePageIndex();

		mUiWidget->removePage(pageIndex);
		scene->removePage(page);
		delete page;

		pageIndex = pageIndex == 0 ? 0 : pageIndex - 1;
		scene->setActivePageIndex(pageIndex);

		startPageChangeAnimation(pageIndex, 200);

		mUiWidget->pageIndicator()->removeItem(pageIndex);
		mUiWidget->setActivePage(pageIndex);
		mUiWidget->showPageIndicator();
	}
}

/*!
    Toggles the homescreen online/offline state.
*/
void HsIdleState::action_toggleConnection_toggleConnection()
{
    HsScene *scene = HsScene::instance();    
    scene->setOnline(!scene->isOnline());
}

/*!
    Handles mouse press events for the waitInput state.
    Filters events for the item \a watched. \a event is the 
    filtered event. Sets the \a filtered true if the event
    was filtered by this handler.
*/
void HsIdleState::waitInput_onMousePressed(
    QGraphicsItem *watched, QGraphicsSceneMouseEvent *event, bool &filtered)
{
    filtered = true;

    HsScene *scene = HsScene::instance();
    HsPage *page = scene->activePage();
    QList<HsWidgetHost *> widgets = page->widgets();

    if (mUiWidget->controlLayer() == watched ||
        mUiWidget->controlLayer()->isAncestorOf(watched)) {
        filtered = false;
        return;
    }

    mUiWidget->captureDelayedPress(event);

    foreach (HsWidgetHost *widget, widgets) {
        if (widget == watched || widget->isAncestorOf(watched)) {
            scene->setActiveWidget(widget);
            emit event_widgetInteraction();
            return;
        }
    }

    mSceneMenuPos = event->scenePos();
    emit event_sceneInteraction();
}

/*!
    Handles mouse move events for the widgetInteraction state.
    Filters events for the item \a watched. \a event is the 
    filtered event. Sets the \a filtered true if the event
    was filtered by this handler.
*/
void HsIdleState::widgetInteraction_onMouseMoved(
    QGraphicsItem *watched, QGraphicsSceneMouseEvent *event, bool &filtered)
{
    Q_UNUSED(watched)
    Q_UNUSED(filtered)

    if (!mTimer.isActive()) {
        return;
    }

    QPointF point = 
        event->scenePos() - event->buttonDownScenePos(Qt::LeftButton);
    if (mTapAndHoldDistance < point.manhattanLength()) {
        mTimer.stop();
        mUiWidget->sendDelayedPress();
        HsScene::instance()->activeWidget()->stopTapAndHoldAnimation();
    }
}

/*!
    Handles mouse release events for the widgetInteraction state.
    Filters events for the item \a watched. \a event is the 
    filtered event. Sets the \a filtered true if the event
    was filtered by this handler.
*/
void HsIdleState::widgetInteraction_onMouseReleased(
    QGraphicsItem *watched, QGraphicsSceneMouseEvent *event, bool &filtered)
{
    Q_UNUSED(watched)
    Q_UNUSED(event)
    Q_UNUSED(filtered)

    if (mTimer.isActive()) {
        mTimer.stop();
        mUiWidget->sendDelayedPress();
    }

    HsPage *page = HsScene::instance()->activePage();
    QMetaObject::invokeMethod(page, "updateZValues", Qt::QueuedConnection);

    emit event_waitInput();
}

/*!
    Handles mouse move events for the sceneInteraction state.
    Filters events for the item \a watched. \a event is the 
    filtered event. Sets the \a filtered true if the event
    was filtered by this handler.
*/
void HsIdleState::sceneInteraction_onMouseMoved(
    QGraphicsItem *watched, QGraphicsSceneMouseEvent *event, bool &filtered)
{
    Q_UNUSED(watched)
    Q_UNUSED(filtered)

    mSceneMenuPos = event->scenePos();

    if (!mTimer.isActive()) {
        return;
    }

    QPointF point = 
        event->scenePos() - event->buttonDownScenePos(Qt::LeftButton);
    if (mTapAndHoldDistance < point.manhattanLength()) {
        mTimer.stop();
        mUiWidget->clearDelayedPress();
        emit event_moveScene();
    }
}

/*!
    Handles mouse release events for the sceneInteraction state.
    Filters events for the item \a watched. \a event is the 
    filtered event. Sets the \a filtered true if the event
    was filtered by this handler.
*/
void HsIdleState::sceneInteraction_onMouseReleased(
    QGraphicsItem *watched, QGraphicsSceneMouseEvent *event, bool &filtered)
{
    Q_UNUSED(watched)
    Q_UNUSED(event)
    Q_UNUSED(filtered)

    if (mTimer.isActive()) {
        mTimer.stop();
        mUiWidget->sendDelayedPress();
    }

    emit event_waitInput();
}

/*!
    Handles mouse move events for the moveWidget state.
    Filters events for the item \a watched. \a event is the 
    filtered event. Sets the \a filtered true if the event
    was filtered by this handler.
*/
void HsIdleState::moveWidget_onMouseMoved(
    QGraphicsItem *watched, QGraphicsSceneMouseEvent *event, bool &filtered)
{
    Q_UNUSED(watched)

    HsScene *scene = HsScene::instance();
    HsWidgetHost *widget = scene->activeWidget();
    HsPage *page = scene->activePage();
    
    QPointF delta(event->scenePos() - event->lastScenePos());

    QRectF widgetRect = widget->geometry();
    widgetRect.moveTopLeft(widgetRect.topLeft() + delta);
    
    QRectF pageRect = mView->rect();    
    
    qreal lowerBoundX = -widgetRect.width() / 2 + 10;
    if (page == scene->pages().first()) {
        lowerBoundX = 0;
    }
    qreal upperBoundX = pageRect.width() - widgetRect.width() / 2 - 10;    
    if (page == scene->pages().last() && scene->pages().count() == scene->maximumPageCount()) {
        upperBoundX = pageRect.width() - widgetRect.width();
    }

    qreal widgetX = qBound(lowerBoundX, widgetRect.x(), upperBoundX);
    qreal widgetY = qBound(qreal(64), widgetRect.y(), pageRect.height() - widgetRect.height());
    
    /* if using ItemClipsChildrenToShape-flag in widgethost then
       setPos does not update position here, however setGeometry does it, QT bug?
    */
    widget->setGeometry(widgetX, widgetY, widgetRect.width(), widgetRect.height());

    if (isInPageChangeZone() && mAllowZoneAnimation) {
        if (!mZoneAnimation) {
        	/* We don't want to create another animation 
        	   before previous is finished */
        	mAllowZoneAnimation = false;
            startPageChangeZoneAnimation(800);                
        }
    }
    else if (mZoneAnimation && !isInPageChangeZone()) {
    	if (mZoneAnimation->state() == QAbstractAnimation::Running) {    		
        	if (mZoneAnimation->direction() == QAbstractAnimation::Forward) {	        		    
        	   		mPageChanged = false;
        	   		mZoneAnimation->setDuration(200);
                    mZoneAnimation->setDirection(QAbstractAnimation::Backward);
		    }			    
		}
        else {
        	// Out of the page change zone. Delete animation.	        	
        	delete mZoneAnimation;
            mZoneAnimation = NULL;
            mAllowZoneAnimation = true;
        }
    }
    else if (!isInPageChangeZone()) {
    	/* Zone animation finished and deleted.
    	   New animation can be launched. */
    	mAllowZoneAnimation = true;
    }

    if (mUiWidget->trashBin()->isUnderMouse()) {
        mUiWidget->trashBin()->activate();
    } else {
        mUiWidget->trashBin()->deactivate();
    }

    if (!mUiWidget->pageIndicator()->isAnimationRunning()) {
        mUiWidget->showTrashBin();
    }
    filtered = true;
}

/*!
    Handles mouse release events for the moveWidget state.
    Filters events for the item \a watched. \a event is the 
    filtered event. Sets the \a filtered true if the event
    was filtered by this handler.
*/
void HsIdleState::moveWidget_onMouseReleased(
    QGraphicsItem *watched, QGraphicsSceneMouseEvent *event, bool &filtered)
{
    Q_UNUSED(watched)
    Q_UNUSED(event)

    mTimer.stop();

    if (mZoneAnimation
    	&& mZoneAnimation->state() == QAbstractAnimation::Running
    	&& mZoneAnimation->direction() == QAbstractAnimation::Forward) {
        mPageChanged = false;
        mZoneAnimation->setDuration(200);
        mZoneAnimation->setDirection(QAbstractAnimation::Backward);                
    }

    HsScene *scene = HsScene::instance();
    HsPage *page = scene->activePage();
    HsWidgetHost *widget = scene->activeWidget();

    if (mUiWidget->trashBin()->isUnderMouse()) {
        widget->page()->removeWidget(widget);
        widget->uninitializeWidget();
        widget->deleteFromDatabase();
        widget->deleteLater();
        scene->setActiveWidget(0);
    } else {
        if (widget->page() != page) {
            widget->page()->removeWidget(widget);
            page->addExistingWidget(widget);
            if (HsScene::orientation() == Qt::Horizontal) {
                widget->deleteWidgetPresentation(Qt::Vertical);
            } else {
                widget->deleteWidgetPresentation(Qt::Horizontal);
            }
        }
        
        QRectF widgetRect = widget->geometry();
        QRectF pageRect = page->rect();        
        qreal widgetX = qBound(qreal(0), widgetRect.x(), pageRect.width() - widgetRect.width());
        qreal widgetY = qBound(qreal(64), widgetRect.y(), pageRect.height() - widgetRect.height());        
        widget->setPos(widgetX, widgetY);

        widget->setWidgetPresentation();
        page->updateZValues();
    }
    mAllowZoneAnimation = true;
    filtered = true;
    emit event_waitInput();
}

/*!
    Handles mouse move events for the moveScene state.
    Filters events for the item \a watched. \a event is the 
    filtered event. Sets the \a filtered true if the event
    was filtered by this handler.
*/
void HsIdleState::moveScene_onMouseMoved(
    QGraphicsItem *watched, QGraphicsSceneMouseEvent *event, bool &filtered)
{
    Q_UNUSED(watched)
    Q_UNUSED(filtered)
    
    HsScene *scene = HsScene::instance();
    qreal delta = 
        event->scenePos().x() - event->buttonDownScenePos(Qt::LeftButton).x();

    qreal x = qBound(pageLayerXPos(scene->pages().count() - 1) - HSBOUNDARYEFFECT / 2 / parallaxFactor(),
                     pageLayerXPos(scene->activePageIndex()) + delta,
                     pageLayerXPos(0) + (HSBOUNDARYEFFECT / 2 / parallaxFactor()) - qreal(0.5));

    mUiWidget->pageLayer()->setX(x);
    mUiWidget->sceneLayer()->setX((parallaxFactor() * x) - HSBOUNDARYEFFECT / 2);
}

/*!
    Handles mouse release events for the moveScene state.
    Filters events for the item \a watched. \a event is the 
    filtered event. Sets the \a filtered true if the event
    was filtered by this handler.
*/
void HsIdleState::moveScene_onMouseReleased(
    QGraphicsItem *watched, QGraphicsSceneMouseEvent *event, bool &filtered)
{
    Q_UNUSED(watched)
    Q_UNUSED(filtered)
    
    QList<HsPage *> pages = HsScene::instance()->pages();
    QSizeF pageSize = pages.first()->size();

    int pageIndex = HsScene::instance()->activePageIndex();

    QPointF delta(
        event->scenePos() - event->buttonDownScenePos(Qt::LeftButton));
    if (delta.x() < -pageSize.width() / 3) {
        pageIndex = qMin(pageIndex + 1, pages.count() - 1);
    } else if (pageSize.width() / 3 < delta.x()) {
        pageIndex = qMax(pageIndex - 1, 0);
    }

    HsScene::instance()->setActivePageIndex(pageIndex);
    
    startPageChangeAnimation(pageIndex, 200);

    emit event_waitInput();
}

/*!
    Handles orientation change events. \a orientation is the 
    new orientation.
*/
void HsIdleState::onOrientationChanged(Qt::Orientation orientation)
{
    QList<HsPage *> pages = HsScene::instance()->pages();
    QList<HsWidgetHost *> widgets;
    HsWidgetHost *widget = 0;
    
    QRectF pageRect = HsScene::mainWindow()->layoutRect();

    // TODO: Temporary workaround for the Orbit bug.
    if (orientation == Qt::Horizontal) {
        pageRect = QRectF(0, 0, 640, 360);
    } else {
        pageRect = QRectF(0, 0, 360, 640);
    }
    // End of the workaround.

    const int chromeHeight = 64; // TODO: get this somewhere
        
    for (int i = 0; i < pages.count(); ++i) {        
        widgets = pages[i]->widgets();
        for (int j = 0; j < widgets.count(); ++j) {
            widget = widgets[j];
            HsWidgetPresentationData presentation = widget->widgetPresentation(orientation);
            if (presentation.widgetId < 0) {
                QList<QRectF> geometries = 
                    HsWidgetPositioningOnOrientationChange::instance()->convert(
                        QRectF(0, chromeHeight, 
                            pageRect.height(),
                            pageRect.width() - chromeHeight),
                        QList<QRectF>() << widget->geometry(),
                        QRectF(0, chromeHeight, 
                            pageRect.width(), 
                            pageRect.height() - chromeHeight));
                widget->setGeometry(geometries.first());
                widget->setWidgetPresentation();
            } else {
                widget->setPos(presentation.x, presentation.y);
                widget->setZValue(presentation.zValue);
            }
        }
    }
    
    action_idle_showActivePage();
}

/*!
    Handles tap-and-hold events for the widgetInteraction state.
*/
void HsIdleState::widgetInteraction_onTapAndHoldTimeout()
{
    mUiWidget->clearDelayedPress();
    emit event_moveWidget();
}

/*!
    Handles tap-and-hold events for the sceneInteraction state.
*/
void HsIdleState::sceneInteraction_onTapAndHoldTimeout()
{
    mUiWidget->clearDelayedPress();
    emit event_sceneMenu();
}

#ifdef COVERAGE_MEASUREMENT
#pragma CTC SKIP
#endif //COVERAGE_MEASUREMENT

/*!
 Open task switcher.
 \retval true if operation is successful.
 */
bool HsIdleState::openTaskSwitcher()
{
    return HsMenuService::launchTaskSwitcher();
}
#ifdef COVERAGE_MEASUREMENT
#pragma CTC ENDSKIP
#endif //COVERAGE_MEASUREMENT

/*!
    Page change zone animation has been finished.
 */
void HsIdleState::zoneAnimationFinished()
{
	HsScene *scene = HsScene::instance();
    int pageIndex = scene->activePageIndex();

    if (mPageChanged) {	    
	    if (isInLeftPageChangeZone() &&
	        0 < pageIndex) {
	        --pageIndex;
	    }
		else if (isInRightPageChangeZone() &&
	        pageIndex < scene->pages().count()) {
	        ++pageIndex;
	    }
		else {
			delete mZoneAnimation;
	        mZoneAnimation = NULL;
	        mAllowZoneAnimation = true;
	        return;
	    }

	    if (pageIndex == scene->pages().count()) {
	        if (scene->pages().last()->widgets().isEmpty()) {
	        	delete mZoneAnimation;
	            mZoneAnimation = NULL;
	            mAllowZoneAnimation = true;
	            return;
	        }
			else if (scene->pages().count() < scene->maximumPageCount()) {
	            addPageToScene(pageIndex);
	            mUiWidget->showPageIndicator();
	            mUiWidget->pageIndicator()->addItem(pageIndex);
	        }
			else {
				delete mZoneAnimation;
	            mZoneAnimation = NULL;
	            mAllowZoneAnimation = true;
	            return;
	        }
	    }        
	    scene->setActivePageIndex(pageIndex);
	    startPageChangeAnimation(pageIndex, 200);
    }
    else {    		   
	    scene->setActivePageIndex(pageIndex);
        mUiWidget->setActivePage(pageIndex);
	    mAllowZoneAnimation = true;
    }

    delete mZoneAnimation;
	mZoneAnimation = NULL;	
}
// Undefine the helper macros.
#undef ENTRY_ACTION
#undef EXIT_ACTION
#undef CONNECT_MOUSE_EVENT_HANDLER
#undef DISCONNECT_MOUSE_EVENT_HANDLER