diff options
Diffstat (limited to 'plugins/SendScreenshotPlus/ctrl_button.cpp')
| -rw-r--r-- | plugins/SendScreenshotPlus/ctrl_button.cpp | 699 | 
1 files changed, 699 insertions, 0 deletions
diff --git a/plugins/SendScreenshotPlus/ctrl_button.cpp b/plugins/SendScreenshotPlus/ctrl_button.cpp new file mode 100644 index 0000000000..ad8e542fa3 --- /dev/null +++ b/plugins/SendScreenshotPlus/ctrl_button.cpp @@ -0,0 +1,699 @@ +/*
 +Miranda IM
 +Copyright (C) 2002 Robert Rainwater
 +
 +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 "global.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
 +CRITICAL_SECTION csTips;
 +HWND hwndToolTips = NULL;
 +HMODULE	themeAPIHandle = NULL;
 +
 +// theme procedures
 +HANDLE	(WINAPI *OpenThemeData)(HWND,LPCWSTR);
 +HRESULT	(WINAPI *CloseThemeData)(HANDLE);
 +BOOL	(WINAPI *IsThemeBackgroundPartiallyTransparent)(HANDLE,INT,INT);
 +HRESULT	(WINAPI *DrawThemeParentBackground)(HWND,HDC,RECT *);
 +HRESULT	(WINAPI *DrawThemeBackground)(HANDLE,HDC,INT,INT,const RECT *,const RECT *);
 +HRESULT	(WINAPI *DrawThemeText)(HANDLE,HDC,INT,INT,LPCWSTR,INT,DWORD,DWORD,const RECT *);
 +HRESULT	(WINAPI *GetThemeTextExtent)(HANDLE,HDC,INT,INT,LPCWSTR,INT,DWORD,OPTIONAL const RECT*, RECT *);
 +HRESULT	(WINAPI *GetThemeBackgroundRegion)(HANDLE,HDC,INT,INT,const RECT *,HRGN *);
 +
 +/**
 + * name:	ThemeSupport
 + * desc:	Loads the uxtheme functions, if supported by the os
 + * param:	none
 + * return:	TRUE if themes are supported, FALSE if not
 + **/
 +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");
 +				GetThemeBackgroundRegion = (HRESULT (WINAPI *)(HANDLE,HDC,INT,INT,const RECT *,HRGN *))MGPROC("GetThemeBackgroundRegion");
 +			}
 +		}
 +		// 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)malloc(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)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);
 +				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 ? 1 : 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)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)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)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)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, hInst);
 +}
 +
 +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);
 +}
 +
  | 
