/*
UserinfoEx plugin for Miranda IM

Copyright:
� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include "commonheaders.h"

// Used for our own cheap TrackMouseEvent
#define BUTTON_POLLID			 100
#define BUTTON_POLLDELAY		50

#define MGPROC(x) GetProcAddress(themeAPIHandle,x)

typedef struct TMBCtrl{
	HWND		hwnd;
	HANDLE		hThemeButton;
	HANDLE		hThemeToolbar;

	HICON		hIcon;
	HICON		arrow;			// uses down arrow
	HBITMAP		hBitmap;
	HFONT		hFont;			// font

	DWORD		dwStyle;	
	BOOLEAN		bFocus;	
	
	INT			stateId;		// button state
	INT			defbutton;		// default button
	INT			pbState;
	TCHAR		cHot;
} BTNCTRL, *LPBTNCTRL;

// External theme methods and properties
static CRITICAL_SECTION csTips;
static HWND hwndToolTips = NULL;
static HMODULE	themeAPIHandle = NULL;

// theme procedures
static HANDLE	 (WINAPI *OpenThemeData)(HWND,LPCWSTR);
static HRESULT	(WINAPI *CloseThemeData)(HANDLE);
static BOOL		 (WINAPI *IsThemeBackgroundPartiallyTransparent)(HANDLE,INT,INT);
static HRESULT	(WINAPI *DrawThemeParentBackground)(HWND,HDC,RECT *);
static HRESULT	(WINAPI *DrawThemeBackground)(HANDLE,HDC,INT,INT,const RECT *,const RECT *);
static HRESULT	(WINAPI *DrawThemeText)(HANDLE,HDC,INT,INT,LPCWSTR,INT,DWORD,DWORD,const RECT *);
static HRESULT	(WINAPI *GetThemeTextExtent)(HANDLE,HDC,INT,INT,LPCWSTR,INT,DWORD,OPTIONAL const RECT*, RECT *);

/**
 * name:	ThemeSupport
 * desc:	Loads the uxtheme functions, if supported by the os
 * param:	none
 * return:	TRUE if themes are supported, FALSE if not
 **/
static BOOLEAN __fastcall ThemeSupport() {
	if (IsWinVerXPPlus()) {
		if (!themeAPIHandle) {
			themeAPIHandle = GetModuleHandleA("uxtheme");
			if (themeAPIHandle) {
				OpenThemeData = (HANDLE (WINAPI *)(HWND,LPCWSTR))MGPROC("OpenThemeData");
				CloseThemeData = (HRESULT (WINAPI *)(HANDLE))MGPROC("CloseThemeData");
				IsThemeBackgroundPartiallyTransparent = (BOOL (WINAPI *)(HANDLE,INT,INT))MGPROC("IsThemeBackgroundPartiallyTransparent");
				DrawThemeParentBackground = (HRESULT (WINAPI *)(HWND,HDC,RECT *))MGPROC("DrawThemeParentBackground");
				DrawThemeBackground = (HRESULT (WINAPI *)(HANDLE,HDC,INT,INT,const RECT *,const RECT *))MGPROC("DrawThemeBackground");
				DrawThemeText = (HRESULT (WINAPI *)(HANDLE,HDC,INT,INT,LPCWSTR,INT,DWORD,DWORD,const RECT *))MGPROC("DrawThemeText");
				GetThemeTextExtent = (HRESULT (WINAPI *)(HANDLE,HDC,INT,INT,LPCWSTR,INT,DWORD,OPTIONAL const RECT*, RECT *))MGPROC("GetThemeTextExtent");
			}
		}
		// Make sure all of these methods are valid (i would hope either all or none work)
		if (OpenThemeData
			&& CloseThemeData
			&& IsThemeBackgroundPartiallyTransparent
			&& DrawThemeParentBackground
			&& DrawThemeBackground
			&& DrawThemeText
			&& GetThemeTextExtent)
		{
			return TRUE;
		}
	}
	return FALSE;
}

/**
 * name:	DestroyTheme
 * desc:	destroys theme data for buttons
 * param:	ctl - BTNCTRL structure with the information about the theme to close
 * return:	nothing
 **/
static VOID __fastcall DestroyTheme(BTNCTRL *ctl) {
	if (ctl->hThemeButton) {
		CloseThemeData(ctl->hThemeButton);
		ctl->hThemeButton = NULL;
	}
	if (ctl->hThemeToolbar) {
		CloseThemeData(ctl->hThemeToolbar);
		ctl->hThemeToolbar = NULL;
	}
}

/**
 * name:	LoadTheme
 * desc:	load theme data for buttons if supported by os
 * param:	ctl - BTNCTRL structure with the information about the theme to load
 * return:	nothing
 **/
static VOID __fastcall LoadTheme(BTNCTRL *ctl) {
	if (ThemeSupport()) {
		DestroyTheme(ctl);
		ctl->hThemeButton = OpenThemeData(ctl->hwnd,L"BUTTON");
		ctl->hThemeToolbar = OpenThemeData(ctl->hwnd,L"TOOLBAR");
	}
}

/**
 * name:	TBStateConvert2Flat
 * desc:	convert button stateIDs
 * param:	state - state id for the normal theme button
 * return:	stateID for the flat theme button
 **/
static INT __fastcall TBStateConvert2Flat(INT state) {
	switch (state) {
		case PBS_NORMAL:		return TS_NORMAL;
		case PBS_HOT:			 return TS_HOT;
		case PBS_PRESSED:	 return TS_PRESSED;
		case PBS_DISABLED:	return TS_DISABLED;
		case PBS_DEFAULTED: return TS_NORMAL;
	}
	return TS_NORMAL;
}

/**
 * name:	PaintIcon
 * desc:	Draws the Icon of the button
 * param:	ctl			- BTNCTRL structure for the button
 *			hdcMem		- device context to draw to
 *			ccText		- character count of the text of the button
 *			rcClient	- rectangle of the whole button
 *			rcText		- rectangle of the text to draw later on
 * return:	nothing
 **/
static VOID __fastcall PaintIcon(BTNCTRL *ctl, HDC hdcMem, LPWORD ccText, LPRECT rcClient, LPRECT rcText)
{
	RECT rcImage;

	// draw icon on the left of the button
	if (ctl->hIcon) {
		rcImage.right = GetSystemMetrics(SM_CXSMICON);
		rcImage.bottom = GetSystemMetrics(SM_CYSMICON);
		rcImage.left = (rcClient->right - rcClient->left) / 2 - ((rcImage.right + rcText->right + (*ccText > 0 ? 4 : 0) + (ctl->arrow ? rcImage.right : 0)) / 2);
		rcImage.top = (rcClient->bottom - rcClient->top - rcImage.bottom) / 2;
		rcImage.right += rcImage.left;
		rcImage.bottom += rcImage.top;
		
		OffsetRect(rcText, rcImage.right + 4, 0);
		if (ctl->stateId == PBS_PRESSED)	OffsetRect(&rcImage, 1, 1);

		DrawState(hdcMem, NULL, NULL, (LPARAM)ctl->hIcon, 0, 
			rcImage.left, rcImage.top, 
			rcImage.right - rcImage.left, rcImage.bottom - rcImage.top,
			IsWindowEnabled(ctl->hwnd) ? DST_ICON | DSS_NORMAL : DST_ICON | DSS_DISABLED);
	}

	// draw arrow on the right of the button
	if (ctl->arrow) {
		rcImage.right = GetSystemMetrics(SM_CXSMICON);
		rcImage.left = (*ccText > 0 || ctl->hIcon) 
					 ? rcClient->right - GetSystemMetrics(SM_CXSMICON) 
					 : (rcClient->right - rcClient->left - rcImage.right) / 2;
		rcImage.right += rcImage.left;
		rcImage.bottom = GetSystemMetrics(SM_CYSMICON);
		rcImage.top = (rcClient->bottom - rcClient->top - rcImage.bottom) / 2;
		if (ctl->stateId == PBS_PRESSED)	OffsetRect(&rcImage, 1, 1);

		DrawState(hdcMem, NULL, NULL, (LPARAM)ctl->arrow, 0, 
			rcImage.left, rcImage.top, 
			rcImage.right - rcImage.left, rcImage.bottom - rcImage.top,
			IsWindowEnabled(ctl->hwnd) ? DST_ICON | DSS_NORMAL : DST_ICON | DSS_DISABLED);
	}
}

/**
 * name:	PaintThemeButton
 * desc:	Draws the themed button
 * param:	ctl			- BTNCTRL structure for the button
 *			hdcMem		- device context to draw to
 *			rcClient	- rectangle of the whole button
 * return:	nothing
 **/
static VOID __fastcall PaintThemeButton(BTNCTRL *ctl, HDC hdcMem, LPRECT rcClient)
{
	RECT rcText = { 0, 0, 0, 0 };
	WCHAR wszText[MAX_PATH] = { 0 };
	WORD ccText;

	// Draw the flat button
	if ((ctl->dwStyle & MBS_FLAT) && ctl->hThemeToolbar) {
		INT state = IsWindowEnabled(ctl->hwnd)
				? (ctl->stateId == PBS_NORMAL && ctl->defbutton 
					? PBS_DEFAULTED
					: ctl->stateId)
				: PBS_DISABLED;
		if (IsThemeBackgroundPartiallyTransparent(ctl->hThemeToolbar, TP_BUTTON, TBStateConvert2Flat(state))) {
			if (SUCCEEDED(DrawThemeParentBackground(ctl->hwnd, hdcMem, rcClient)))
				DrawThemeParentBackground(GetParent(ctl->hwnd), hdcMem, rcClient);
		}
		DrawThemeBackground(ctl->hThemeToolbar, hdcMem, TP_BUTTON, TBStateConvert2Flat(state), rcClient, rcClient);
	}
	else {
		// draw themed button background
		if (ctl->hThemeButton) {
			INT state = IsWindowEnabled(ctl->hwnd)
				? (ctl->stateId == PBS_NORMAL && ctl->defbutton 
					? PBS_DEFAULTED
					: ctl->stateId)
				: PBS_DISABLED;
			if (IsThemeBackgroundPartiallyTransparent(ctl->hThemeButton, BP_PUSHBUTTON, state)) {
				if (SUCCEEDED(DrawThemeParentBackground(ctl->hwnd, hdcMem, rcClient)))
					DrawThemeParentBackground(GetParent(ctl->hwnd), hdcMem, rcClient);
			}
			DrawThemeBackground(ctl->hThemeButton, hdcMem, BP_PUSHBUTTON, state, rcClient, rcClient);
		}
	}
	
	// calculate text rect
	{
		RECT	sizeText;
		HFONT	hOldFont;

		ccText = GetWindowTextW(ctl->hwnd, wszText, sizeof(wszText) / sizeof(WCHAR));

		if (ccText > 0) {
			hOldFont = (HFONT)SelectObject(hdcMem, ctl->hFont);
			
			GetThemeTextExtent(
				ctl->hThemeButton,
				hdcMem,
				BP_PUSHBUTTON,
				IsWindowEnabled(ctl->hwnd) ? ctl->stateId : PBS_DISABLED,
				wszText,
				ccText,
				DST_PREFIXTEXT,
				NULL,
				&sizeText);
			
			if (ctl->cHot) {
				RECT rcHot;
				
				GetThemeTextExtent(ctl->hThemeButton,
					hdcMem,
					BP_PUSHBUTTON,
					IsWindowEnabled(ctl->hwnd) ? ctl->stateId : PBS_DISABLED,
					L"&",
					1,
					DST_PREFIXTEXT,
					NULL,
					&rcHot);
				
				sizeText.right -= (rcHot.right - rcHot.left);
			}
			SelectObject(hdcMem, hOldFont);

			rcText.left = (ctl->hIcon) ? 0 : (rcClient->right - rcClient->left - (sizeText.right - sizeText.left)) / 2;
			rcText.top = (rcClient->bottom - rcClient->top - (sizeText.bottom - sizeText.top)) / 2;
			rcText.right = rcText.left + (sizeText.right - sizeText.left);
			rcText.bottom = rcText.top + (sizeText.bottom - sizeText.top);
			if (ctl->stateId == PBS_PRESSED) {
				OffsetRect(&rcText, 1, 1);
			}
		}
	}
	PaintIcon(ctl, hdcMem, &ccText, rcClient, &rcText);
	// draw text
	if (ccText > 0 && ctl->hThemeButton) { 
		HFONT hOldFont = (HFONT)SelectObject(hdcMem, ctl->hFont);
		DrawThemeText(ctl->hThemeButton, hdcMem, BP_PUSHBUTTON, IsWindowEnabled(ctl->hwnd) ? ctl->stateId : PBS_DISABLED, wszText, ccText, DST_PREFIXTEXT, 0, &rcText);
		SelectObject(hdcMem, hOldFont);
	}
}

/**
 * name:	PaintThemeButton
 * desc:	Draws the none themed button
 * param:	ctl			- BTNCTRL structure for the button
 *			hdcMem		- device context to draw to
 *			rcClient	- rectangle of the whole button
 * return:	nothing
 **/
static VOID __fastcall PaintButton(BTNCTRL *ctl, HDC hdcMem, LPRECT rcClient)
{
	RECT rcText = { 0, 0, 0, 0 };
	TCHAR szText[MAX_PATH] = { 0 };
	WORD ccText;

	// Draw the flat button
	if (ctl->dwStyle & MBS_FLAT) {
		HBRUSH hbr = NULL;
		
		if (ctl->stateId == PBS_PRESSED || ctl->stateId == PBS_HOT)
			hbr = GetSysColorBrush(COLOR_3DLIGHT);
		else {
			HDC dc;
			HWND hwndParent;

			hwndParent = GetParent(ctl->hwnd);
			if (dc = GetDC(hwndParent)) {
				hbr = (HBRUSH)SendMessage(hwndParent, WM_CTLCOLORDLG, (WPARAM)dc, (LPARAM)hwndParent);
				ReleaseDC(hwndParent, dc);
			}
		}
		if (hbr) {
			FillRect(hdcMem, rcClient, hbr);
			DeleteObject(hbr);
		}
		if (ctl->stateId == PBS_HOT || ctl->bFocus) {
			if (ctl->pbState) DrawEdge(hdcMem, rcClient, EDGE_ETCHED, BF_RECT|BF_SOFT);
			else DrawEdge(hdcMem, rcClient, BDR_RAISEDOUTER, BF_RECT|BF_SOFT|BF_FLAT);
		}
		else
		if (ctl->stateId == PBS_PRESSED)
			DrawEdge(hdcMem, rcClient, BDR_SUNKENOUTER, BF_RECT|BF_SOFT);
	}
	else {
		UINT uState = DFCS_BUTTONPUSH|((ctl->stateId == PBS_HOT) ? DFCS_HOT : 0)|((ctl->stateId == PBS_PRESSED) ? DFCS_PUSHED : 0);
		if (ctl->defbutton&&ctl->stateId==PBS_NORMAL) uState |= DLGC_DEFPUSHBUTTON;
		DrawFrameControl(hdcMem, rcClient, DFC_BUTTON, uState);
		// Draw focus rectangle if button has focus
		if (ctl->bFocus) {
			RECT focusRect = *rcClient;
			InflateRect(&focusRect, -3, -3);
			DrawFocusRect(hdcMem, &focusRect);
		}
	}
	// calculate text rect
	{
		SIZE	sizeText;
		HFONT	hOldFont;

		ccText = GetWindowText(ctl->hwnd, szText, SIZEOF(szText));

		if (ccText > 0) {
			hOldFont = (HFONT)SelectObject(hdcMem, ctl->hFont);
			GetTextExtentPoint32(hdcMem, szText, ccText, &sizeText);
			if (ctl->cHot) {
				SIZE sizeHot;
				
				GetTextExtentPoint32A(hdcMem, "&", 1, &sizeHot);
				sizeText.cx -= sizeHot.cx;
			}
			SelectObject(hdcMem, hOldFont);

			rcText.left = (ctl->hIcon) ? 0 : (rcClient->right - rcClient->left - sizeText.cx) / 2;
			rcText.top = (rcClient->bottom - rcClient->top - sizeText.cy) / 2;
			rcText.right = rcText.left + sizeText.cx;
			rcText.bottom = rcText.top + sizeText.cy;
			if (ctl->stateId == PBS_PRESSED)
				OffsetRect(&rcText, 1, 1);
		}
	}
	PaintIcon(ctl, hdcMem, &ccText, rcClient, &rcText);

	// draw text
	if (ccText > 0) { 
		HFONT hOldFont;

		hOldFont = (HFONT)SelectObject(hdcMem, ctl->hFont);

		SetBkMode(hdcMem, TRANSPARENT);
		SetTextColor(hdcMem, 
			IsWindowEnabled(ctl->hwnd) || !ctl->hThemeButton 
			? ctl->stateId == PBS_HOT
					? GetSysColor(COLOR_HOTLIGHT)
					: GetSysColor(COLOR_BTNTEXT) 
				: GetSysColor(COLOR_GRAYTEXT));

		DrawState(hdcMem, NULL, NULL, (LPARAM)szText, 0, 
			rcText.left, rcText.top, rcText.right - rcText.left, rcText.bottom - rcText.top,
			IsWindowEnabled(ctl->hwnd) || ctl->hThemeButton ? DST_PREFIXTEXT | DSS_NORMAL : DST_PREFIXTEXT | DSS_DISABLED);
		SelectObject(hdcMem, hOldFont);
	}
}

/**
 * name:	Button_WndProc
 * desc:	window procedure for the button class
 * param:	hwndBtn		- window handle to the button
 *			uMsg		- message to handle
 *			wParam		- message specific parameter
 *			lParam		- message specific parameter
 * return:	message specific
 **/
static LRESULT CALLBACK Button_WndProc(HWND hwndBtn, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	LPBTNCTRL bct = (LPBTNCTRL)GetWindowLongPtr(hwndBtn, 0);
	
	switch (uMsg) {
		case WM_NCCREATE:
		{
			LPCREATESTRUCT cs = (LPCREATESTRUCT)lParam;

			cs->style |= BS_OWNERDRAW;
			if (!(bct = (LPBTNCTRL)mir_alloc(sizeof(BTNCTRL))))
				return FALSE;
			ZeroMemory(bct, sizeof(BTNCTRL));
			bct->hwnd = hwndBtn;
			bct->stateId = PBS_NORMAL;
			bct->hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
			bct->dwStyle = cs->style;
			if (cs->style & MBS_DOWNARROW)
				bct->arrow = IcoLib_GetIcon(ICO_BTN_DOWNARROW);
			LoadTheme(bct);
			SetWindowLongPtr(hwndBtn, 0, (LONG_PTR)bct);
			if (cs->lpszName) SetWindowText(hwndBtn, cs->lpszName);
			return TRUE;
		}
		case WM_DESTROY:
			if (bct) {
				EnterCriticalSection(&csTips);
				if (hwndToolTips) {
					TOOLINFO ti;

					ZeroMemory(&ti, sizeof(ti));
					ti.cbSize = sizeof(ti);
					ti.uFlags = TTF_IDISHWND;
					ti.hwnd = bct->hwnd;
					ti.uId = (UINT_PTR)bct->hwnd;
					if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) {
						SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
					}
					if (SendMessage(hwndToolTips, TTM_GETTOOLCOUNT, 0, (LPARAM)&ti) == 0) {
						DestroyWindow(hwndToolTips);
						hwndToolTips = NULL;
					}
				}
				LeaveCriticalSection(&csTips);
				DestroyTheme(bct);
				mir_free(bct);
			}
			SetWindowLongPtr(hwndBtn, 0, NULL);
			break;
		case WM_SETTEXT:
			bct->cHot = 0;
			if ((LPTSTR)lParam) {
				LPTSTR tmp = (LPTSTR)lParam;
				
				while (*tmp) {
					if (*tmp=='&' && *(tmp+1)) {
						bct->cHot = _totlower(*(tmp+1));
						break;
					}
					tmp++;
				}
				InvalidateRect(bct->hwnd, NULL, TRUE);
			}
			break;
		case WM_SYSKEYUP:
			if (bct->stateId != PBS_DISABLED && bct->cHot && bct->cHot == _totlower((TCHAR)wParam)) {
				if (bct->dwStyle & MBS_PUSHBUTTON) {
					if (bct->pbState) bct->pbState = 0;
					else bct->pbState = 1;
					InvalidateRect(bct->hwnd, NULL, TRUE);
				}
				else
					SetFocus(hwndBtn);
				SendMessage(GetParent(hwndBtn), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndBtn), BN_CLICKED), (LPARAM)hwndBtn);
				return 0;
			}
			break;
		case WM_THEMECHANGED: 
		{
			// themed changed, reload theme object
			LoadTheme(bct);
			InvalidateRect(bct->hwnd, NULL, TRUE); // repaint it
			break;
		}
		case WM_SETFONT: // remember the font so we can use it later
			bct->hFont = (HFONT)wParam; // maybe we should redraw?
			break;
		case WM_NCPAINT:
		case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HDC hdcPaint;
			HDC hdcMem;
			HBITMAP hbmMem;
			HDC hOld;
			RECT rcClient;
			
			if (hdcPaint = BeginPaint(hwndBtn, &ps)) {
				GetClientRect(bct->hwnd, &rcClient);
				hdcMem = CreateCompatibleDC(hdcPaint);
				hbmMem = CreateCompatibleBitmap(hdcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
				hOld = (HDC)SelectObject(hdcMem, hbmMem);

				// If its a push button, check to see if it should stay pressed
				if ((bct->dwStyle & MBS_PUSHBUTTON) && bct->pbState) bct->stateId = PBS_PRESSED;

				if ((bct->dwStyle & MBS_FLAT) && bct->hThemeToolbar || bct->hThemeButton)
					PaintThemeButton(bct, hdcMem, &rcClient);
				else
					PaintButton(bct, hdcMem, &rcClient);

				BitBlt(hdcPaint, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdcMem, 0, 0, SRCCOPY);
				SelectObject(hdcMem, hOld);
				DeleteObject(hbmMem);
				DeleteDC(hdcMem);				
				EndPaint(hwndBtn, &ps);
			}
			return 0;
		}
		case BM_SETIMAGE:
			if (wParam == IMAGE_ICON) {
				bct->hIcon = (HICON)lParam;
				bct->hBitmap = NULL;
				InvalidateRect(bct->hwnd, NULL, TRUE);
			}
			else if (wParam == IMAGE_BITMAP) {
				bct->hIcon = NULL;
				bct->hBitmap = (HBITMAP)lParam;
				InvalidateRect(bct->hwnd, NULL, TRUE);
			}
			else if (wParam == NULL && lParam == NULL) {
				bct->hIcon = NULL;
				bct->hBitmap = NULL;
				InvalidateRect(bct->hwnd, NULL, TRUE);
			}
			break;
		case BM_SETCHECK:
			if (!(bct->dwStyle & MBS_PUSHBUTTON)) break;
			if (wParam == BST_CHECKED) {
				bct->pbState = 1;
								bct->stateId = PBS_PRESSED;
			}
			else if (wParam == BST_UNCHECKED) {
				bct->pbState = 0;
								bct->stateId = PBS_NORMAL;
			}
			InvalidateRect(bct->hwnd, NULL, TRUE);
			break;
		case BM_GETCHECK:
			if (bct->dwStyle & MBS_PUSHBUTTON) return bct->pbState ? BST_CHECKED : BST_UNCHECKED;
			return 0;
		case BUTTONSETDEFAULT:
			bct->defbutton = (wParam != 0);
			InvalidateRect(bct->hwnd, NULL, TRUE);
			break;
		case BUTTONADDTOOLTIP:
		{
			if (!wParam) break;
						EnterCriticalSection(&csTips);
			if (!hwndToolTips) {
				hwndToolTips = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, WS_POPUP, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL), NULL);
			}

			if (lParam == MBF_UNICODE) {
				TOOLINFOW ti;

				ZeroMemory(&ti, sizeof(TOOLINFOW));
				ti.cbSize = sizeof(TOOLINFOW);
				ti.uFlags = TTF_IDISHWND;
				ti.hwnd = bct->hwnd;
				ti.uId = (UINT_PTR)bct->hwnd;
				if (SendMessage(hwndToolTips, TTM_GETTOOLINFOW, 0, (LPARAM)&ti)) {
					SendMessage(hwndToolTips, TTM_DELTOOLW, 0, (LPARAM)&ti);
				}
				ti.uFlags = TTF_IDISHWND|TTF_SUBCLASS;
				ti.uId = (UINT_PTR)bct->hwnd;
				ti.lpszText=(LPWSTR)wParam;
				SendMessage(hwndToolTips, TTM_ADDTOOLW, 0, (LPARAM)&ti);
			}
			else {
				TOOLINFOA ti;

				ZeroMemory(&ti, sizeof(TOOLINFOA));
				ti.cbSize = sizeof(TOOLINFOA);
				ti.uFlags = TTF_IDISHWND;
				ti.hwnd = bct->hwnd;
				ti.uId = (UINT_PTR)bct->hwnd;
				if (SendMessage(hwndToolTips, TTM_GETTOOLINFOA, 0, (LPARAM)&ti)) {
					SendMessage(hwndToolTips, TTM_DELTOOLA, 0, (LPARAM)&ti);
				}
				ti.uFlags = TTF_IDISHWND|TTF_SUBCLASS;
				ti.uId = (UINT_PTR)bct->hwnd;
				ti.lpszText=(LPSTR)wParam;
				SendMessage(hwndToolTips, TTM_ADDTOOLA, 0, (LPARAM)&ti);
			}
						LeaveCriticalSection(&csTips);
			break;
		}
		case BUTTONTRANSLATE:
		{
			TCHAR szButton[MAX_PATH];
			GetWindowText(bct->hwnd, szButton, MAX_PATH);
			SetWindowText(bct->hwnd, TranslateTS(szButton));
			break;
		}
		case WM_SETFOCUS: // set keybord bFocus and redraw
			bct->bFocus = 1;
			InvalidateRect(bct->hwnd, NULL, TRUE);
			break;
		case WM_KILLFOCUS: // kill bFocus and redraw
			bct->bFocus = 0;
			InvalidateRect(bct->hwnd, NULL, TRUE);
			break;
		case WM_WINDOWPOSCHANGED:
			InvalidateRect(bct->hwnd, NULL, TRUE);
			break;
		case WM_ENABLE: // windows tells us to enable/disable
			bct->stateId = wParam ? PBS_NORMAL : PBS_DISABLED;
			InvalidateRect(bct->hwnd, NULL, TRUE);
			break;
		case WM_MOUSELEAVE: // faked by the WM_TIMER
			if (bct->stateId != PBS_DISABLED) { // don't change states if disabled
				bct->stateId = PBS_NORMAL;
				InvalidateRect(bct->hwnd, NULL, TRUE);
			}
			break;
		case WM_LBUTTONDOWN:
			if (bct->stateId != PBS_DISABLED) { // don't change states if disabled
				bct->stateId = PBS_PRESSED;
				InvalidateRect(bct->hwnd, NULL, TRUE);
			}
			break;
		case WM_LBUTTONUP:
			if (bct->stateId != PBS_DISABLED) { // don't change states if disabled
				BOOLEAN bPressed = bct->stateId == PBS_PRESSED;

				if (bct->dwStyle & MBS_PUSHBUTTON) {
					if (bct->pbState) bct->pbState = 0;
					else bct->pbState = 1;
				}
				bct->stateId = PBS_HOT;

				// Tell your daddy you got clicked, if mouse is still over the button.
				if ((bct->dwStyle & MBS_PUSHBUTTON) || bPressed)
					SendMessage(GetParent(hwndBtn), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndBtn), BN_CLICKED), (LPARAM)hwndBtn);
				InvalidateRect(bct->hwnd, NULL, TRUE);
			}
			break;
		case WM_MOUSEMOVE:
			if (bct->stateId == PBS_NORMAL) {
				bct->stateId = PBS_HOT;
				InvalidateRect(bct->hwnd, NULL, TRUE);
			}
			// Call timer, used to start cheesy TrackMouseEvent faker
			SetTimer(hwndBtn, BUTTON_POLLID, BUTTON_POLLDELAY, NULL);
			break;
		case WM_TIMER: // use a timer to check if they have did a mouseout
						if (wParam == BUTTON_POLLID) {
					RECT rc;
					POINT pt;

					GetWindowRect(hwndBtn, &rc);
					GetCursorPos(&pt);
					if (!PtInRect(&rc, pt)) { // mouse must be gone, trigger mouse leave
						PostMessage(hwndBtn, WM_MOUSELEAVE, 0, 0L);
						KillTimer(hwndBtn, BUTTON_POLLID);
					}
						}
			break;
		case WM_ERASEBKGND:
			return 1;
	}
	return DefWindowProc(hwndBtn, uMsg, wParam, lParam);
}

VOID CtrlButtonUnloadModule() 
{
	DeleteCriticalSection(&csTips);
	UnregisterClass(UINFOBUTTONCLASS, ghInst);
}

VOID CtrlButtonLoadModule()
{
	WNDCLASSEX wc;
	
	ZeroMemory(&wc, sizeof(wc));
	wc.cbSize				 = sizeof(wc);
	wc.lpszClassName	= UINFOBUTTONCLASS;
	wc.lpfnWndProc		= Button_WndProc;
	wc.hCursor				= LoadCursor(NULL, IDC_ARROW);
	wc.cbWndExtra		 = sizeof(LPBTNCTRL);
	wc.style					= CS_GLOBALCLASS;
	RegisterClassEx(&wc);
	InitializeCriticalSection(&csTips);
}