///////////////////////////////////////////////////////////////
//
// InternetExplorerSink.h
//
// Created: 12/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.
//
///////////////////////////////////////////////////////////////
#ifndef __INTERNETEXPLORERSINK_H_F2026BFC_F70F_46B5_A50E_896E4E022F83_
#define __INTERNETEXPLORERSINK_H_F2026BFC_F70F_46B5_A50E_896E4E022F83_
#include "Gesture.h"
/**
* @brief Class to intercept mouse movements within a given window and
* inform an observer of these movements.
*
* The MouseTracker class subclasses a window. The classes message pump
* intercepts all mouse messages and forwards these to the watcher.
*
* Clients of the MouseTracker can disable mouse tracking by using the
* Suspend RAII class. Each Suspend object sets the m_isSuspended flag
* to true for its lifetime, and resets the flag to false when its
* destroyed. The ProcessWindowMessage function (implemented via the
* BEGIN_MSG_MAP_EX macro) ignores all messages if m_isSuspended is set
* to true.
*
* We can't simply temporarily unsubclass the (subclassed) Window. If
* we've got multiple ATL CWindowImpl derived objects subclassing a window
* (as we do in the MouseGestures app if (for example) the Google toolbar
* is installed), then there's no guarantee that we're the last object
* in the message chain. Unsubclass can fail (the top-most message proc
* will be different from the static WndProc function for this class),
* resulting in recursion (and ultimately a stack overflow) if we send
* our own messages to the subclassed window.
**/
class MouseTracker : public CWindowImpl< MouseTracker >
{
public:
struct Watcher
{
//
// return true if we want to pass the message onto the subclassed window
// or false if we want to swallow the message and start tracking the mouse
//
virtual bool OnLeftButtonDown( HWND hWnd, const POINT &pt, DWORD flags ) { return true; }
virtual bool OnRightButtonDown( HWND hWnd, const POINT &pt, DWORD flags ) { return true; }
//
// if the user (i.e. the class that derives from Watcher) returns true from
// either OnLeftButtonDown or OnRightButtonDown then we fire the OnLeftButtonUp
// or OnRightButtonUp events respectively, with the (cracked) WPARAM and LPARAM
// of the mouse message.
//
virtual bool OnLeftButtonUp( HWND hWnd, const POINT &pt, DWORD flags ) { return true; }
virtual bool OnRightButtonUp( HWND hWnd, const POINT &pt, DWORD flags ) { return true; }
//
// if the user (i.e. the class that derives from Watcher) returns false from
// either OnLeftButtonDown or OnRightButtonDown then we fire the OnLeftButtonUp
// or OnRightButtonUp events respectively, with the path of the mouse
// between the two events. There's no return value as we always swallow
// the windows message
//
virtual void OnLeftButtonUp( HWND hWnd, const Path &path ) {}
virtual void OnRightButtonUp( HWND hWnd, const Path &path ) {}
//
// Mouse Wheel
//
virtual bool OnMouseWheel( HWND hWnd, WORD flags, WORD delta, const POINT &pt ) { return true; }
};
public:
MouseTracker();
~MouseTracker();
/**
* Extend the BEGIN_MSG_MAP macro to (which implments the head of the
* ProcessWindowMessage function) to ignore all messages if the m_isSuspended
* flag is set
**/
#define BEGIN_MSG_MAP_EX(theClass) \
public: \
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) \
{ \
if( m_isSuspended ) return FALSE; \
BOOL bHandled = TRUE; \
switch(dwMsgMapID) \
{ \
case 0:
public:
BEGIN_MSG_MAP_EX( MouseTracker )
MESSAGE_HANDLER( WM_MOUSEMOVE, OnMouseMove )
MESSAGE_HANDLER( WM_RBUTTONDOWN, OnRightButtonDown )
MESSAGE_HANDLER( WM_RBUTTONUP, OnRightButtonUp )
MESSAGE_HANDLER( WM_LBUTTONDOWN, OnLeftButtonDown )
MESSAGE_HANDLER( WM_LBUTTONUP, OnLeftButtonUp )
MESSAGE_HANDLER( WM_MOUSEWHEEL, OnMouseWheel )
MESSAGE_HANDLER( WM_DESTROY, OnDestroy )
END_MSG_MAP()
private:
LRESULT OnMouseWheel( UINT, WPARAM, LPARAM, BOOL & );
LRESULT OnMouseMove( UINT, WPARAM, LPARAM, BOOL & );
LRESULT OnRightButtonDown( UINT, WPARAM, LPARAM, BOOL & );
LRESULT OnRightButtonUp( UINT, WPARAM, LPARAM, BOOL & );
LRESULT OnLeftButtonDown( UINT, WPARAM, LPARAM, BOOL & );
LRESULT OnLeftButtonUp( UINT, WPARAM, LPARAM, BOOL & );
LRESULT OnDestroy( UINT, WPARAM, LPARAM, BOOL & );
public:
HRESULT Advise( HWND hWnd, Watcher *pWatcher );
HRESULT Unadvise();
/**
* Call reset if you want to stop tracking the mouse in response
* to a mouse event (e.g. a left-right click)
**/
void Reset();
private:
struct ButtonRec
{
Path path;
bool isTracking;
ButtonRec() :
path(),
isTracking( false )
{
}
};
enum
{
Left = 0,
Right = 1,
REC_COUNT
};
public:
/**
* RAII class to toggle the m_isSuspended flag. When the
* Suspend object is constructed, m_isSuspended is set to true.
* When the Suspend object is destroyed, m_isSuspened is set to
* false.
* The current state of the m_isSuspended is ignored, so Suspend
* objects shouldn't be used simultaneously.
**/
class Suspend
{
public:
Suspend( MouseTracker &mouseTracker );
~Suspend();
private:
bool &isSuspended;
};
friend class Suspend;
private:
void SetCapture();
void ReleaseCapture();
private:
ButtonRec m_buttonRecs[ REC_COUNT ];
bool m_isBound; /// < are we bound to a subclassed window
Watcher *m_pWatcher; /// < the object watching the mouse
bool m_isSuspended; /// < is tracking suspended?
bool m_hasCapture; /// < have we captured the mouse?
};
#endif // __INTERNETEXPLORERSINK_H_F2026BFC_F70F_46B5_A50E_896E4E022F83_