Click here to Skip to main content
15,896,453 members
Articles / Web Development / HTML

Mouse Gestures for Internet Explorer

Rate me:
Please Sign up or sign in to vote.
4.84/5 (99 votes)
21 Sep 200514 min read 1.3M   13.4K   235  
Adding mouse gesture recognition to Internet Explorer.
///////////////////////////////////////////////////////////////
//
// MouseTracker.cpp
//
// Created: 08/07/2003
// Copyright (c) 2003 Ralph Hare (ralph.hare@ysgyfarnog.co.uk)
// All rights reserved.
//
// The code and information is provided "as-is" without
// warranty of any kind, either expressed or implied.
//
///////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "MouseTracker.h"
#include "TrailManager.h"

namespace
{
    inline POINT PointFromLPARAM( LPARAM lParam )
    {
        POINT   pt = { GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) };
        return pt;
    }
}

MouseTracker::MouseTracker() :
    m_isBound( false ),
    m_pWatcher( NULL ),
    m_isSuspended( false ),
    m_hasCapture( false )
{
}

MouseTracker::~MouseTracker()
{
    if( m_isBound )
    {
        UnsubclassWindow();
    }
}

LRESULT MouseTracker::OnMouseMove( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled )
{
    bHandled = FALSE;

    if( m_buttonRecs[ Left ].isTracking )
    {
        m_buttonRecs[ Left ].path.push_back( PointFromLPARAM( lParam ) );
        bHandled = TRUE;
    }

    if( m_buttonRecs[ Right ].isTracking )
    {
        m_buttonRecs[ Right ].path.push_back( PointFromLPARAM( lParam ) );
        bHandled = TRUE;

        GetTrail()->Update( m_buttonRecs[ Right ].path );
    }

    return 0;
}

LRESULT MouseTracker::OnRightButtonDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled )
{
    if( m_isSuspended )
    {
        bHandled = FALSE;
        return 1;
    }

    POINT   pt = PointFromLPARAM( lParam );

//
// if we don't have a watcher, or the watcher returns true, then
// we mark this message as not being handled so it gets passed through
// to the window we're subclassing
//
    if( ( m_pWatcher == NULL ) || m_pWatcher->OnRightButtonDown( m_hWnd, pt, static_cast< DWORD >( wParam ) ) )
    {
        bHandled = FALSE;
    }
    else
    {
        m_buttonRecs[ Right ].isTracking = true;

        GetTrail()->Begin( m_hWnd );

    //
    // set the capture to this window while the mouse button is down. this allows
    // the user to continue the gesture outside of the window rect but we still
    // get the mouse move/button up messages
    //
        SetCapture();

        m_buttonRecs[ Right ].path.clear();
        m_buttonRecs[ Right ].path.push_back( pt );
    }

    return 0;
}

LRESULT MouseTracker::OnRightButtonUp( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled )
{
    if( m_isSuspended )
    {
        bHandled = FALSE;
        return 1;
    }

    ReleaseCapture();

    POINT   pt = PointFromLPARAM( lParam );

    if( m_buttonRecs[ Right ].isTracking )
    {
    //
    // we're not tracking any more
    //
        m_buttonRecs[ Right ].isTracking = false;

    //
    // has the mouse moved at all while we've been tracking?
    //
        if( m_buttonRecs[ Right ].path.size() > 1 )
        {
            m_buttonRecs[ Right ].path.push_back( pt );
        }

        GetTrail()->End();

        if( m_pWatcher )
        {
            m_pWatcher->OnRightButtonUp( m_hWnd, m_buttonRecs[ Right ].path );
        }
    }
    else
    {
        if( m_pWatcher )
        {
            bHandled = m_pWatcher->OnRightButtonUp( m_hWnd, pt, static_cast< DWORD >( wParam ) );
        }
    }

    return 0;
}

LRESULT MouseTracker::OnLeftButtonDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled )
{
    if( m_isSuspended )
    {
        bHandled = FALSE;
        return 1;
    }

    POINT   pt = PointFromLPARAM( lParam );

    if( ( m_pWatcher == NULL ) || m_pWatcher->OnLeftButtonDown( m_hWnd, pt, static_cast< DWORD >( wParam ) ) )
    {
        bHandled = FALSE;
    }
    else
    {
        m_buttonRecs[ Left ].isTracking = true;

        SetCapture();

        m_buttonRecs[ Left ].path.clear();
        m_buttonRecs[ Left ].path.push_back( pt );
    }

    return 0;
}

LRESULT MouseTracker::OnLeftButtonUp( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled )
{
    if( m_isSuspended )
    {
        bHandled = FALSE;
        return 1;
    }

    ReleaseCapture();

    POINT   pt = PointFromLPARAM( lParam );

    if( m_buttonRecs[ Left ].isTracking )
    {
        m_buttonRecs[ Left ].isTracking = false;

        if( m_buttonRecs[ Left ].path.size() > 1 )
        {
            m_buttonRecs[ Left ].path.push_back( pt );
        }

        if( m_pWatcher )
        {
            m_pWatcher->OnLeftButtonUp( m_hWnd, m_buttonRecs[ Left ].path );
        }
    }
    else
    {
        if( m_pWatcher )
        {
            bHandled = m_pWatcher->OnLeftButtonUp( m_hWnd, pt, static_cast< DWORD >( wParam ) );
        }
    }

    return 0;
}

LRESULT MouseTracker::OnMouseWheel( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled )
{
    if( m_pWatcher )
    {
        bHandled = m_pWatcher->OnMouseWheel( 
                                        m_hWnd,
                                        LOWORD( wParam ), 
                                        HIWORD( wParam ),
                                        PointFromLPARAM( lParam )
                                        );
    }

    return 0;
}

LRESULT MouseTracker::OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL & )
{
    if( m_isBound )
    {
    //
    // force the unsubclass on shutdown - even if somebody else
    // has subclassed this window, we need to make sure we detach
    // from it otherwise things go bang in the destructor
    //
        UnsubclassWindow( TRUE );
        m_isBound = false;
    }

    return 0;
}

LRESULT MouseTracker::OnSetFocus( UINT, WPARAM, LPARAM, BOOL & )
{
    GetTrail()->Show();
    return 0;
}

LRESULT MouseTracker::OnKillFocus( UINT, WPARAM, LPARAM, BOOL & )
{
    GetTrail()->Hide();
    return 0;
}

HRESULT MouseTracker::Advise( HWND hWnd, MouseTracker::Watcher *pWatcher )
{
    if( SubclassWindow( hWnd ) )
    {
        m_pWatcher  = pWatcher;
        m_isBound   = true;

        return S_OK;
    }
    else
    {
        ATLASSERT( FALSE );
    }

    return E_FAIL;
}

HRESULT MouseTracker::Unadvise()
{
    if( m_isBound && UnsubclassWindow() )
    {
        m_isBound = false;
    }

    return S_OK;
}

void MouseTracker::Reset()
{
    GetTrail()->End();

    for( size_t idx = 0; idx < REC_COUNT; ++idx )
    {
        m_buttonRecs[ idx ].isTracking = false;
    }

    ReleaseCapture();
}

void MouseTracker::SetCapture()
{
    if( m_hasCapture == false )
    {
        CWindowImpl< MouseTracker >::SetCapture();
        m_hasCapture = true;
    }
}

void MouseTracker::ReleaseCapture()
{
    if( m_hasCapture )
    {
        ::ReleaseCapture();
        m_hasCapture = false;
    }
}

MouseTrail * MouseTracker::GetTrail()
{
    ATLASSERT( TrailManager::GetInstance().GetTrail() != NULL );
    return TrailManager::GetInstance().GetTrail();
}

///////////////////////////////////////////////////////////////////////////////

MouseTracker::Suspend::Suspend( MouseTracker &mouseTracker ) :
    isSuspended( mouseTracker.m_isSuspended )
{
    isSuspended = true;
}

MouseTracker::Suspend::~Suspend()
{
    isSuspended = false;
}

///////////////////////////////////////////////////////////////////////////////

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions