Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

813 lines
19 KiB

/******************************Module*Header*******************************\
* Module Name: buttons.c
*
* Bitmap button support. On Daytona bitmap buttons are provided by
* mmcntrls. On Chicago there is no mmcntrls, so we use the functions
* in this file.
*
*
* Created: 19-04-94
* Author: Stephen Estrop [StephenE]
*
* Copyright (c) 1993 Microsoft Corporation
\**************************************************************************/
#pragma warning( once : 4201 4214 )
#define NOOLE
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include "buttons.h"
#include "literals.h"
/* -------------------------------------------------------------------------
** Color globals
** -------------------------------------------------------------------------
*/
int nSysColorChanges = 0;
DWORD rgbFace;
DWORD rgbShadow;
DWORD rgbHilight;
DWORD rgbFrame;
/*****************************Private*Routine******************************\
* PatB
*
* Fast way to fill an rectangle with a solid colour.
*
* History:
* 18-11-93 - StephenE - Created
*
\**************************************************************************/
void
PatB(
HDC hdc,
int x,
int y,
int dx,
int dy,
DWORD rgb
)
{
RECT rc;
SetBkColor(hdc,rgb);
rc.left = x;
rc.top = y;
rc.right = x + dx;
rc.bottom = y + dy;
ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
}
/*****************************Private*Routine******************************\
* CheckSysColors
*
* Checks the system colors and updates the cached global variables if
* they have changed.
*
* History:
* 18-11-93 - StephenE - Created
*
\**************************************************************************/
void
CheckSysColors(
void
)
{
static COLORREF rgbSaveFace = 0xffffffffL,
rgbSaveShadow = 0xffffffffL,
rgbSaveHilight = 0xffffffffL,
rgbSaveFrame = 0xffffffffL;
rgbFace = GetSysColor(COLOR_BTNFACE);
rgbShadow = GetSysColor(COLOR_BTNSHADOW);
rgbHilight = GetSysColor(COLOR_BTNHIGHLIGHT);
rgbFrame = GetSysColor(COLOR_WINDOWFRAME);
if (rgbSaveFace!=rgbFace || rgbSaveShadow!=rgbShadow
|| rgbSaveHilight!=rgbHilight || rgbSaveFrame!=rgbFrame)
{
++nSysColorChanges;
rgbSaveFace = rgbFace;
rgbSaveShadow = rgbShadow;
rgbSaveHilight = rgbHilight;
rgbSaveFrame = rgbFrame;
}
}
/* -------------------------------------------------------------------------
** Button globals -- some of these should be constants
** -------------------------------------------------------------------------
*/
const TCHAR szBbmProp[] = TEXT("ButtonBitmapProp");
const TCHAR szButtonProp[] = TEXT("ButtonProp");
typedef struct tagBTNSTATE { /* instance data for toolbar window */
WNDPROC lpfnDefProc;
HWND hwndToolTips;
HINSTANCE hInst;
UINT wID;
UINT uStyle;
HBITMAP hbm;
HDC hdcGlyphs;
HDC hdcMono;
HBITMAP hbmMono;
HBITMAP hbmDefault;
int dxBitmap;
int dyBitmap;
int nButtons;
int nSysColorChanges;
BITMAPBTN Buttons[1];
} BTNSTATE, NEAR *PBTNSTATE, FAR *LPBTNSTATE;
typedef struct {
WNDPROC lpfnDefProc;
HWND hwndParent;
HWND hwndToolTips;
} BTN_INFO, *LPBTN_INFO;
LRESULT CALLBACK
ButtonSubclassProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
LRESULT CALLBACK
ParentSubclassProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
void FAR PASCAL
RelayToToolTips(
HWND hwndToolTips,
HWND hWnd,
UINT wMsg,
WPARAM wParam,
LPARAM lParam
);
BOOL
InitObjects(
LPBTNSTATE pTBState
);
BOOL
FreeObjects(
LPBTNSTATE pTBState
);
void
CreateButtonMask(
LPBTNSTATE pTBState,
PBITMAPBTN pTBButton
);
/*****************************Private*Routine******************************\
* InitObjects
*
*
*
* History:
* 18-11-93 - StephenE - Created
*
\**************************************************************************/
BOOL
InitObjects(
LPBTNSTATE pTBState
)
{
pTBState->hdcGlyphs = CreateCompatibleDC(NULL);
if (pTBState->hdcGlyphs == NULL ) {
return FALSE;
}
pTBState->hdcMono = CreateCompatibleDC(NULL);
if (pTBState->hdcMono == NULL ) {
DeleteObject( pTBState->hdcGlyphs );
return FALSE;
}
pTBState->hbmMono = CreateBitmap( pTBState->dxBitmap,
pTBState->dyBitmap, 1, 1, NULL);
if ( pTBState->hbmMono == NULL ) {
DeleteObject( pTBState->hdcGlyphs );
DeleteObject( pTBState->hdcMono );
return FALSE;
}
pTBState->hbmDefault = SelectObject(pTBState->hdcMono, pTBState->hbmMono);
return TRUE;
}
/*****************************Private*Routine******************************\
* FreeObjects
*
*
*
* History:
* 18-11-93 - StephenE - Created
*
\**************************************************************************/
BOOL
FreeObjects(
LPBTNSTATE pTBState
)
{
if (pTBState->hdcMono) {
SelectObject(pTBState->hdcMono, pTBState->hbmDefault);
DeleteDC(pTBState->hdcMono); /* toast the DCs */
}
if (pTBState->hdcGlyphs) {
DeleteDC(pTBState->hdcGlyphs);
}
if (pTBState->hbmMono) {
DeleteObject(pTBState->hbmMono);
}
return TRUE;
}
/*****************************Private*Routine******************************\
* CreateButtonMask
*
* create a mono bitmap mask:
* 1's where color == COLOR_BTNFACE || COLOR_HILIGHT
* 0's everywhere else
*
*
* History:
* 18-11-93 - StephenE - Created
*
\**************************************************************************/
void
CreateButtonMask(
LPBTNSTATE pTBState,
PBITMAPBTN pTBButton
)
{
/* initalize whole area with 0's */
PatBlt( pTBState->hdcMono, 0, 0, pTBState->dxBitmap,
pTBState->dyBitmap, WHITENESS);
/* create mask based on color bitmap
** convert this to 1's
*/
SetBkColor(pTBState->hdcGlyphs, rgbFace);
BitBlt( pTBState->hdcMono, 0, 0, pTBState->dxBitmap, pTBState->dyBitmap,
pTBState->hdcGlyphs, pTBButton->iBitmap * pTBState->dxBitmap, 0,
SRCCOPY );
/* convert this to 1's */
SetBkColor(pTBState->hdcGlyphs, rgbHilight);
/* OR in the new 1's */
BitBlt( pTBState->hdcMono, 0, 0, pTBState->dxBitmap, pTBState->dyBitmap,
pTBState->hdcGlyphs, pTBButton->iBitmap * pTBState->dxBitmap, 0,
SRCPAINT );
}
#define PSDPxax 0x00B8074A
/*****************************Private*Routine******************************\
* BtnDrawButton
*
*
*
* History:
* 18-11-93 - StephenE - Created
*
\**************************************************************************/
void WINAPI
BtnDrawButton(
HWND hwnd,
HDC hdc,
int dx,
int dy,
LPBITMAPBTN ptButton
)
{
int glyph_offset;
HBRUSH hbrOld, hbr;
BOOL bMaskCreated = FALSE;
RECT rcFocus;
PBTNSTATE pTBState;
int x = 0, y = 0;
pTBState = (PBTNSTATE)GetProp(hwnd, szBbmProp);
CheckSysColors();
if (pTBState->nSysColorChanges != nSysColorChanges) {
DeleteObject( pTBState->hbm );
pTBState->hbm = CreateMappedBitmap( pTBState->hInst,
pTBState->wID, TRUE, NULL, 0);
pTBState->nSysColorChanges = nSysColorChanges;
}
/*
** erase with face color
*/
PatB(hdc, x, y, dx, dy, rgbFace);
SetRect( &rcFocus, x, y, x + dx, y + dy );
if (ptButton->fsState & BTNSTATE_PRESSED) {
DrawEdge( hdc, &rcFocus, EDGE_SUNKEN, BF_RECT );
glyph_offset = 1;
}
else {
DrawEdge( hdc, &rcFocus, EDGE_RAISED, BF_RECT );
glyph_offset = 0;
}
/*
** make the coordinates the interior of the button
*/
x += 2;
y += 2;
dx -= 4;
dy -= 4;
SelectObject( pTBState->hdcGlyphs, pTBState->hbm );
/* now put on the face */
/*
** We need to centre the Bitmap here within the button
*/
x += (dx - pTBState->dxBitmap ) / 2;
y += (dy - pTBState->dyBitmap ) / 2;
if (!(ptButton->fsState & BTNSTATE_DISABLED)) {
/* regular version */
BitBlt( hdc, x + glyph_offset, y + glyph_offset,
pTBState->dxBitmap, pTBState->dyBitmap,
pTBState->hdcGlyphs,
ptButton->iBitmap * pTBState->dxBitmap, 0, SRCCOPY);
}
else {
/* disabled version */
bMaskCreated = TRUE;
CreateButtonMask(pTBState, ptButton );
SetTextColor(hdc, 0L); /* 0's in mono -> 0 (for ROP) */
SetBkColor(hdc, 0x00FFFFFF); /* 1's in mono -> 1 */
hbr = CreateSolidBrush(rgbHilight);
if (hbr) {
hbrOld = SelectObject(hdc, hbr);
if (hbrOld) {
/* draw hilight color where we have 0's in the mask */
BitBlt( hdc, x + 1, y + 1,
pTBState->dxBitmap, pTBState->dyBitmap,
pTBState->hdcMono, 0, 0, PSDPxax);
SelectObject(hdc, hbrOld);
}
DeleteObject(hbr);
}
hbr = CreateSolidBrush(rgbShadow);
if (hbr) {
hbrOld = SelectObject(hdc, hbr);
if (hbrOld) {
/* draw the shadow color where we have 0's in the mask */
BitBlt(hdc, x, y, pTBState->dxBitmap, pTBState->dyBitmap,
pTBState->hdcMono, 0, 0, PSDPxax);
SelectObject(hdc, hbrOld);
}
DeleteObject(hbr);
}
}
if (ptButton->fsState & ODS_FOCUS) {
BtnDrawFocusRect(hdc, &rcFocus, ptButton->fsState);
}
}
/*****************************Private*Routine******************************\
* BtnCreateBitmapButtons
*
* Returns TRUE if successful, otherwise FALSE;
*
* History:
* 18-11-93 - StephenE - Created
*
\**************************************************************************/
BOOL WINAPI
BtnCreateBitmapButtons(
HWND hWnd,
HINSTANCE hInst,
UINT wID,
UINT uStyle,
LPBITMAPBTN lpButtons,
int nButtons,
int dxBitmap,
int dyBitmap
)
{
PBTNSTATE pTBState;
/*
** If we have already created Bitmap Buttons for this
** window just return.
*/
if (GetProp(hWnd, szBbmProp)) {
return TRUE;
}
CheckSysColors();
/*
** Allocate the required storage and save the pointer in the window
** property list.
*/
pTBState = (PBTNSTATE)LocalAlloc( LMEM_FIXED,
(sizeof(BTNSTATE) - sizeof(BITMAPBTN)) +
(nButtons * sizeof(BITMAPBTN)) );
if (pTBState == NULL ) {
return FALSE;
}
SetProp(hWnd, szBbmProp, (HANDLE)pTBState);
pTBState->hInst = hInst;
pTBState->wID = wID;
pTBState->uStyle = uStyle;
pTBState->nButtons = nButtons;
pTBState->hbm = CreateMappedBitmap( hInst, wID, TRUE, NULL, 0);
pTBState->dxBitmap = dxBitmap;
pTBState->dyBitmap = dyBitmap;
InitObjects( pTBState );
CopyMemory( pTBState->Buttons, lpButtons, nButtons * sizeof(BITMAPBTN) );
/*
** Does the caller want tool tips ?
*/
if (pTBState->uStyle & BBS_TOOLTIPS) {
pTBState->hwndToolTips = CreateWindow(TOOLTIPS_CLASS, g_szEmpty,
WS_POPUP,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
hWnd, NULL, hInst, NULL);
if (pTBState->hwndToolTips != (HWND)NULL ) {
int i;
TOOLINFO ti;
pTBState->lpfnDefProc = SubclassWindow( hWnd, ParentSubclassProc );
ti.uFlags = 0;
ti.cbSize = sizeof(ti);
ti.lpszText = LPSTR_TEXTCALLBACK;
for ( i = 0; i < nButtons; i++ ) {
LPBTN_INFO lpBtnInfo;
HWND hwndBtn;
hwndBtn = GetDlgItem(hWnd, pTBState->Buttons[i].uId);
if ( hwndBtn == (HWND)NULL ) {
break;
}
lpBtnInfo = (LPBTN_INFO)LocalAlloc(LPTR, sizeof(BTN_INFO));
if (lpBtnInfo == NULL ) {
break;
}
SetProp(hwndBtn, szButtonProp, (HANDLE)lpBtnInfo);
lpBtnInfo->hwndToolTips = pTBState->hwndToolTips;
lpBtnInfo->hwndParent = hWnd;
lpBtnInfo->lpfnDefProc = SubclassWindow( hwndBtn,
ButtonSubclassProc );
ti.hwnd = hwndBtn;
ti.uId = pTBState->Buttons[i].uId;
GetClientRect( hwndBtn, &ti.rect );
SendMessage( lpBtnInfo->hwndToolTips, TTM_ADDTOOL,
(WPARAM)0, (LPARAM)&ti );
/*
** Add the same rectangle in parent co-ordinates so that
** the tooltip still gets displayed even though the button
** is disabled.
*/
MapWindowRect( hwndBtn, hWnd, &ti.rect );
ti.hwnd = hWnd;
SendMessage( lpBtnInfo->hwndToolTips, TTM_ADDTOOL,
(WPARAM)0, (LPARAM)&ti );
}
}
else {
/*
** No tips available, just remove the BBS_TOOLTIPS style
*/
pTBState->uStyle &= ~BBS_TOOLTIPS;
}
}
return TRUE;
}
/******************************Public*Routine******************************\
* BtnDestroyBitmapButtons
*
*
*
* History:
* dd-mm-94 - StephenE - Created
*
\**************************************************************************/
void WINAPI
BtnDestroyBitmapButtons(
HWND hwnd
)
{
PBTNSTATE pTBState;
pTBState = (PBTNSTATE)GetProp(hwnd, szBbmProp);
if ( pTBState != NULL ) {
DeleteObject( pTBState->hbm );
FreeObjects( pTBState );
}
RemoveProp(hwnd, szBbmProp);
}
/******************************Public*Routine******************************\
* BtnDrawFocusRect
*
* Use this function to draw focus rectangle around a bitmap button.
*
* History:
* 18-11-93 - StephenE - Created
*
\**************************************************************************/
void WINAPI
BtnDrawFocusRect(
HDC hdc,
const RECT *lpRect,
UINT fsState
)
{
int iFaceOffset;
RECT rc;
CopyRect( &rc, lpRect );
rc.top = rc.left = 3;
if (fsState & ODS_SELECTED) {
iFaceOffset = 2;
}
else {
iFaceOffset = 4;
}
rc.right -= iFaceOffset;
rc.bottom -= iFaceOffset;
SetBkColor( hdc, rgbFace );
DrawFocusRect( hdc, &rc );
}
/******************************Public*Routine******************************\
* BtnUpdateColors
*
* After a WM_SYSCOLORCHANGE message is received this function should be
* called to update the colors of the button bitmaps.
*
* History:
* 18-11-93 - StephenE - Created
*
\**************************************************************************/
void WINAPI
BtnUpdateColors(
HWND hwnd
)
{
PBTNSTATE pTBState;
pTBState = (PBTNSTATE)GetProp(hwnd, szBbmProp);
if (pTBState->nSysColorChanges != nSysColorChanges)
{
DeleteObject( pTBState->hbm );
pTBState->hbm = CreateMappedBitmap( pTBState->hInst,
pTBState->wID, TRUE, NULL, 0);
pTBState->nSysColorChanges = nSysColorChanges;
}
}
/******************************Public*Routine******************************\
* ButtonSubclassProc
*
*
*
* History:
* dd-mm-94 - StephenE - Created
*
\**************************************************************************/
LRESULT CALLBACK
ButtonSubclassProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
LPBTN_INFO lpBtnInfo;
WNDPROC lpfnDefProc;
lpBtnInfo = (LPBTN_INFO)GetProp( hwnd, szButtonProp );
/*
** Save this in case anything happens to lpBtnInfo before we return.
*/
lpfnDefProc = lpBtnInfo->lpfnDefProc;
switch ( uMsg ) {
case WM_DESTROY:
SubclassWindow( hwnd, lpfnDefProc );
if (lpBtnInfo) {
LocalFree((HLOCAL)lpBtnInfo);
RemoveProp(hwnd, szButtonProp);
}
break;
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MOUSEMOVE:
RelayToToolTips( lpBtnInfo->hwndToolTips, hwnd, uMsg, wParam, lParam );
break;
case WM_MOVE:
{
TOOLINFO ti;
ti.cbSize = sizeof(ti);
ti.uFlags = 0;
ti.hwnd = hwnd;
ti.lpszText = LPSTR_TEXTCALLBACK;
ti.uId = GetDlgCtrlID( hwnd );
GetClientRect( hwnd, &ti.rect );
SendMessage( lpBtnInfo->hwndToolTips, TTM_NEWTOOLRECT, 0,
(LPARAM)&ti );
/*
** Add the same rectangle in parent co-ordinates so that
** the tooltip still gets displayed even though the button
** is disabled.
*/
MapWindowRect( hwnd, lpBtnInfo->hwndParent, &ti.rect );
ti.hwnd = lpBtnInfo->hwndParent;
SendMessage( lpBtnInfo->hwndToolTips, TTM_NEWTOOLRECT,
(WPARAM)0, (LPARAM)&ti );
}
break;
case WM_NOTIFY:
SendMessage(lpBtnInfo->hwndParent, WM_NOTIFY, wParam, lParam);
break;
}
return CallWindowProc(lpfnDefProc, hwnd, uMsg, wParam, lParam);
}
/******************************Public*Routine******************************\
* ParentSubclassProc
*
* Why do I need to subclass the buttons parent window ? Well,
* if a button is disable it will not receive mouse messages, the
* messages go to the window underneath the button (ie. the parent).
* Therefore we detect this and relay the mouse message to the tool tips
* window as above.
*
* History:
* dd-mm-94 - StephenE - Created
*
\**************************************************************************/
LRESULT CALLBACK
ParentSubclassProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
WNDPROC lpfnDefProc;
PBTNSTATE pTBState;
pTBState = (PBTNSTATE)GetProp(hwnd, szBbmProp);
/*
** Save this in case anything happens to lpBtnInfo before we return.
*/
lpfnDefProc = pTBState->lpfnDefProc;
switch ( uMsg ) {
case TB_GETTOOLTIPS:
return (LRESULT)(UINT)pTBState->hwndToolTips;
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MOUSEMOVE:
case WM_NCMOUSEMOVE:
RelayToToolTips( pTBState->hwndToolTips, hwnd, uMsg, wParam, lParam );
break;
}
return CallWindowProc(lpfnDefProc, hwnd, uMsg, wParam, lParam);
}
/******************************Public*Routine******************************\
* RelayToToolTips
*
*
*
* History:
* dd-mm-94 - StephenE - Created
*
\**************************************************************************/
void FAR PASCAL
RelayToToolTips(
HWND hwndToolTips,
HWND hWnd,
UINT wMsg,
WPARAM wParam,
LPARAM lParam
)
{
if(hwndToolTips) {
MSG msg;
msg.lParam = lParam;
msg.wParam = wParam;
msg.message = wMsg;
msg.hwnd = hWnd;
SendMessage(hwndToolTips, TTM_RELAYEVENT, 0, (LPARAM)(LPMSG)&msg);
}
}