MessageHandleResult RockerHandler::handleMessageInternal(MSG* pMsg) {
	CPoint ptCurrent(pMsg->lParam);
	CSize dist;
	switch (getState()) {
	case GS_None:
		if (pMsg->message == WM_LBUTTONDOWN || pMsg->message == WM_RBUTTONDOWN) {
			m_ptStart = ptCurrent;
			m_bLeft = (pMsg->message == WM_LBUTTONDOWN);
			setState(GS_Initiated);
			ATLTRACE(CString(_T("Rocker Gesture Initiated: ")) + (m_bLeft ? _T(" Left\n") : _T(" Right\n")));
			return MHR_Initiated;
		}
		break;
	case GS_Initiated:
		dist = ptCurrent - m_ptStart;
		if (pMsg->message == WM_MOUSEMOVE && (pMsg->wParam & (m_bLeft ? MK_LBUTTON : MK_RBUTTON))) {
			if (abs(dist.cx) > 10 || abs(dist.cy) > 10) {
				setState(GS_None);
				ATLTRACE(_T("Rocker Gesture Canceled due to mouse moved too far away\n"));
				return MHR_Canceled;
			} else
				return MHR_Swallowed;
		} else if (pMsg->message == (m_bLeft ? WM_RBUTTONDOWN : WM_LBUTTONDOWN)
				   && (pMsg->wParam & (m_bLeft ? MK_LBUTTON : MK_RBUTTON))) {
			ATLTRACE(_T("Rocker Gesture Triggered\n"));
			setState(GS_Triggered);
			return MHR_Triggered;
		} else if (pMsg->message == WM_LBUTTONDOWN || pMsg->message == WM_RBUTTONDOWN
				   || pMsg->message == WM_LBUTTONDBLCLK || pMsg->message == WM_RBUTTONDBLCLK) {
			ATLTRACE(_T("Duplicate Rocker Gesture Initiation\n"));
			return MHR_Discarded;
		} else {
			ATLTRACE(_T("Rocker Gesture Canceled due to message no. %x\n"), pMsg->message);
			setState(GS_None);
			return MHR_Canceled;
		}
		break;
	case GS_Triggered:
		if ((pMsg->wParam & (m_bLeft ? MK_LBUTTON : MK_RBUTTON)) == 0
			|| (pMsg->message != (m_bLeft ? WM_RBUTTONDOWN : WM_LBUTTONDOWN)
			&& pMsg->message != (m_bLeft ? WM_RBUTTONUP : WM_LBUTTONUP)
			&& pMsg->message != WM_MOUSEMOVE)) {
			ATLTRACE(_T("Rocker Gesture Ended\n"));
			setState(GS_None);
			return MHR_GestureEnd;
		} else {
			return MHR_Swallowed;
		}
		break;
	}
	return MHR_NotHandled;
}
MessageHandleResult WheelHandler::handleMessageInternal(MSG* pMsg) {
	CPoint ptCurrent(pMsg->lParam);
	CSize dist;
	switch (getState()) {
	case GS_None:
		if (pMsg->message == WM_RBUTTONDOWN) {
			m_ptStart = ptCurrent;
			setState(GS_Initiated);
			ATLTRACE(_T("Wheel Gesture Initiated\n"));
			return MHR_Initiated;
		}
		break;
	case GS_Initiated:
		dist = ptCurrent - m_ptStart;
		if (pMsg->message == WM_MOUSEMOVE && (pMsg->wParam & MK_RBUTTON)) {
			if (abs(dist.cx) > 10 || abs(dist.cy) > 10) {
				setState(GS_None);
				ATLTRACE(_T("Wheel Gesture Canceled due to mouse moved too far away\n"));
				return MHR_Canceled;
			} else
				return MHR_Swallowed;
		} else if (pMsg->message == WM_MOUSEWHEEL && (pMsg->wParam & MK_RBUTTON)) {
			ATLTRACE(_T("Wheel Gesture Triggered\n"));
			setState(GS_Triggered);
			return MHR_Triggered;
		} else if (pMsg->message == WM_RBUTTONDOWN || pMsg->message == WM_RBUTTONDBLCLK) {
			ATLTRACE(_T("Duplicate Wheel Gesture Initiation\n"));
			return MHR_Discarded;
		} else {
			ATLTRACE(_T("Wheel Gesture Canceled due to message no. %x\n"), pMsg->message);
			setState(GS_None);
			return MHR_Canceled;
		}
		break;
	case GS_Triggered:
		if ((pMsg->message == WM_MOUSEMOVE || pMsg->message == WM_MOUSEWHEEL) && (pMsg->wParam & MK_RBUTTON)) {
			return MHR_Swallowed;
		} else {
			ATLTRACE(_T("Wheel Gesture Ended\n"));
			setState(GS_None);
			return MHR_GestureEnd;
		}
		break;
	}
	return MHR_NotHandled;
}
	/** Start tracking mouse in VWindow starting at ptFrom. Returns VTRUE on
	success, VFALSE if the user cancelled the operation or the resulting
	point was the same as the from point. The ending position will be
	stored in the ptResult VPoint object on success. This function handles
	capturing the mouse, and releasing it when needed. The mouse should NOT
	be captured before calling this function. During the tracking, the
	virtual function OnTract() will be called each time the mouse position
	changes. If the users presses the enter key during tracking, it has the
	same effect as stopping the tracking by letting the mouse button up.
	This is a valid way of saying "I'm done, and return the result".*/
	VBOOL			Track(	VWindow const&	window,
							VPoint const&	ptFrom,
							VPoint&			ptResult)
	{
		VASSERT(window.GetSafeWindow())

		/* Initialize ptResult to ptFrom.*/
		ptResult = ptFrom;

		/* This tracks the current position. Initialize to ptFrom.*/
		VPoint ptCurrent(ptFrom);

		/* Set initial variables.*/
		VBOOL	bResult =			VFALSE;
		VBOOL	bKeepGoing =		VTRUE;
		VBOOL	bReleaseCapture =	VFALSE;
		MSG		msg;

		/* Capture the mouse?*/
		if ( !window.IsMouseCaptured() )
		{
			window.SetCapture();
			bReleaseCapture = VTRUE;
		}

		while ( bKeepGoing && GetMessage(&msg, NULL, 0, 0) )
		{
			/* If we lost the capture for any reason, stop!*/
			if ( GetCapture() != window.GetHandle() )
				break;

			switch ( msg.message )
			{
				case WM_LBUTTONDOWN:
					if ( m_Options.IsNotSet(OPTION_RIGHT_MOUSE_TRACK) )
						break;
					/* Fall through to WM_RBUTTONDOWN.*/

				case WM_RBUTTONDOWN:
					bKeepGoing = VFALSE;
					break;

				case WM_RBUTTONUP:
					if ( m_Options.IsNotSet(OPTION_RIGHT_MOUSE_TRACK) )
						break;
					/* Fall through to WM_LBUTTONUP.*/

				case WM_LBUTTONUP:
					bKeepGoing =	VFALSE;
					bResult =		VTRUE;
					break;

				case WM_KEYDOWN:
				{
					if ( msg.wParam == VK_ESCAPE )
						bKeepGoing = VFALSE;
					else if ( msg.wParam == VK_RETURN )
					{
						bKeepGoing =	VFALSE;
						bResult =		VTRUE;
					}
					else if (
						(m_Options.IsNotSet(OPTION_DISABLE_KEYBOARD_MOVE)) &&
						(msg.wParam == VK_LEFT ||
						msg.wParam == VK_RIGHT ||
						msg.wParam == VK_UP ||
						msg.wParam == VK_DOWN) )
					{
						/* Get cursor position into ptCurrent.*/
						ptCurrent.GetCursorPosition();

						/* Modify position based on keystroke.*/
						if ( msg.wParam == VK_LEFT )
							ptCurrent.SubtractX(m_nKeyboardIncrement);
						else if ( msg.wParam == VK_RIGHT )
							ptCurrent.AddX(m_nKeyboardIncrement);
						else if ( msg.wParam == VK_UP )
							ptCurrent.SubtractY(m_nKeyboardIncrement);
						else
							ptCurrent.AddY(m_nKeyboardIncrement);

						/* Move the screen cursor to new location and convert
						ptCurrent back to client coordinates.*/
						SetCursorPos(VPOINT_BREAK(ptCurrent));
						window.ScreenToClient(ptCurrent);

						/* Call virtual function.*/
						CallOnTrack(window, ptFrom, ptCurrent);
					}

					break;
				}

				/* Call virtual function to show what we are doing.*/
				case WM_MOUSEMOVE:
				{
					ptCurrent.Set(	(VINT)(VSHORT)LOWORD(msg.lParam),
									(VINT)(VSHORT)HIWORD(msg.lParam));
					CallOnTrack(window, ptFrom, ptCurrent);
					break;
				}

				default:
					((VWindow*)&window)->WindowProc(	msg.hwnd,
														msg.message,
														msg.wParam,
														msg.lParam);
					break;
			}
		}

		/* Did the points change while being tracked?*/
		if ( bResult && ptCurrent == ptFrom )
			bResult = VFALSE;

		/* Release the mouse capturing?*/
		window.IsMouseCaptured(bReleaseCapture);

		/* Save out parameter?*/
		if ( bResult )
			ptResult = ptCurrent;

		/* Call virtual function to notify of end of tracking.*/
		OnTrackEnd(window, ptFrom, ptCurrent, bResult);

		return bResult;
	}