//
//	Copyright © 2006 Nemetschek North America 
//	All Rights Reserved.
//
// Created by Vladislav Stanev

#include "StdHeaders.h"

#include "CustomCtrl.h"
#include "Dialog.h"

#include <map>


#if _MINICAD_
	#ifdef _WINDOWS
	# include <commctrl.h>
	#endif
#endif

using namespace VectorWorks::VWUI;

SCustomCtrlToolTip::SCustomCtrlToolTip()
{
	fRect.MakeEmpty();
	fbTrackMouse		= false;
	fPos.Set( -1, -1 );
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
VWCustomCtrl::VWCustomCtrl(TControlID id, bool bDefaultPaint)
: VWControl( id )
{
#ifdef _WINDOWS
	fhParentWnd			= NULL;
	fhCustomWnd			= NULL;
	fpDefaultWndProc	= NULL;
	fpToolTipItem		= NULL;
	fbTrackingMouse		= false;
#else
	fpParentWnd			= NULL;
#endif

	fbDefaultPaint		= bDefaultPaint;
	fpActiveToolTip		= NULL;
	fbToolTipsEnabled	= false;
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
VWCustomCtrl::~VWCustomCtrl()
{
#ifdef _WINDOWS
	if ( fhToolTip ) {
		DestroyWindow( fhToolTip );
	}
	if ( fpToolTipItem ) {
		TOOLINFO*	pToolTip = (TOOLINFO*) fpToolTipItem;
		if ( pToolTip->lpszText ) {
			delete pToolTip->lpszText;
		}

		free( pToolTip );
		pToolTip	= NULL;
	}
	fpToolTipItem	= NULL;
	fhToolTip		= NULL;
#else
#endif
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
bool VWCustomCtrl::CreateControl(VWDialog* pDlg, Uint16 width, Uint16 height)
{
	::GS_CreateCustomControl( gCBP, pDlg->GetControlID(), fControlID, width, height );
	return VWControl::CreateControl( pDlg );
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnControlAttached()
{
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnAfterPaint()
{
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
bool VWCustomCtrl::OnEvent(SCustomCtrlGeneralEvent& event)
{
	return false;
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnMouseMoved(const SCustomCtrlMouseEvent& event)
{
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnMouseDown(const SCustomCtrlMouseEvent& event)
{
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnMouseUp(const SCustomCtrlMouseEvent& event)
{
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnMouseClick(const SCustomCtrlMouseEvent& event)
{
	if ( fpParentDlg ) {
		long        controlID    = fControlID;
		// call the dialog to dispatch normal event to the dialog
		// though the event map, as if the control is being click and the message
		fpParentDlg->LocalDialogEventHandler( controlID, NULL );
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnMouseDblClick(const SCustomCtrlMouseEvent& event)
{
	if ( fpParentDlg ) {
		long        controlID    = -fControlID;
		// call the dialog to dispatch normal event to the dialog
		// though the event map, as if the control is being click and the message
		fpParentDlg->LocalDialogEventHandler( controlID, NULL );
	}
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnMouseRDown(const SCustomCtrlMouseEvent& event)
{
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnMouseRUp(const SCustomCtrlMouseEvent& event)
{
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnMouseRClick(const SCustomCtrlMouseEvent& event)
{
	if ( fpParentDlg ) {
		long        controlID	= fControlID;
		long		data		= 1;	// right
		// call the dialog to dispatch normal event to the dialog
		// though the event map, as if the control is being click and the message
		fpParentDlg->LocalDialogEventHandler( controlID, & data );
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnMouseRDblClick(const SCustomCtrlMouseEvent& event)
{
	if ( fpParentDlg ) {
		long        controlID	= -fControlID;
		long		data		= 1;	// right
		// call the dialog to dispatch normal event to the dialog
		// though the event map, as if the control is being click and the message
		fpParentDlg->LocalDialogEventHandler( controlID, & data );
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnMouseMDown(const SCustomCtrlMouseEvent& event)
{
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnMouseMUp(const SCustomCtrlMouseEvent& event)
{
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnMouseMClick(const SCustomCtrlMouseEvent& event)
{
	if ( fpParentDlg ) {
		long        controlID	= fControlID;
		long		data		= 2;	// middle
		// call the dialog to dispatch normal event to the dialog
		// though the event map, as if the control is being click and the message
		fpParentDlg->LocalDialogEventHandler( controlID, & data );
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnMouseMDblClick(const SCustomCtrlMouseEvent& event)
{
	if ( fpParentDlg ) {
		long        controlID	= -fControlID;
		long		data		= 2;	// middle
		// call the dialog to dispatch normal event to the dialog
		// though the event map, as if the control is being click and the message
		fpParentDlg->LocalDialogEventHandler( controlID, & data );
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void  VWCustomCtrl::OnMouseWheel(const SCustomCtrlMouseEvent& event, Sint16 deltaX, Sint16 deltaY)
{
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnKeyDown(const SCustomCtrlKeyEvent& event)
{
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnKeyUp(const SCustomCtrlKeyEvent& event)
{
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::OnChar(const SCustomCtrlKeyEvent& event)
{
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
bool VWCustomCtrl::OnToolTip(size_t id, SCustomCtrlToolTip& inoutToolTip)
{
	return true;
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::Refresh()
{
	if ( fpParentDlg ) {
		::GS_RefreshItem( gCBP, fpParentDlg->GetDialogID(), fControlID ); 
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::UpdateScrollArea(Uint16 width, Uint16 height, Uint16 leftStart, Uint16 topStart, Uint16 stepSize, bool bLiveUpdate, bool bAlwaysShowHoriz, bool bAlwaysShowVert)
{
	if ( fpParentDlg ) {
		::GS_UpdateCustomControlScrollArea( gCBP, fpParentDlg->GetDialogID(), fControlID, width, height, leftStart, topStart, stepSize, bLiveUpdate, bAlwaysShowHoriz, bAlwaysShowVert );
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::GetScrollArea(Uint16& outLeftStart, Uint16& outTopStart)
{
	int		leftPos		= 0;
	int		topPos		= 0;
	if ( fpParentDlg ) {
		::GS_GetCustomControlScrollArea(gCBP, fpParentDlg->GetDialogID(), fControlID, & leftPos, & topPos );
	}
	
	outLeftStart		= leftPos;
	outTopStart			= topPos;
}


//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
ViewCoord VWCustomCtrl::GetControlWidth() const
{
#ifdef _WINDOWS
	RECT r;
	::GetClientRect( fhCustomWnd, & r );
	return (ViewCoord) (r.right - r.left);
#else
	return fCustomCtrlRect.Width();
#endif
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
ViewCoord VWCustomCtrl::GetControlHeight() const
{
#ifdef _WINDOWS
	RECT r;
	::GetClientRect( fhCustomWnd, & r );
	return (ViewCoord) (r.bottom - r.top);
#else
	return fCustomCtrlRect.Height();
#endif
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
ViewRect VWCustomCtrl::GetControlRect() const
{
#if GS_WIN
	return fCustomCtrlRect;
#else
	ViewRect	rc;
	rc.left		= fControlRect.left;
	rc.right	= fControlRect.right;
	rc.top		= fControlRect.top;
	rc.bottom	= fControlRect.bottom;
	return rc;
#endif
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
Uint32 VWCustomCtrl::GetOSParentHandle() const
{
#if GS_WIN
	return (Uint32) fhParentWnd;
#else
	return (Uint32) fpParentWnd;
#endif
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
Uint32 VWCustomCtrl::GetOSControlHandle() const
{
#if GS_WIN
	return (Uint32) fhCustomWnd;
#else
	return (Uint32) fControlRef;
#endif
}



//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::EnableToolTips(bool bEnable)
{
	fbToolTipsEnabled	= bEnable;

#if GS_WIN
	if ( fpToolTipItem == NULL ) {
		fpToolTipItem			= malloc( sizeof(TOOLINFO) );

		TOOLINFO*	pToolTip	= (TOOLINFO*) fpToolTipItem;
		memset( pToolTip, 0, sizeof(TOOLINFO) );

		// Create a ToolTip.
		fhToolTip = CreateWindowEx(	WS_EX_TOPMOST,
									TOOLTIPS_CLASS, NULL,
									WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,		
									CW_USEDEFAULT, CW_USEDEFAULT,
									CW_USEDEFAULT, CW_USEDEFAULT,
									fhParentWnd,
									NULL, (HINSTANCE) NULL, NULL );

		DSTOPIF( fhToolTip == NULL, (kVStanev, "Cannot create tooltip window.") );
		if ( fhToolTip ) {   
			// Set up tool information.
			// In this case, the "tool" is the entire parent window.
			pToolTip->cbSize	= sizeof(TOOLINFO);
			pToolTip->uFlags	= TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE | TTF_SUBCLASS | TTF_TRANSPARENT;
			pToolTip->hwnd		= fhParentWnd;
			pToolTip->hinst		= (HINSTANCE) NULL;
			pToolTip->lpszText	= new char[ 1024 ];
			pToolTip->uId		= (UINT_PTR) fhParentWnd;
			GetClientRect( fhParentWnd, & pToolTip->rect );

			// Associate the ToolTip with the tool window.
			SendMessage( fhToolTip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) pToolTip );
		}
		else {
			delete pToolTip;
			fpToolTipItem	= NULL;
		}
	}

	if ( ! fbToolTipsEnabled ) {
		if ( fpActiveToolTip ) {
			if ( ! fpActiveToolTip->fbCustomPos ) {
				fpActiveToolTip->fToolTip.fPos.Set( -1, -1 );
			}
			fpActiveToolTip		= NULL;
		}

		int	initial	= GetDoubleClickTime();
		int	autopop	= GetDoubleClickTime() * 10;
		int	reshow	= GetDoubleClickTime() / 5;

		SendMessage( fhToolTip, TTM_SETDELAYTIME, TTDT_INITIAL,		MAKELONG(initial, 0) );
		SendMessage( fhToolTip, TTM_SETDELAYTIME, TTDT_AUTOPOP,		MAKELONG(autopop, 0) );
		SendMessage( fhToolTip, TTM_SETDELAYTIME, TTDT_RESHOW,		MAKELONG(reshow, 0) );
		//SendMessage( fhToolTip, TTM_SETDELAYTIME, TTDT_AUTOMATIC,	MAKELONG(, 0) );

		SendMessage( fhToolTip, TTM_SETMAXTIPWIDTH, 0, 300 );

		SendMessage( fhToolTip, TTM_TRACKACTIVATE, (WPARAM) FALSE, (LPARAM) (TOOLINFO*) fpToolTipItem );
		fbTrackingMouse		= false;
	}
#else

#endif
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::ClearActiveTooltip( void )
{
	if ( fpActiveToolTip ) {
		if ( !fpActiveToolTip->fbCustomPos ) {
			fpActiveToolTip->fToolTip.fPos.Set( -1, -1 );
		}
		
		fpActiveToolTip		= NULL;
	}
}



void VWCustomCtrl::RemoveAllToolTips()
{
	ClearActiveTooltip();
	
	farrToolTips.clear();
}

void VWCustomCtrl::RemoveToolTip(size_t id)
{
	ASSERTN( kVStanev, id < farrToolTips.size() );
	
	if ( id < farrToolTips.size() ) {
		SToolTipDef	*tooltip = &(farrToolTips[id]);
		
		if ( tooltip == fpActiveToolTip )
			ClearActiveTooltip();
		
		farrToolTips.erase( farrToolTips.begin() + id );
		fpActiveToolTip = NULL;
	}
}

size_t VWCustomCtrl::AddToolTip(const SCustomCtrlToolTip& toolTip)
{
	SToolTipDef		def;
	def.fToolTip		= toolTip;
	// make sure the tool tip appears on the rect borders too
	if ( ! def.fToolTip.fRect.IsEmpty() ) {
		def.fToolTip.fRect.Outset( 1, 1 );
	}
	// remember if we have fixed tool tip position
	def.fbCustomPos		= def.fToolTip.fPos.x >= 0 && def.fToolTip.fPos.y >= 0;
	def.fTipID			= farrToolTips.size();
	farrToolTips.push_back( def );

	return def.fTipID;
}

// ==============================================================================
#if GS_WIN
typedef std::map<HWND, VWCustomCtrl*>	TControlInstanceMap;
static TControlInstanceMap				s_controlsInstances;

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::AttachControl()
{
	if ( fpParentDlg ) {
		fhParentWnd			= (HWND) ::GS_OverrideControl( gCBP, fpParentDlg->GetControlID(), fControlID,  NULL );
		HWND hWndControl	= ::GetDlgItem( fhParentWnd, fControlID );
		ASSERTN( kVStanev, hWndControl );
		if ( hWndControl != NULL ) {
			// override the window proc that is to receive windows events
			fpDefaultWndProc	= (WNDPROC) ::GetWindowLongPtr( hWndControl, GWLP_WNDPROC );
			VERIFYN( kVStanev, ::SetWindowLongPtr( hWndControl, GWLP_WNDPROC, (LPARAM)(WNDPROC) VWCustomCtrl::_CustomWindowProc ) );

			// make sure needed flags are set
			//	SS_NOTIFY	- sends notification messages to static controls
			LONG	style		= GetWindowLongPtr( hWndControl, GWL_STYLE );
			::SetWindowLongPtr( hWndControl, GWL_STYLE, style | SS_NOTIFY );

			fhCustomWnd			= hWndControl;
			s_controlsInstances.insert( TControlInstanceMap::value_type( hWndControl, this ) );

			this->OnControlAttached();
		}
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
/*static*/ LRESULT VWCustomCtrl::_CustomWindowProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	TControlInstanceMap::iterator	it			= s_controlsInstances.find( hWnd );
	VWCustomCtrl*					pInstance	= it != s_controlsInstances.end() ? it->second : NULL;
	return pInstance
			? pInstance->CustomWindowProc( hWnd, nMsg, wParam, lParam )
			: ::DefWindowProc(hWnd, nMsg, wParam, lParam);
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
LRESULT VWCustomCtrl::CustomWindowProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	LRESULT	result	= 0L;

	SCustomCtrlGeneralEvent		event;
	event.wnd						= (Uint32) hWnd;
	event.message					= nMsg;
	event.data1						= wParam;
	event.data2						= lParam;
	event.result					= 0;
	if ( this->OnEvent( event ) ) {
		// event has been handled by the general event handling funtino
		// so it's not necessary to recognize it
		result	= event.result;
	}
	else {
		switch( nMsg ) {
			case WM_PAINT: {
					if ( fbDefaultPaint ) {
						// use default window proc to paint for this window
						result		= (*fpDefaultWndProc)( hWnd, nMsg, wParam, lParam );
					}
					else {
						PAINTSTRUCT		ps;
						HDC				hDC	= ::BeginPaint( hWnd, & ps );

						{ // keep this block so 'VWCustomCtrlDC' could be deleted before OnAfterPaint call
							VWCustomCtrlDC	dc( (unsigned long)hWnd, (void*)hDC, 0, 0 );

							RECT r;
							::GetClientRect( hWnd, & r );

							fCustomCtrlRect.SetLTRB( (ViewCoord)r.left, (ViewCoord)r.top, (ViewCoord)r.right, (ViewCoord)r.bottom );
							dc.SetClipRect( fCustomCtrlRect );

							this->OnPaint( dc );
						} // keep this block so 'VWCustomCtrlDC' could be deleted before OnAfterPaint call
	
						this->OnAfterPaint();

						::EndPaint( hWnd, & ps );
					}
				} break;

			case WM_LBUTTONDOWN:
			case WM_RBUTTONDOWN:
			case WM_MBUTTONDOWN: {
					SCustomCtrlMouseEvent	event;
					event.x							= ((int)(short)LOWORD(lParam));
					event.y							= ((int)(short)HIWORD(lParam));
					event.bShiftDown				= wParam & MK_SHIFT;
					event.bCtrlDown					= wParam & MK_CONTROL;

					if ( nMsg == WM_LBUTTONDOWN ) 			this->OnMouseDown( event );
					else if ( nMsg == WM_RBUTTONDOWN )		this->OnMouseRDown( event );
					else if ( nMsg == WM_MBUTTONDOWN )		this->OnMouseMDown( event );
					else									DSTOP((kVStanev, "something's wrong with the message in VWCustomCtrl::CustomWindowProc" ));
					::SetFocus( hWnd );
				} break;

			case WM_LBUTTONUP:
			case WM_RBUTTONUP:
			case WM_MBUTTONUP: {
					SCustomCtrlMouseEvent	event;
					event.x							= ((int)(short)LOWORD(lParam));
					event.y							= ((int)(short)HIWORD(lParam));
					event.bShiftDown				= wParam & MK_SHIFT;
					event.bCtrlDown					= wParam & MK_CONTROL;

					if ( nMsg == WM_LBUTTONUP ) 			this->OnMouseUp( event );
					else if ( nMsg == WM_RBUTTONUP )		this->OnMouseRUp( event );
					else if ( nMsg == WM_MBUTTONUP )		this->OnMouseMUp( event );
					else									DSTOP((kVStanev, "something's wrong with the message in VWCustomCtrl::CustomWindowProc" ));

					if ( nMsg == WM_LBUTTONUP ) 			this->OnMouseClick( event );
					else if ( nMsg == WM_RBUTTONUP )		this->OnMouseRClick( event );
					else if ( nMsg == WM_MBUTTONUP )		this->OnMouseMClick( event );
					else									DSTOP((kVStanev, "something's wrong with the message in VWCustomCtrl::CustomWindowProc" ));
				} break;

			case WM_LBUTTONDBLCLK:
			case WM_RBUTTONDBLCLK:
			case WM_MBUTTONDBLCLK: {
					SCustomCtrlMouseEvent	event;
					event.x							= ((int)(short)LOWORD(lParam));
					event.y							= ((int)(short)HIWORD(lParam));
					event.bShiftDown				= wParam & MK_SHIFT;
					event.bCtrlDown					= wParam & MK_CONTROL;

					if ( nMsg == WM_LBUTTONDBLCLK ) 		this->OnMouseDblClick( event );
					else if ( nMsg == WM_RBUTTONDBLCLK )	this->OnMouseRDblClick( event );
					else if ( nMsg == WM_MBUTTONDBLCLK )	this->OnMouseMDblClick( event );
					else									DSTOP((kVStanev, "something's wrong with the message in VWCustomCtrl::CustomWindowProc" ));
					::SetFocus( hWnd );
				} break;

			case WM_ERASEBKGND:
				// Eliminates flicker.
				// We could place a this->OnEraseBackground here.
				break;

			case WM_MOUSEMOVE: {
					SCustomCtrlMouseEvent	event;

					event.x							= ((int)(short)LOWORD(lParam));
					event.y							= ((int)(short)HIWORD(lParam));
					event.bShiftDown				= wParam & MK_SHIFT;
					event.bCtrlDown					= wParam & MK_CONTROL;
					
					this->OnMouseMoved( event );

					if ( fbToolTipsEnabled && fpToolTipItem ) {
						static ViewPt	oldPt( 0, 0 );
						bool			bActivateToolTip	= false;
						if ((event.x != oldPt.x) || (event.y != oldPt.y)) {
							oldPt.x						= event.x;
							oldPt.y						= event.y;

							ViewPt		scrollAreaPoint;
							Uint16		leftStart;
							Uint16		topStart;
							
							this->GetScrollArea( leftStart, topStart );

							leftStart	= ( leftStart == Uint16(-1) ) ? 0 : leftStart;
							topStart	= ( topStart == Uint16(-1) ) ? 0 : topStart;

							scrollAreaPoint.x	= event.x + leftStart;
							scrollAreaPoint.y	= event.y + topStart;
							
							// check the existing tool tip
							if ( fpActiveToolTip &&
								! (fpActiveToolTip->fToolTip.fRect.IsEmpty() || fpActiveToolTip->fToolTip.fRect.PtInRect( scrollAreaPoint )) )
							{
								// this tool tip is no longer active
								if ( ! fpActiveToolTip->fbCustomPos ) {
									fpActiveToolTip->fToolTip.fPos.Set( -1, -1 );
								}
								fpActiveToolTip		= NULL;
								SendMessage( fhToolTip, TTM_TRACKACTIVATE, (WPARAM) FALSE, (LPARAM) (TOOLINFO*) fpToolTipItem );
							}

							bActivateToolTip = true;

							// find the tool
							SToolTipDef*	pFoundToolTip	= NULL;
							size_t			toolCnt			= farrToolTips.size();
							for(size_t i=0; i<toolCnt; i++) {
								SToolTipDef&	toolTip	= farrToolTips[ i ];
								if ( toolTip.fToolTip.fRect.IsEmpty() ) {
									// we just found a tool tip for the whole window
									// continue searching for more bound restricted one
									pFoundToolTip	= & toolTip;
								}
								else if ( toolTip.fToolTip.fRect.PtInRect( scrollAreaPoint ) ) {
									// op, here is bound restricted one, that's it! STOP!
									pFoundToolTip	= & toolTip;
									break;
								}
							}
							
							if ( fpActiveToolTip == NULL ) {
								fpActiveToolTip		= pFoundToolTip;
							}
							else if ( pFoundToolTip && pFoundToolTip->fToolTip.fRect.PtInRect( scrollAreaPoint ) ) {
								// if the found tool tip is a region it overrides the active tool tip
								fpActiveToolTip		= pFoundToolTip;
							}

							if ( fpActiveToolTip ) {
								// Position the ToolTip. 
								if ( fpActiveToolTip->fToolTip.fbTrackMouse ) {
									// The coordinates are adjusted so that the ToolTip does not overlap the mouse pointer.
									fpActiveToolTip->fToolTip.fPos.Set( event.x + 10, event.y - 20 );
								}
								else if ( fpActiveToolTip->fToolTip.fPos.x < 0 || fpActiveToolTip->fToolTip.fPos.y < 0 ) {
									fpActiveToolTip->fToolTip.fPos.Set( event.x + 10, event.y - 20 );
								}

								if ( this->OnToolTip( fpActiveToolTip->fTipID, fpActiveToolTip->fToolTip ) ) {
									TOOLINFO*	pTooltipInfo	= (TOOLINFO*) fpToolTipItem;
									// Update the text.
									fpActiveToolTip->fToolTip.fText.CopyInto( pTooltipInfo->lpszText );
									SendMessage( fhToolTip, TTM_SETTOOLINFO, 0, (LPARAM) ((TOOLINFO*) fpToolTipItem) );
									SendMessage( fhToolTip, TTM_SETMAXTIPWIDTH, 0, 300 );

									POINT pt = { fpActiveToolTip->fToolTip.fPos.x, fpActiveToolTip->fToolTip.fPos.y }; 
									ClientToScreen( fhCustomWnd, & pt );

									Uint16		leftStart	= 0;
									Uint16		topStart	= 0;
									this->GetScrollArea( leftStart, topStart );

									// compensate tool position according to the scroll
									// so it appear on correct location
									pt.x		+= leftStart;
									pt.y		-= topStart;

									SendMessage( fhToolTip, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(pt.x, pt.y) );
								}
							}
						}

						if ( ! fbTrackingMouse ) {
							// Request notification when the mouse leaves.
							TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT) };
							tme.hwndTrack		= fhCustomWnd;
							tme.dwFlags			= TME_LEAVE;
							TrackMouseEvent( & tme );

							bActivateToolTip	= true;
							fbTrackingMouse		= true;
						}

						if ( fpActiveToolTip && 
							 bActivateToolTip ) {
							// Activate the ToolTip.
							SendMessage( fhToolTip, TTM_TRACKACTIVATE, (WPARAM) TRUE, (LPARAM) (TOOLINFO*) fpToolTipItem );
						}
					}
				} break;

			case WM_MOUSELEAVE: {
					if ( fbToolTipsEnabled ) {
						POINT	pt;
						GetCursorPos( & pt );
						// The mouse pointer has left our window. Deactivate the ToolTip.
						// make sure we dont stop the tooltip if mouse over the tool tip
						HWND	hCursorOverWnd	= WindowFromPoint( pt );
						if ( fpToolTipItem && hCursorOverWnd != fhToolTip ) {
							if ( fpActiveToolTip ) {
								if ( ! fpActiveToolTip->fbCustomPos ) {
									fpActiveToolTip->fToolTip.fPos.Set( -1, -1 );
								}
								fpActiveToolTip		= NULL;
							}
							SendMessage( fhToolTip, TTM_TRACKACTIVATE, (WPARAM) FALSE, (LPARAM) (TOOLINFO*) fpToolTipItem );
						}
					}
					fbTrackingMouse		= false;
					result				= FALSE;
				} break;

			case WM_MOUSEWHEEL: {
					DWORD					fwKeys	= (LOWORD(wParam));
					short 					zDelta	= GET_WHEEL_DELTA_WPARAM(wParam);
					// correct the delta to be 1 per step
					zDelta							/= WHEEL_DELTA;

					SCustomCtrlMouseEvent	event;

					event.x							= ((int)(short)LOWORD(lParam));
					event.y							= ((int)(short)HIWORD(lParam));
					event.bShiftDown				= fwKeys & MK_SHIFT;
					event.bCtrlDown					= fwKeys & MK_CONTROL;

					this->OnMouseWheel( event, 0, zDelta );
				} break;

			case WM_KEYDOWN:
			case WM_KEYUP: {
					SCustomCtrlKeyEvent		event;
					event.keyCode					= wParam;
					switch( wParam ) {
						case VK_SHIFT:			event.keyType = kKeyType_Shift;		break;
						case VK_CONTROL:		event.keyType = kKeyType_Control;	break;
						case VK_SPACE:			event.keyType = kKeyType_Space;		break;
						case VK_RETURN:			event.keyType = kKeyType_Enter;		break;
						case VK_ESCAPE:			event.keyType = kKeyType_Escape;	break;
						case VK_INSERT:			event.keyType = kKeyType_Insert;	break;
						case VK_DELETE:			event.keyType = kKeyType_Delete;	break;
						case VK_BACK:			event.keyType = kKeyType_BackSpace;	break;
						case VK_HOME:			event.keyType = kKeyType_Home;		break;
						case VK_END:			event.keyType = kKeyType_End;		break;
						case VK_PRIOR:			event.keyType = kKeyType_PageUp;	break;
						case VK_NEXT:			event.keyType = kKeyType_PageDown;	break;
						case VK_TAB:			event.keyType = kKeyType_Tab;		break;
						case VK_LEFT:  			event.keyType = kKeyType_Left;		break;
						case VK_RIGHT: 			event.keyType = kKeyType_Right;		break;
						case VK_UP: 			event.keyType = kKeyType_Up;		break;
						case VK_DOWN:  			event.keyType = kKeyType_Down;		break;
						case VK_F1:				event.keyType = kKeyType_F1;		break;
						case VK_F2:				event.keyType = kKeyType_F2;		break;
						case VK_F3:				event.keyType = kKeyType_F3;		break;
						case VK_F4:				event.keyType = kKeyType_F4;		break;
						case VK_F5:				event.keyType = kKeyType_F5;		break;
						case VK_F6:				event.keyType = kKeyType_F6;		break;
						case VK_F7:				event.keyType = kKeyType_F7;		break;
						case VK_F8:				event.keyType = kKeyType_F8;		break;
						case VK_F9:				event.keyType = kKeyType_F9;		break;
						case VK_F10:			event.keyType = kKeyType_F10;		break;
						case VK_F11:			event.keyType = kKeyType_F11;		break;
						case VK_F12:			event.keyType = kKeyType_F12;		break;
						default:				event.keyType = kKeyType_Unknown;	break;
					}

					if ( nMsg == WM_KEYDOWN )		this->OnKeyDown( event );
					else if ( nMsg == WM_KEYUP )	this->OnKeyUp( event );
				} break;

			case WM_CHAR : {
					SCustomCtrlKeyEvent		event;
					event.keyCode					= wParam;
					event.keyType					= kKeyType_Unknown;
					this->OnChar( event );
				} break;

			case WM_GETDLGCODE: {
 					//result = DLGC_WANTCHARS | DLGC_WANTALLKEYS | DLGC_WANTARROWS | DLGC_WANTTAB;
				} break;

			case WM_COMMAND: {
					HWND		hControl	= (HWND) lParam;
					// reflect the message back to the control
					::SendMessage( hControl, WM_COMMAND, wParam, lParam );
				} break;

			default: {
					/* We have this default proc call here commented
					 * becaues it messes up with scroll bars behaviour of the custom control
					 * And since there is no advantage of calling the default wnd proc here and now
					 * We have it commented. If uncommening be sure to fix the scroll bar problems
					 * Vlado, 3/6/08
					 *
					if ( fpDefaultWndProc ) {
						result	= (*fpDefaultWndProc)( hWnd, nMsg, wParam, lParam );
					}
					else {
						result	= ::DefWindowProc( hWnd, nMsg, wParam, lParam );
					}*/

					result	= ::DefWindowProc( hWnd, nMsg, wParam, lParam );
				} break;
		}
	}

	return result;
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
#else

typedef std::map<Uint64, VWCustomCtrl*>		TControlInstanceMap;
static TControlInstanceMap					s_controlsInstances;

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::AttachControl()
{
	if ( fpParentDlg ) {
		Uint64				uniqueID;
		uniqueID = fpParentDlg->GetControlID();
		uniqueID = uniqueID << 32;
		uniqueID = uniqueID | fControlID;
		
		//uniqueID = ( fpParentDlg->GetControlID() << 32 ) | fControlID;
		
		fpParentWnd	= ::GS_OverrideControl( gCBP, fpParentDlg->GetControlID(), fControlID, (void*) VWCustomCtrl::_CustomMacHandler );
			
		ControlID		ctrlID;
		ctrlID.signature	= 'CUST';
		ctrlID.id			= fControlID;
		
		fControlRef			= NULL;
		GetControlByID( (WindowRef) fpParentWnd, & ctrlID, & fControlRef );
		
		GetControlBounds( fControlRef, & fControlRect );

		/*
		FSSpec fileSpec;
		FSMakeFSSpec( 0, 0, "\pname.txt", & fileSpec );
		
		DumpControlHierarchy( (WindowRef) fpParentWnd, & fileSpec );
		*/
			
		s_controlsInstances.insert( TControlInstanceMap::value_type( uniqueID, this ) );

		this->OnControlAttached();
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
/*static*/ void VWCustomCtrl::_CustomMacHandler( long inMessage, void *ioParam, void *paneVP, int dialogID, int componentID )
{
	Uint64		bigDialogID		= dialogID;
	Uint64		uniqueID;
	uniqueID					= bigDialogID << 32;
	uniqueID					= uniqueID | componentID;
	
	TControlInstanceMap::iterator	it	= s_controlsInstances.find( uniqueID );
	
	if ( it != s_controlsInstances.end() ) {
		VWCustomCtrl*	pInstance	= it->second;;
		
		if ( pInstance ) {
		  pInstance->CustomMacHandler( inMessage, ioParam, paneVP, dialogID, componentID );
		}
	}
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void VWCustomCtrl::CustomMacHandler(long inMessage, void* ioParam, void* paneVP, int dialogID, int componentID )
{
//	Those are deprecated messages. They are still send by VW but we use our own
//	const signed long msg_Click				= 812;	// SMouseEvent*
//	const signed long msg_MouseUp			= 850;	// SMouseEvent*
//	const signed long msg_AdjustCursor		= 813;	// SMouseEvent*

	const signed long msg_DrawOrPrint		= 811;	// Rect* (frame of Pane)

	const signed long msg_MouseWheel		= 851;	// SMouseEvent*

	const signed long msg_MouseDown			= 852;	// SMouseEvent*
	const signed long msg_MouseUp			= 853;	// SMouseEvent*
	const signed long msg_MouseClick		= 854;	// SMouseEvent*
	const signed long msg_MouseDblClick		= 855;	// SMouseEvent*

	const signed long msg_MouseRDown		= 856;	// SMouseEvent*
	const signed long msg_MouseRUp			= 857;	// SMouseEvent*
	const signed long msg_MouseRClick		= 858;	// SMouseEvent*
	const signed long msg_MouseRDblClick	= 859;	// SMouseEvent*

	const signed long msg_MouseMDown		= 860;	// SMouseEvent*
	const signed long msg_MouseMUp			= 861;	// SMouseEvent*
	const signed long msg_MouseMClick		= 862;	// SMouseEvent*
	const signed long msg_MouseMDblClick	= 863;	// SMouseEvent*

	const signed long msg_MouseMoved		= 864;	// SMouseEvent*

	typedef struct {
		Point		wherePort;
		Point		whereLocal;
		EventRecord	macEvent;
		Boolean		delaySelect;
	} SMouseEvent;

	// there are other messages that will be documented for keyboard events and other mouse events on this widget.
	// other types of events need to be registered with the operating system.
	

	SCustomCtrlGeneralEvent		event;
	event.wnd						= 0;
	event.message					= inMessage;
	event.data1						= (Uint32) ioParam;
	event.data2						= (Uint32) paneVP;
	event.result					= 0;
	if ( this->OnEvent( event ) ) {
		// event has been handled by the general event handling funtino
		// so it's not necessary to recognize it
		;
	}
	else {
		switch( inMessage ) {
			case msg_DrawOrPrint: {
				GrafPtr port;
				GetPort( &port );
				
				{ // keep this block so 'VWCustomCtrlDC' could be deleted before OnAfterPaint call
					Rect&	rect			= *((Rect*)ioParam);

					VWCustomCtrlDC	dc( (unsigned long) paneVP, (void*) port, rect.left, rect.top );

					if ( fControlRef ) {
						GetControlBounds( fControlRef, &fControlRect );
					}
					
					fCustomCtrlRect.left	= 0;
					fCustomCtrlRect.top		= 0;
					fCustomCtrlRect.right	= rect.right - rect.left;
					fCustomCtrlRect.bottom	= rect.bottom - rect.top;

					GetPort( &port );
					
					this->OnPaint( dc );
				} // keep this block so 'VWCustomCtrlDC' could be deleted before OnAfterPaint call

				this->OnAfterPaint();

				break;
			}
				
			case msg_MouseDown:
			case msg_MouseRDown:
			case msg_MouseMDown: {
					SMouseEvent*			pEvent	= (SMouseEvent*) ioParam;

					SCustomCtrlMouseEvent	event;
					event.x							= pEvent->whereLocal.h;
					event.y							= pEvent->whereLocal.v;
					event.bCtrlDown					= pEvent->macEvent.modifiers & cmdKey;
					event.bShiftDown				= pEvent->macEvent.modifiers & shiftKey;
					if ( inMessage == msg_MouseDown ) 			this->OnMouseDown( event );
					else if ( inMessage == msg_MouseRDown ) 	this->OnMouseRDown( event );
					else if ( inMessage == msg_MouseMDown ) 	this->OnMouseMDown( event );
					else										DSTOP((kVStanev, "something's wrong with the message in VWCustomCtrl::CustomMacHandler" ));
				} break;

			case msg_MouseUp:
			case msg_MouseRUp:
			case msg_MouseMUp: {
					SMouseEvent*			pEvent	= (SMouseEvent*) ioParam;

					SCustomCtrlMouseEvent	event;
					event.x							= pEvent->whereLocal.h;
					event.y							= pEvent->whereLocal.v;
					event.bCtrlDown					= pEvent->macEvent.modifiers & cmdKey;
					event.bShiftDown				= pEvent->macEvent.modifiers & shiftKey;
					if ( inMessage == msg_MouseUp ) 			this->OnMouseUp( event );
					else if ( inMessage == msg_MouseRUp ) 		this->OnMouseRUp( event );
					else if ( inMessage == msg_MouseMUp ) 		this->OnMouseMUp( event );
					else										DSTOP((kVStanev, "something's wrong with the message in VWCustomCtrl::CustomMacHandler" ));
				} break;
			
			case msg_MouseClick:
			case msg_MouseRClick:
			case msg_MouseMClick: {
					SMouseEvent*			pEvent	= (SMouseEvent*) ioParam;

					SCustomCtrlMouseEvent	event;
					event.x							= pEvent->whereLocal.h;
					event.y							= pEvent->whereLocal.v;
					event.bCtrlDown					= pEvent->macEvent.modifiers & cmdKey;
					event.bShiftDown				= pEvent->macEvent.modifiers & shiftKey;
					if ( inMessage == msg_MouseClick ) 			this->OnMouseClick( event );
					else if ( inMessage == msg_MouseRClick ) 	this->OnMouseRClick( event );
					else if ( inMessage == msg_MouseMClick ) 	this->OnMouseMClick( event );
					else										DSTOP((kVStanev, "something's wrong with the message in VWCustomCtrl::CustomMacHandler" ));
				} break;

			case msg_MouseDblClick:
			case msg_MouseRDblClick:
			case msg_MouseMDblClick: {
					SMouseEvent*			pEvent	= (SMouseEvent*) ioParam;

					SCustomCtrlMouseEvent	event;
					event.x							= pEvent->whereLocal.h;
					event.y							= pEvent->whereLocal.v;
					event.bCtrlDown					= pEvent->macEvent.modifiers & cmdKey;
					event.bShiftDown				= pEvent->macEvent.modifiers & shiftKey;
					if ( inMessage == msg_MouseDblClick ) 			this->OnMouseDblClick( event );
					else if ( inMessage == msg_MouseRDblClick ) 	this->OnMouseRDblClick( event );
					else if ( inMessage == msg_MouseMDblClick ) 	this->OnMouseMDblClick( event );
					else											DSTOP((kVStanev, "something's wrong with the message in VWCustomCtrl::CustomMacHandler" ));
				} break;
			
			case msg_MouseMoved: {
					SMouseEvent*			pEvent	= (SMouseEvent*) ioParam;

					SCustomCtrlMouseEvent	event;
					event.x							= pEvent->whereLocal.h;
					event.y							= pEvent->whereLocal.v;
					event.bCtrlDown					= pEvent->macEvent.modifiers & cmdKey;
					event.bShiftDown				= pEvent->macEvent.modifiers & shiftKey;
					this->OnMouseMoved( event );
					
					// do tooltips
					if ( fbToolTipsEnabled ) {
						ViewPt		scrollAreaPoint;
						Uint16		leftStart;
						Uint16		topStart;
						
						this->GetScrollArea( leftStart, topStart );

						leftStart	= ( leftStart == Uint16(-1) ) ? 0 : leftStart;
						topStart	= ( topStart == Uint16(-1) ) ? 0 : topStart;
						
						scrollAreaPoint.x	= event.x + leftStart;
						scrollAreaPoint.y	= event.y + topStart;

						// find the tool
						SToolTipDef*	pDefaultToolTip	= NULL;
						SToolTipDef*	pFoundToolTip	= NULL;
						size_t			toolCnt			= farrToolTips.size();
						for(size_t i=0; i<toolCnt; i++) {
							SToolTipDef&	toolTip	= farrToolTips[ i ];
							if ( toolTip.fToolTip.fRect.IsEmpty() ) {
								// we just found a tool tip for the whole window
								// continue searching for more bound restricted one
								pFoundToolTip	= & toolTip;
								pDefaultToolTip	= & toolTip;
							}
							else if ( toolTip.fToolTip.fRect.PtInRect( scrollAreaPoint ) ) {
								// op, here is bound restricted one, that's it! STOP!
								pFoundToolTip	= & toolTip;
								break;
							}
						}

						if ( pFoundToolTip && pFoundToolTip->fToolTip.fRect.PtInRect( scrollAreaPoint ) ) {
							// this tool tip is no longer active
							if ( fpActiveToolTip && fpActiveToolTip != pFoundToolTip && ! fpActiveToolTip->fbCustomPos ) {
								fpActiveToolTip->fToolTip.fPos.Set( -1, -1 );
							}
							// if the found tool tip is a region it overrides the active tool tip
							fpActiveToolTip		= pFoundToolTip;
						}
						else {
							// this tool tip is no longer active
							if ( fpActiveToolTip && fpActiveToolTip != pDefaultToolTip && ! fpActiveToolTip->fbCustomPos ) {
								fpActiveToolTip->fToolTip.fPos.Set( -1, -1 );
							}
							fpActiveToolTip		= pDefaultToolTip;
						}

						if ( fpActiveToolTip ) {
							HMHelpContentRec 	rectHelpTag;

							rectHelpTag.version = kMacHelpVersion;  
							rectHelpTag.tagSide = kHMOutsideBottomLeftAligned; 
		
							// Position the ToolTip. 
							if ( fpActiveToolTip->fToolTip.fbTrackMouse ) {
								// The coordinates are adjusted so that the ToolTip does not overlap the mouse pointer.
								fpActiveToolTip->fToolTip.fPos.Set( event.x, event.y );
								rectHelpTag.tagSide = kHMOutsideTopLeftAligned; 
							}
							else if ( fpActiveToolTip->fToolTip.fPos.x < 0 || fpActiveToolTip->fToolTip.fPos.y < 0 ) {
								fpActiveToolTip->fToolTip.fPos.Set( event.x, event.y );
							}

							WindowRef 	window 		= (WindowRef) fpParentWnd;	
							Rect		rect		= { 0, 0, 0, 0 };
							GetWindowBounds( window, kWindowContentRgn, & rect );
							
							// translate tool tip pos into screen coords
							ViewPt	toolTipPt	= fpActiveToolTip->fToolTip.fPos;
							toolTipPt.x			+= rect.left + fControlRect.left - leftStart;
							toolTipPt.y			+= rect.top + fControlRect.top - topStart;
							
							// Set the hot rect for the help tag using the cell in which the mouse
							// is over.  We use the global coordinates.
							SetRect ( & rectHelpTag.absHotRect,
										toolTipPt.x,		//ptTopLeftLocalPoint.h,
										toolTipPt.y,		//ptTopLeftLocalPoint.v, 
										toolTipPt.x + 0,	//ptBottomRightLocalPoint.h,
										toolTipPt.y + 0	//ptBottomRightLocalPoint.v
									); 

							if ( this->OnToolTip( fpActiveToolTip->fTipID, fpActiveToolTip->fToolTip ) ) {
								// Set the main help string
								rectHelpTag.content[ kHMMinimumContentIndex ].contentType = kHMPascalStrContent;
								fpActiveToolTip->fToolTip.fText.CopyInto( rectHelpTag.content[ kHMMinimumContentIndex ].u.tagString );

								// Set the window for the help tag
								HMSetWindowHelpContent( window, & rectHelpTag ); 

								// Display the help tag
								::HMDisplayTag( & rectHelpTag );
							}
						}
					}
				} break;
			
			case msg_MouseWheel: {
					SMouseEvent*			pEvent	= (SMouseEvent*) ioParam;

					Sint32					deltaX	= 0;
					Sint32					deltaY	= 0;
					if ( pEvent->macEvent.message == 0 ) {
						deltaX		= (Sint32) pEvent->macEvent.when;
					}
					else {
						deltaY		= (Sint32) pEvent->macEvent.when;
					}
					
					SCustomCtrlMouseEvent	event;
					event.x							= pEvent->whereLocal.h;
					event.y							= pEvent->whereLocal.v;
					event.bCtrlDown					= pEvent->macEvent.modifiers & cmdKey;
					event.bShiftDown				= pEvent->macEvent.modifiers & shiftKey;					
					this->OnMouseWheel( event, DemoteTo<Sint16>( kVStanev, deltaX), DemoteTo<Sint16>( kVStanev, deltaY ) );
			}
		}
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
#endif

