// This is part of the Professional User Interface Suite library.
// Copyright (C) 2001-2004 FOSS Software, Inc.
// All rights reserved.
//
// http://www.prof-uis.com
// http://www.fossware.com
// mailto:support@fossware.com
//
// This source code can be used, modified and redistributed
// under the terms of the license agreement that is included
// in the Professional User Interface Suite package.
//
// Warranties and Disclaimers:
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND
// INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
// IN NO EVENT WILL FOSS SOFTWARE INC. BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES,
// INCLUDING DAMAGES FOR LOSS OF PROFITS, LOSS OR INACCURACY OF DATA,
// INCURRED BY ANY PERSON FROM SUCH PERSON'S USAGE OF THIS SOFTWARE
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

#include "stdafx.h"

#if (!defined __EXT_MOUSECAPTURESINK_H)
	#include <../Src/ExtMouseCaptureSink.h>
#endif

#ifdef __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS
	#if (!defined __EXT_POPUP_MENU_WND_H)
		#include <ExtPopupMenuWnd.h>
	#endif
#endif // __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CExtMouseCaptureSink

#ifdef __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS
volatile HHOOK CExtMouseCaptureSink::g_hMouseHook = NULL;
volatile HWND CExtMouseCaptureSink::g_hWndCapture = NULL;
CCriticalSection CExtMouseCaptureSink::g_cs;
#endif // __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS

CExtMouseCaptureSink::CExtMouseCaptureSink()
{
}

CExtMouseCaptureSink::~CExtMouseCaptureSink()
{
}

HWND CExtMouseCaptureSink::SetCapture( HWND hWnd )
{
#ifdef __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS

	if(		hWnd == NULL
		||	( ! ::IsWindow(hWnd) )
		)
	{
		ReleaseCapture();
		return NULL;
	}

HWND hWndOldCapture = NULL;
CSingleLock _slo( &g_cs, TRUE );
	if( g_hMouseHook == NULL )
	{
		ASSERT( g_hWndCapture == NULL );
		g_hMouseHook =
			::SetWindowsHookEx(
				WH_MOUSE,
				_HookMouseProc, 
				0,
				::GetCurrentThreadId()
				);
		ASSERT( g_hMouseHook != NULL );
	}
	else
	{
		ASSERT( g_hWndCapture != NULL );
		hWndOldCapture = g_hWndCapture;
	}

	if( g_hMouseHook != NULL )
		g_hWndCapture = hWnd;
	else
	{
		hWnd = g_hWndCapture = NULL;
	}

	_slo.Unlock();
	
	if( hWndOldCapture != NULL
		&& ::IsWindow( hWndOldCapture )
		)
		::SendMessage( hWndOldCapture, WM_CAPTURECHANGED, 0L, LPARAM(hWnd) );
	///// (debug/preview) ::SendMessage( hWnd, WM_CAPTURECHANGED, 0L, LPARAM(hWnd) );

	return hWnd;

#else // __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS

	return ::SetCapture( hWnd );

#endif // else from __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS
}

HWND CExtMouseCaptureSink::GetCapture()
{
#ifdef __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS

	return g_hWndCapture;

#else // __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS

	return ::GetCapture();

#endif // else from __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS
}

BOOL CExtMouseCaptureSink::ReleaseCapture()
{
#ifdef __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS

	if( g_hWndCapture == NULL )
		return TRUE;

HWND hWndOldCapture = NULL;
CSingleLock _slo( &g_cs, TRUE );
	if( g_hMouseHook != NULL )
	{
		ASSERT( g_hWndCapture != NULL );
		::UnhookWindowsHookEx( g_hMouseHook );
		g_hMouseHook = NULL;
		hWndOldCapture = g_hWndCapture;
		g_hWndCapture = NULL;
	}
	else
	{
		ASSERT( g_hWndCapture == NULL );
	}
	_slo.Unlock();
	
	if( hWndOldCapture != NULL
		&& ::IsWindow( hWndOldCapture )
		)
		::SendMessage( hWndOldCapture, WM_CAPTURECHANGED, 0L, 0L );
	
	return TRUE;

#else // __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS

	return ::ReleaseCapture();

#endif // else from __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS
}

#ifdef __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS

LRESULT CALLBACK CExtMouseCaptureSink::_HookMouseProc(
	int nCode,      // hook code
	WPARAM wParam,  // message identifier
	LPARAM lParam   // mouse coordinates
	)
{
	__PROF_UIS_MANAGE_STATE;

MOUSEHOOKSTRUCT * lpMS = (MOUSEHOOKSTRUCT*)lParam;
	ASSERT( lpMS != NULL );
HWND hWndHook = (HWND)g_hWndCapture;
	if(		lpMS->hwnd != hWndHook
		&&	hWndHook != NULL
		&&	::IsWindow(hWndHook)
		)
	{

		UINT nMsgToSend = WM_NULL;
		
		switch( wParam )
		{
		case WM_MOUSEMOVE:
		case WM_LBUTTONDOWN:
		case WM_LBUTTONUP:
		case WM_LBUTTONDBLCLK:
		case WM_RBUTTONDOWN:
		case WM_RBUTTONUP:
		case WM_RBUTTONDBLCLK:
		case WM_MBUTTONDOWN:
		case WM_MBUTTONUP:
		case WM_MBUTTONDBLCLK:
				nMsgToSend = wParam;
			break;

		case WM_NCMOUSEMOVE:
			nMsgToSend = WM_MOUSEMOVE;
			break;
		case WM_NCLBUTTONDOWN:
			nMsgToSend = WM_LBUTTONDOWN;
			break;
		case WM_NCLBUTTONUP:
			nMsgToSend = WM_NCLBUTTONUP;
			break;
		case WM_NCLBUTTONDBLCLK:
			nMsgToSend = WM_LBUTTONDBLCLK;
			break;
		case WM_NCRBUTTONDOWN:
			nMsgToSend = WM_RBUTTONDOWN;
			break;
		case WM_NCRBUTTONUP:
			nMsgToSend = WM_RBUTTONUP;
			break;
		case WM_NCRBUTTONDBLCLK:
			nMsgToSend = WM_RBUTTONDBLCLK;
			break;
		case WM_NCMBUTTONDOWN:
			nMsgToSend = WM_MBUTTONDOWN;
			break;
		case WM_NCMBUTTONUP:
			nMsgToSend = WM_MBUTTONUP;
			break;
		case WM_NCMBUTTONDBLCLK:
			nMsgToSend = WM_MBUTTONDBLCLK;
			break;

		} // switch( wParam )

		if( nMsgToSend != WM_NULL )
		{
			WPARAM wParamMsg = 0L;
			if( CExtPopupMenuWnd::IsKeyPressed(VK_LBUTTON) )
				wParamMsg |= MK_LBUTTON;
			if( CExtPopupMenuWnd::IsKeyPressed(VK_RBUTTON) )
				wParamMsg |= MK_RBUTTON;
			if( CExtPopupMenuWnd::IsKeyPressed(VK_MBUTTON) )
				wParamMsg |= MK_MBUTTON;
			if( CExtPopupMenuWnd::IsKeyPressed(VK_SHIFT) )
				wParamMsg |= MK_SHIFT;
			if( CExtPopupMenuWnd::IsKeyPressed(VK_CONTROL) )
				wParamMsg |= MK_CONTROL;

			CPoint pt( lpMS->pt );
			VERIFY( ::ScreenToClient( hWndHook, &pt ) );
			LPARAM lParamMsg = MAKELPARAM( pt.x, pt.y );

			::SendMessage(
				hWndHook,
				nMsgToSend,
				wParamMsg,
				lParamMsg
				);
			return -1; // eat!
		
		} // if( nMsgToSend != WM_NULL )
	} // if( hWndHook != NULL && ::IsWindow(hWndHook) )

	return
		::CallNextHookEx(
			g_hMouseHook,
			nCode,
			wParam,
			lParam
			);
}

#endif // __EXT_MOUSECAPTURESINK_USES_WIN_HOOKS

