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.
 
 
 
 
 
 

1680 lines
43 KiB

/*
**
** Toolbar.c
**
** This is it, the incredibly famous toolbar control. Most of
** the customization stuff is in another file.
**
*/
#include "ctlspriv.h"
#define Reference(x) ((x)=(x))
#define SZCODE static char _based(_segname("_CODE"))
char aszToolbarClassName[] = TOOLBARCLASSNAME;
SZCODE szUSER[] = "USER.EXE";
SZCODE szDrawFrameControl[] = "DrawFrameControl";
SZCODE szKernel[] = "KERNEL.EXE";
SZCODE szWriteProfileStruct[] = "WritePrivateProfileStruct";
// these values are defined by the UI gods...
#define DEFAULTBITMAPX 16
#define DEFAULTBITMAPY 15
#define DEFAULTBUTTONX 24
#define DEFAULTBUTTONY 22
// horizontal/vertical space taken up by button chisel, sides,
// and a 1 pixel margin. used in GrowToolbar.
#define XSLOP 7
#define YSLOP 6
#define SLOPTOP 1
#define SLOPBOT 1
#define SLOPLFT 8
static int dxButtonSep = 8;
static int xFirstButton = SLOPLFT; //!!! was 8
static int iInitCount = 0;
static int nSelectedBM = -1;
static HDC hdcGlyphs = NULL; // globals for fast drawing
static HDC hdcMono = NULL;
static HBITMAP hbmMono = NULL;
static HBITMAP hbmDefault = NULL;
static HDC hdcButton = NULL; // contains hbmFace (when it exists)
static HBITMAP hbmFace = NULL;
static int dxFace, dyFace; // current dimensions of hbmFace (2*dxFace)
static HDC hdcFaceCache = NULL; // used for button cache
static HFONT hIconFont = NULL; // font used for strings in buttons
static int yIconFont; // height of the font
static BOOL g_bUseDFC = FALSE; // use DrawFrameControl, if available
static BOOL g_bProfStruct = FALSE; // use PrivateProfileStruct routines
static WORD g_dxOverlap = 1; // overlap between buttons
static WORD wStateMasks[] = {
TBSTATE_ENABLED,
TBSTATE_CHECKED,
TBSTATE_PRESSED,
TBSTATE_HIDDEN,
TBSTATE_INDETERMINATE
};
LRESULT CALLBACK _loadds ToolbarWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
#define HeightWithString(h) (h + yIconFont + 1)
static BOOL NEAR PASCAL InitGlobalObjects(void)
{
LOGFONT lf;
TEXTMETRIC tm;
HFONT hOldFont;
iInitCount++;
if (iInitCount != 1)
return TRUE;
hdcGlyphs = CreateCompatibleDC(NULL);
if (!hdcGlyphs)
return FALSE;
hdcMono = CreateCompatibleDC(NULL);
if (!hdcMono)
return FALSE;
hbmMono = CreateBitmap(DEFAULTBUTTONX, DEFAULTBUTTONY, 1, 1, NULL);
if (!hbmMono)
return FALSE;
hbmDefault = SelectObject(hdcMono, hbmMono);
hdcButton = CreateCompatibleDC(NULL);
if (!hdcButton)
return FALSE;
hdcFaceCache = CreateCompatibleDC(NULL);
if (!hdcFaceCache)
return FALSE;
SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0);
hIconFont = CreateFontIndirect(&lf);
if (!hIconFont)
return FALSE;
hOldFont = SelectObject(hdcMono, hIconFont);
GetTextMetrics(hdcMono, &tm);
yIconFont = tm.tmHeight;
if (hOldFont)
SelectObject(hdcMono, hOldFont);
#if WINVER >= 0x0400
// set a global flag to see if USER will draw for us
if (GetProcAddress(LoadLibrary(szUSER), szDrawFrameControl))
{
g_bUseDFC = TRUE;
g_dxOverlap = 0; // buttons do NOT overlap with new look
}
// set a global flag to see if KERNEL does profile structs
if (GetProcAddress(LoadLibrary(szKernel), szWriteProfileStruct))
g_bProfStruct = TRUE;
#endif
return TRUE;
}
static BOOL NEAR PASCAL FreeGlobalObjects(void)
{
iInitCount--;
if (iInitCount != 0)
return TRUE;
if (hdcMono) {
if (hbmDefault)
SelectObject(hdcMono, hbmDefault);
DeleteDC(hdcMono); // toast the DCs
}
hdcMono = NULL;
if (hdcGlyphs)
DeleteDC(hdcGlyphs);
hdcGlyphs = NULL;
if (hdcFaceCache)
DeleteDC(hdcFaceCache);
hdcFaceCache = NULL;
if (hdcButton) {
if (hbmDefault)
SelectObject(hdcButton, hbmDefault);
DeleteDC(hdcButton);
}
hdcButton = NULL;
if (hbmFace)
DeleteObject(hbmFace);
hbmFace = NULL;
if (hbmMono)
DeleteObject(hbmMono);
hbmMono = NULL;
if (hIconFont)
DeleteObject(hIconFont);
hIconFont = NULL;
}
HWND WINAPI CreateToolbarEx(HWND hwnd, DWORD ws, WORD wID, int nBitmaps,
HINSTANCE hBMInst, WORD wBMID, LPCTBBUTTON lpButtons,
int iNumButtons, int dxButton, int dyButton,
int dxBitmap, int dyBitmap, UINT uStructSize)
{
HWND hwndToolbar;
hwndToolbar = CreateWindow(aszToolbarClassName, NULL, WS_CHILD | ws,
0, 0, 100, 30, hwnd, (HMENU)wID,
(HINSTANCE)GetWindowWord(hwnd, GWW_HINSTANCE), NULL);
if (!hwndToolbar)
goto Error1;
SendMessage(hwndToolbar, TB_BUTTONSTRUCTSIZE, uStructSize, 0L);
if (dxBitmap && dyBitmap)
if (!SendMessage(hwndToolbar, TB_SETBITMAPSIZE, 0, MAKELONG(dxBitmap, dyBitmap)))
{
//!!!! do we actually need to deal with this?
DestroyWindow(hwndToolbar);
hwndToolbar = NULL;
goto Error1;
}
if (dxButton && dyButton)
if (!SendMessage(hwndToolbar, TB_SETBUTTONSIZE, 0, MAKELONG(dxButton, dyButton)))
{
//!!!! do we actually need to deal with this?
DestroyWindow(hwndToolbar);
hwndToolbar = NULL;
goto Error1;
}
SendMessage(hwndToolbar, TB_ADDBITMAP, nBitmaps, MAKELONG(hBMInst, wBMID));
SendMessage(hwndToolbar, TB_ADDBUTTONS, iNumButtons, (LPARAM)lpButtons);
Error1:
return hwndToolbar;
}
#if 0
/* This is no longer declared in COMMCTRL.H. It only exists for compatibility
** with existing apps; new apps must use CreateToolbarEx.
*/
HWND WINAPI CreateToolbar(HWND hwnd, DWORD ws, WORD wID, int nBitmaps, HINSTANCE hBMInst, WORD wBMID, LPCTBBUTTON lpButtons, int iNumButtons)
{
// old-style toolbar, so no divider.
ws |= CCS_NODIVIDER;
return (CreateToolbarEx(hwnd, ws, wID, nBitmaps, hBMInst, wBMID,
lpButtons, iNumButtons, 0, 0, 0, 0, sizeof(OLDTBBUTTON)));
}
#endif
BOOL FAR PASCAL InitToolbarClass(HINSTANCE hInstance)
{
WNDCLASS wc;
if (!GetClassInfo(hInstance, aszToolbarClassName, &wc)) {
wc.lpszClassName = aszToolbarClassName;
wc.style = CS_GLOBALCLASS | CS_DBLCLKS;
wc.lpfnWndProc = (WNDPROC)ToolbarWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(PTBSTATE);
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.lpszMenuName = NULL;
if (!RegisterClass(&wc))
return FALSE;
}
return TRUE;
}
#define BEVEL 2
#define FRAME 1
static void NEAR PASCAL 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);
}
static void NEAR PASCAL DrawString(HDC hdc, int x, int y, int dx, PSTR pszString)
{
int oldMode;
DWORD oldTextColor;
HFONT oldhFont;
DWORD dwExt;
int len;
oldMode = SetBkMode(hdc, TRANSPARENT);
oldTextColor = SetTextColor(hdc, 0L);
oldhFont = SelectObject(hdc, hIconFont);
len = lstrlen(pszString);
dwExt = GetTextExtent(hdc, (LPSTR)pszString, len);
// center the string horizontally
x += (dx - LOWORD(dwExt) - 1)/2;
TextOut(hdc, x, y, (LPSTR)pszString, len);
if (oldhFont)
SelectObject(hdc, oldhFont);
SetTextColor(hdc, oldTextColor);
SetBkMode(hdc, oldMode);
}
// create a mono bitmap mask:
// 1's where color == COLOR_BTNFACE || COLOR_HILIGHT
// 0's everywhere else
static void NEAR PASCAL CreateMask(PTBSTATE pTBState, PTBBUTTON pTBButton, int xoffset, int yoffset, int dx, int dy)
{
PSTR pFoo;
// initalize whole area with 1's
PatBlt(hdcMono, 0, 0, dx, dy, WHITENESS);
// create mask based on color bitmap
// convert this to 1's
SetBkColor(hdcGlyphs, rgbFace);
BitBlt(hdcMono, xoffset, yoffset, pTBState->iDxBitmap, pTBState->iDyBitmap,
hdcGlyphs, pTBButton->iBitmap * pTBState->iDxBitmap, 0, SRCCOPY);
// convert this to 1's
SetBkColor(hdcGlyphs, rgbHilight);
// OR in the new 1's
BitBlt(hdcMono, xoffset, yoffset, pTBState->iDxBitmap, pTBState->iDyBitmap,
hdcGlyphs, pTBButton->iBitmap * pTBState->iDxBitmap, 0, SRCPAINT);
if (pTBButton->iString != -1 && (pTBButton->iString < pTBState->nStrings))
{
pFoo = pTBState->pStrings[pTBButton->iString];
DrawString(hdcMono, 1, yoffset + pTBState->iDyBitmap + 1, dx, pFoo);
}
}
/* Given a button number, the corresponding bitmap is loaded and selected in,
* and the Window origin set.
* Returns NULL on Error, 1 if the necessary bitmap is already selected,
* or the old bitmap otherwise.
*/
static HBITMAP FAR PASCAL SelectBM(HDC hDC, PTBSTATE pTBState, int nButton)
{
PTBBMINFO pTemp;
HBITMAP hRet;
int nBitmap, nTot;
for (pTemp=pTBState->pBitmaps, nBitmap=0, nTot=0; ; ++pTemp, ++nBitmap)
{
if (nBitmap >= pTBState->nBitmaps)
return(NULL);
if (nButton < nTot+pTemp->nButtons)
break;
nTot += pTemp->nButtons;
}
/* Special case when the required bitmap is already selected
*/
if (nBitmap == nSelectedBM)
return((HBITMAP)1);
if (!pTemp->hbm || (hRet=SelectObject(hDC, pTemp->hbm))==NULL)
{
if (pTemp->hbm)
DeleteObject(pTemp->hbm);
if (pTemp->hInst)
pTemp->hbm = CreateMappedBitmap(pTemp->hInst, pTemp->wID,
TRUE, NULL, 0);
else
pTemp->hbm = (HBITMAP)pTemp->wID;
if (!pTemp->hbm || (hRet=SelectObject(hDC, pTemp->hbm))==NULL)
return(NULL);
}
nSelectedBM = nBitmap;
#ifdef WIN32
SetWindowOrgEx(hDC, nTot * pTBState->iDxBitmap, 0, NULL);
#else // WIN32
SetWindowOrg(hDC, nTot * pTBState->iDxBitmap, 0);
#endif
return(hRet);
}
static void FAR PASCAL DrawBlankButton(HDC hdc, int x, int y, int dx, int dy, WORD state, WORD wButtType)
{
#if WINVER >= 0x0400
RECT r1;
#endif
// face color
PatB(hdc, x, y, dx, dy, rgbFace);
#if WINVER >= 0x0400
if (g_bUseDFC)
{
r1.left = x;
r1.top = y;
r1.right = x + dx;
r1.bottom = y + dy;
DrawFrameControl(hdc, &r1, wButtType,
(state & TBSTATE_PRESSED) ? DFCS_PUSHED : 0);
}
else
#endif
{
if (state & TBSTATE_PRESSED) {
PatB(hdc, x + 1, y, dx - 2, 1, rgbFrame);
PatB(hdc, x + 1, y + dy - 1, dx - 2, 1, rgbFrame);
PatB(hdc, x, y + 1, 1, dy - 2, rgbFrame);
PatB(hdc, x + dx - 1, y +1, 1, dy - 2, rgbFrame);
PatB(hdc, x + 1, y + 1, 1, dy-2, rgbShadow);
PatB(hdc, x + 1, y + 1, dx-2, 1, rgbShadow);
}
else {
PatB(hdc, x + 1, y, dx - 2, 1, rgbFrame);
PatB(hdc, x + 1, y + dy - 1, dx - 2, 1, rgbFrame);
PatB(hdc, x, y + 1, 1, dy - 2, rgbFrame);
PatB(hdc, x + dx - 1, y + 1, 1, dy - 2, rgbFrame);
dx -= 2;
dy -= 2;
PatB(hdc, x + 1, y + 1, 1, dy - 1, rgbHilight);
PatB(hdc, x + 1, y + 1, dx - 1, 1, rgbHilight);
PatB(hdc, x + dx, y + 1, 1, dy, rgbShadow);
PatB(hdc, x + 1, y + dy, dx, 1, rgbShadow);
PatB(hdc, x + dx - 1, y + 2, 1, dy - 2, rgbShadow);
PatB(hdc, x + 2, y + dy - 1, dx - 2, 1, rgbShadow);
}
}
}
#define DSPDxax 0x00E20746
#define PSDPxax 0x00B8074A
#define FillBkColor(hdc, prc) ExtTextOut(hdc,0,0,ETO_OPAQUE,prc,NULL,0,NULL)
static void NEAR PASCAL DrawFace(PTBSTATE pTBState, PTBBUTTON ptButton, HDC hdc, int x, int y,
int offx, int offy, int dx)
{
PSTR pFoo;
BitBlt(hdc, x + offx, y + offy, pTBState->iDxBitmap, pTBState->iDyBitmap,
hdcGlyphs, ptButton->iBitmap * pTBState->iDxBitmap, 0, SRCCOPY);
if (ptButton->iString != -1 && (ptButton->iString < pTBState->nStrings))
{
pFoo = pTBState->pStrings[ptButton->iString];
DrawString(hdc, x + 1, y + offy + pTBState->iDyBitmap + 1, dx, pFoo);
}
}
static void FAR PASCAL DrawButton(HDC hdc, int x, int y, int dx, int dy, PTBSTATE pTBState, PTBBUTTON ptButton, BOOL bFaceCache)
{
int yOffset;
HBRUSH hbrOld, hbr;
BOOL bMaskCreated = FALSE;
BYTE state;
int xButton = 0; // assume button is down
int dxFace, dyFace;
int xCenterOffset;
dxFace = dx - 4;
dyFace = dy - 4;
// make local copy of state and do proper overriding
state = ptButton->fsState;
if (state & TBSTATE_INDETERMINATE) {
if (state & TBSTATE_PRESSED)
state &= ~TBSTATE_INDETERMINATE;
else if (state & TBSTATE_ENABLED)
state = TBSTATE_INDETERMINATE;
else
state &= ~TBSTATE_INDETERMINATE;
}
// get the proper button look-- up or down.
if (!(state & (TBSTATE_PRESSED | TBSTATE_CHECKED))) {
xButton = dx; // use 'up' version of button
}
if (bFaceCache)
BitBlt(hdc, x, y, dx, dy, hdcButton, xButton, 0, SRCCOPY);
else
DrawBlankButton(hdc, x, y, dx, dy, state, pTBState->wButtonType);
// move coordinates inside border and away from upper left highlight.
// the extents change accordingly.
x += 2;
y += 2;
if (!SelectBM(hdcGlyphs, pTBState, ptButton->iBitmap))
return;
// calculate offset of face from (x,y). y is always from the top,
// so the offset is easy. x needs to be centered in face.
yOffset = 1;
xCenterOffset = (dxFace - pTBState->iDxBitmap)/2;
if (state & (TBSTATE_PRESSED | TBSTATE_CHECKED))
{
// pressed state moves down and to the right
xCenterOffset++;
yOffset++;
}
// now put on the face
if (state & TBSTATE_ENABLED) {
// regular version
DrawFace(pTBState, ptButton, hdc, x, y, xCenterOffset, yOffset, dxFace);
} else {
// disabled version (or indeterminate)
bMaskCreated = TRUE;
CreateMask(pTBState, ptButton, xCenterOffset, yOffset, dxFace, dyFace);
SetTextColor(hdc, 0L); // 0's in mono -> 0 (for ROP)
SetBkColor(hdc, 0x00FFFFFF); // 1's in mono -> 1
// draw glyph's white understrike
if (!(state & TBSTATE_INDETERMINATE)) {
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, dxFace, dyFace, hdcMono, 0, 0, PSDPxax);
SelectObject(hdc, hbrOld);
}
DeleteObject(hbr);
}
}
// gray out glyph
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, dxFace, dyFace, hdcMono, 0, 0, PSDPxax);
SelectObject(hdc, hbrOld);
}
DeleteObject(hbr);
}
if (state & TBSTATE_CHECKED) {
BitBlt(hdcMono, 1, 1, dxFace - 1, dyFace - 1, hdcMono, 0, 0, SRCAND);
}
}
if (state & (TBSTATE_CHECKED | TBSTATE_INDETERMINATE)) {
hbrOld = SelectObject(hdc, hbrDither);
if (hbrOld) {
if (!bMaskCreated)
CreateMask(pTBState, ptButton, xCenterOffset, yOffset, dxFace, dyFace);
SetTextColor(hdc, 0L); // 0 -> 0
SetBkColor(hdc, 0x00FFFFFF); // 1 -> 1
// only draw the dither brush where the mask is 1's
BitBlt(hdc, x, y, dxFace, dyFace, hdcMono, 0, 0, DSPDxax);
SelectObject(hdc, hbrOld);
}
}
}
static void NEAR PASCAL FlushButtonCache(PTBSTATE pTBState)
{
if (pTBState->hbmCache) {
DeleteObject(pTBState->hbmCache);
pTBState->hbmCache = 0;
}
}
// make sure that hbmMono is big enough to do masks for this
// size of button. if not, fail.
static BOOL NEAR PASCAL CheckMonoMask(int width, int height)
{
BITMAP bm;
HBITMAP hbmTemp;
GetObject(hbmMono, sizeof(BITMAP), &bm);
if (width > bm.bmWidth || height > bm.bmHeight) {
hbmTemp = CreateBitmap(width, height, 1, 1, NULL);
if (!hbmTemp)
return FALSE;
SelectObject(hdcMono, hbmTemp);
DeleteObject(hbmMono);
hbmMono = hbmTemp;
}
return TRUE;
}
/*
** GrowToolbar
**
** Attempt to grow the button size.
**
** The calling function can either specify a new internal measurement
** or a new external measurement.
*/
static BOOL NEAR PASCAL GrowToolbar(PTBSTATE pTBState, int newButWidth, int newButHeight, BOOL bInside)
{
// if growing based on inside measurement, get full size
if (bInside) {
newButHeight += YSLOP;
newButWidth += XSLOP;
// if toolbar already has strings, don't shrink width it because it
// might clip room for the string
if ((newButWidth < pTBState->iButWidth) && pTBState->nStrings)
newButWidth = pTBState->iButWidth;
}
else {
if (newButHeight < pTBState->iButHeight)
newButHeight = pTBState->iButHeight;
if (newButWidth < pTBState->iButWidth)
newButWidth = pTBState->iButWidth;
}
// if the size of the toolbar is actually growing, see if shadow
// bitmaps can be made sufficiently large.
if ((newButWidth > pTBState->iButWidth) || (newButHeight > pTBState->iButHeight)) {
if (!CheckMonoMask(newButWidth, newButHeight))
return(FALSE);
}
pTBState->iButWidth = newButWidth;
pTBState->iButHeight = newButHeight;
//!!!ACK ACK ACK ACK
#if 0
// bar height has 2 pixels above, 3 below
pTBState->iBarHeight = pTBState->iButHeight + 5;
pTBState->iYPos = 2;
#else
pTBState->iBarHeight = pTBState->iButHeight + SLOPTOP+SLOPBOT;
pTBState->iYPos = SLOPTOP;
#endif
return TRUE;
}
static BOOL NEAR PASCAL SetBitmapSize(PTBSTATE pTBState, int width, int height)
{
int realh = height;
if (pTBState->nStrings)
realh = HeightWithString(height);
if (GrowToolbar(pTBState, width, realh, TRUE)) {
pTBState->iDxBitmap = width;
pTBState->iDyBitmap = height;
return TRUE;
}
return FALSE;
}
static void NEAR PASCAL UpdateTBState(PTBSTATE pTBState)
{
int i;
PTBBMINFO pBitmap;
if (pTBState->nSysColorChanges!=nSysColorChanges)
{
/* Reset all of the bitmaps if the sys colors have changed
* since the last time the bitmaps were created.
*/
for (i=pTBState->nBitmaps-1, pBitmap=pTBState->pBitmaps; i>=0;
--i, ++pBitmap)
{
if (pBitmap->hInst && pBitmap->hbm)
{
DeleteObject(pBitmap->hbm);
pBitmap->hbm = NULL;
}
}
FlushButtonCache(pTBState);
// now we're updated to latest color scheme
pTBState->nSysColorChanges = nSysColorChanges;
}
}
#define CACHE 0x01
#define BUILD 0x02
static void NEAR PASCAL ToolbarPaint(HWND hWnd, PTBSTATE pTBState)
{
RECT rc;
HDC hdc;
PAINTSTRUCT ps;
int iButton, xButton, yButton;
int cButtons = pTBState->iNumButtons;
PTBBUTTON pAllButtons = pTBState->Buttons;
HBITMAP hbmOldGlyphs;
int xCache = 0;
WORD wFlags = 0;
int iCacheWidth = 0;
HBITMAP hbmTemp;
BOOL bFaceCache = TRUE; // assume face cache exists
int dx,dy;
CheckSysColors();
UpdateTBState(pTBState);
hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rc);
if (!rc.right)
goto Error1;
dx = pTBState->iButWidth;
dy = pTBState->iButHeight;
// setup global stuff for fast painting
/* We need to kick-start the bitmap selection process.
*/
nSelectedBM = -1;
hbmOldGlyphs = SelectBM(hdcGlyphs, pTBState, 0);
if (!hbmOldGlyphs)
goto Error1;
yButton = pTBState->iYPos;
rc.top = yButton;
rc.bottom = yButton + dy;
if (!(pTBState->hbmCache)) {
// calculate the width of the cache.
for (iButton = 0; iButton < cButtons; iButton++) {
if (!(pAllButtons[iButton].fsState & TBSTATE_HIDDEN) &&
!(pAllButtons[iButton].fsStyle & TBSTYLE_SEP))
iCacheWidth += pTBState->iButWidth;
}
pTBState->hbmCache = CreateCompatibleBitmap(hdcGlyphs, iCacheWidth, dy);
wFlags |= BUILD;
// if needed, create or enlarge bitmap for pre-building button states
if (!(hbmFace && (dx <= dxFace) && (dy <= dyFace))) {
hbmTemp = CreateCompatibleBitmap(hdcGlyphs, 2*dx, dy);
if (hbmTemp) {
SelectObject(hdcButton, hbmTemp);
if (hbmFace)
DeleteObject(hbmFace);
hbmFace = hbmTemp;
dxFace = dx;
dyFace = dy;
}
else
bFaceCache = FALSE;
}
}
if (pTBState->hbmCache) {
SelectObject(hdcFaceCache,pTBState->hbmCache);
wFlags |= CACHE;
}
else
wFlags = 0;
if (bFaceCache) {
DrawBlankButton(hdcButton, 0, 0, dx, dy, TBSTATE_PRESSED, pTBState->wButtonType);
DrawBlankButton(hdcButton, dx, 0, dx, dy, 0, pTBState->wButtonType);
}
for (iButton = 0, xButton = xFirstButton;
iButton < cButtons;
iButton++) {
PTBBUTTON ptbButton = &pAllButtons[iButton];
if (ptbButton->fsState & TBSTATE_HIDDEN) {
/* Do nothing */ ;
} else if (ptbButton->fsStyle & TBSTYLE_SEP) {
xButton += ptbButton->iBitmap;
} else {
if (wFlags & BUILD)
DrawButton(hdcFaceCache, xCache, 0, dx, dy, pTBState, ptbButton, bFaceCache);
rc.left = xButton;
rc.right = xButton + dx;
if (RectVisible(hdc, &rc)) {
if ((wFlags & CACHE) && !(ptbButton->fsState & TBSTATE_PRESSED))
BitBlt(hdc, xButton, yButton, dx, dy,
hdcFaceCache, xCache, 0, SRCCOPY);
else
DrawButton(hdc, xButton, yButton, dx, dy, pTBState, ptbButton, bFaceCache);
}
// advance the "pointer" in the cache
xCache += dx;
xButton += (dx - g_dxOverlap);
}
}
if (wFlags & CACHE)
SelectObject(hdcFaceCache, hbmDefault);
SelectObject(hdcGlyphs, hbmOldGlyphs);
Error1:
EndPaint(hWnd, &ps);
}
static BOOL NEAR PASCAL GetItemRect(PTBSTATE pTBState, UINT uButton, LPRECT lpRect)
{
UINT iButton, xPos;
PTBBUTTON pButton;
if (uButton>=(UINT)pTBState->iNumButtons
|| (pTBState->Buttons[uButton].fsState&TBSTATE_HIDDEN))
{
return(FALSE);
}
xPos = xFirstButton;
for (iButton=0, pButton=pTBState->Buttons; iButton<uButton;
++iButton, ++pButton)
{
if (pButton->fsState & TBSTATE_HIDDEN)
{
/* Do nothing */ ;
}
else if (pButton->fsStyle & TBSTYLE_SEP)
{
xPos += pButton->iBitmap;
}
else
{
xPos += (pTBState->iButWidth - g_dxOverlap);
}
}
/* pButton should now point at the required button, and xPos should be
* its left edge. Note that we already checked if the button was
* hidden above.
*/
lpRect->left = xPos;
lpRect->right = xPos + (pButton->fsStyle&TBSTYLE_SEP
? pButton->iBitmap : pTBState->iButWidth);
lpRect->top = pTBState->iYPos;
lpRect->bottom = lpRect->top + pTBState->iButHeight;
return(TRUE);
}
static void NEAR PASCAL InvalidateButton(HWND hwnd, PTBSTATE pTBState, PTBBUTTON pButtonToPaint)
{
RECT rc;
if (GetItemRect(pTBState, pButtonToPaint-pTBState->Buttons, &rc))
{
InvalidateRect(hwnd, &rc, FALSE);
}
}
static int FAR PASCAL TBHitTest(PTBSTATE pTBState, int xPos, int yPos)
{
int iButton;
int cButtons = pTBState->iNumButtons;
PTBBUTTON pButton;
xPos -= xFirstButton;
if (xPos < 0)
return(-1);
yPos -= pTBState->iYPos;
for (iButton=0, pButton=pTBState->Buttons; iButton<cButtons;
++iButton, ++pButton)
{
if (pButton->fsState & TBSTATE_HIDDEN)
/* Do nothing */ ;
else if (pButton->fsStyle & TBSTYLE_SEP)
xPos -= pButton->iBitmap;
else
xPos -= (pTBState->iButWidth - g_dxOverlap);
if (xPos < 0)
{
if (pButton->fsStyle&TBSTYLE_SEP
|| (UINT)yPos>=(UINT)pTBState->iButHeight)
break;
return(iButton);
}
}
return(-1 - iButton);
}
static int FAR PASCAL PositionFromID(PTBSTATE pTBState, int id)
{
int i;
int cButtons = pTBState->iNumButtons;
PTBBUTTON pAllButtons = pTBState->Buttons;
for (i = 0; i < cButtons; i++)
if (pAllButtons[i].idCommand == id)
return i; // position found
return -1; // ID not found!
}
// check a radio button by button index.
// the button matching idCommand was just pressed down. this forces
// up all other buttons in the group.
// this does not work with buttons that are forced up with
static void NEAR PASCAL MakeGroupConsistant(HWND hWnd, PTBSTATE pTBState, int idCommand)
{
int i, iFirst, iLast, iButton;
int cButtons = pTBState->iNumButtons;
PTBBUTTON pAllButtons = pTBState->Buttons;
iButton = PositionFromID(pTBState, idCommand);
if (iButton < 0)
return;
// assertion
// if (!(pAllButtons[iButton].fsStyle & TBSTYLE_CHECK))
// return;
// did the pressed button just go down?
if (!(pAllButtons[iButton].fsState & TBSTATE_CHECKED))
return; // no, can't do anything
// find the limits of this radio group
for (iFirst = iButton; (iFirst > 0) && (pAllButtons[iFirst].fsStyle & TBSTYLE_GROUP); iFirst--)
if (!(pAllButtons[iFirst].fsStyle & TBSTYLE_GROUP))
iFirst++;
cButtons--;
for (iLast = iButton; (iLast < cButtons) && (pAllButtons[iLast].fsStyle & TBSTYLE_GROUP); iLast++);
if (!(pAllButtons[iLast].fsStyle & TBSTYLE_GROUP))
iLast--;
// search for the currently down button and pop it up
for (i = iFirst; i <= iLast; i++) {
if (i != iButton) {
// is this button down?
if (pAllButtons[i].fsState & TBSTATE_CHECKED) {
pAllButtons[i].fsState &= ~TBSTATE_CHECKED; // pop it up
InvalidateButton(hWnd, pTBState, &pAllButtons[i]);
break; // only one button is down right?
}
}
}
}
static void NEAR PASCAL DestroyStrings(PTBSTATE pTBState)
{
PSTR *p;
PSTR end = 0, start = 0;
int i;
p = pTBState->pStrings;
for (i = 0; i < pTBState->nStrings; i++) {
if (!(*p < end) && (*p > start)) {
start = (*p);
end = start + LocalSize((HANDLE)*p);
LocalFree((HANDLE)*p);
}
p++;
i++;
}
LocalFree((HANDLE)pTBState->pStrings);
}
// not needed for MCIWnd
#if 0
#define MAXSTRINGSIZE 1024
static int NEAR PASCAL AddStrings(PTBSTATE pTBState, WPARAM wParam, LPARAM lParam)
{
int i;
DWORD dwExt;
HFONT hOldFont;
LPSTR lpsz;
PSTR pString, psz, ptmp;
int numstr;
PSTR *pFoo;
PSTR *pOffset;
char cSeparator;
int len;
int newWidth;
// read the string as a resource
if (wParam != 0) {
pString = (PSTR)LocalAlloc(LPTR, MAXSTRINGSIZE);
if (!pString)
return -1;
i = LoadString((HINSTANCE)wParam, LOWORD(lParam), (LPSTR)pString, MAXSTRINGSIZE);
if (!i) {
LocalFree(pString);
return -1;
}
// realloc string buffer to actual needed size
LocalReAlloc(pString, i, LMEM_MOVEABLE);
// convert separators to '\0' and count number of strings
cSeparator = *pString;
ptmp = pString;
pString += 1;
for (numstr = 0, psz = pString; i >= psz-pString; *psz ? psz = (PSTR)AnsiNext((LPSTR)psz) : psz++) {
if (*psz == cSeparator) {
numstr++;
*psz = 0; // terminate with 0
}
}
}
// read explicit string. copy it into local memory, too.
else {
// find total length and number of strings
for (i = 0, numstr = 0, lpsz = (LPSTR)lParam;;) {
i++;
if (*lpsz == 0) {
numstr++;
if (*(lpsz+1) == 0)
break;
}
lpsz++;
}
pString = (PSTR)LocalAlloc(LPTR, i);
ptmp = pString;
if (!pString)
return -1;
hmemcpy(pString, (void FAR *)lParam, i);
}
// make room for increased string pointer table
if (pTBState->pStrings)
pFoo = (PSTR *)LocalReAlloc(pTBState->pStrings,
(pTBState->nStrings + numstr) * sizeof(PSTR), LMEM_MOVEABLE);
else
pFoo = (PSTR *)LocalAlloc(LPTR, numstr * sizeof(PSTR));
if (!pFoo) {
LocalFree(ptmp);
return -1;
}
pTBState->pStrings = pFoo;
// pointer to next open slot in string index table.
pOffset = pTBState->pStrings + pTBState->nStrings;
hOldFont = SelectObject(hdcMono, hIconFont);
// fix up string pointer table to deal with the new strings.
// check if any string is big enough to necessitate a wider button.
newWidth = pTBState->iDxBitmap;
for (i = 0; i < numstr; i++, pOffset++) {
*pOffset = pString;
len = lstrlen(pString);
dwExt = GetTextExtent(hdcMono, pString, len);
if ((int)(LOWORD(dwExt)) > newWidth)
newWidth = LOWORD(dwExt);
pString += len + 1;
}
if (hOldFont)
SelectObject(hdcMono, hOldFont);
// is the world big enough to handle the larger buttons?
if (!GrowToolbar(pTBState, newWidth, HeightWithString(pTBState->iDyBitmap), TRUE))
{
// back out changes.
if (pTBState->nStrings == 0) {
LocalFree(pTBState->pStrings);
pTBState->pStrings = 0;
}
else
pTBState->pStrings = (PSTR *)LocalReAlloc(pTBState->pStrings,
pTBState->nStrings * sizeof(PSTR), LMEM_MOVEABLE);
LocalFree(ptmp);
return -1;
}
i = pTBState->nStrings;
pTBState->nStrings += numstr;
return i; // index of first added string
}
#endif
/* Adds a new bitmap to the list of BMs available for this toolbar.
* Returns the index of the first button in the bitmap or -1 if there
* was an error.
*/
static int NEAR PASCAL AddBitmap(PTBSTATE pTBState, int nButtons,
HINSTANCE hBMInst, WORD wBMID)
{
PTBBMINFO pTemp;
int nBM, nIndex;
if (pTBState->pBitmaps)
{
/* Check if the bitmap has already been added
*/
for (nBM=pTBState->nBitmaps, pTemp=pTBState->pBitmaps, nIndex=0;
nBM>0; --nBM, ++pTemp)
{
if (pTemp->hInst==hBMInst && pTemp->wID==wBMID)
{
/* We already have this bitmap, but have we "registered" all
* the buttons in it?
*/
if (pTemp->nButtons >= nButtons)
return(nIndex);
if (nBM == 1)
{
/* If this is the last bitmap, we can easily increase the
* number of buttons without messing anything up.
*/
pTemp->nButtons = nButtons;
return(nIndex);
}
}
nIndex += pTemp->nButtons;
}
pTemp = (PTBBMINFO)LocalReAlloc(pTBState->pBitmaps,
(pTBState->nBitmaps+1)*sizeof(TBBMINFO), LMEM_MOVEABLE);
if (!pTemp)
return(-1);
pTBState->pBitmaps = pTemp;
}
else
{
pTBState->pBitmaps = (PTBBMINFO)LocalAlloc(LPTR, sizeof(TBBMINFO));
if (!pTBState->pBitmaps)
return(-1);
}
pTemp = pTBState->pBitmaps + pTBState->nBitmaps;
pTemp->hInst = hBMInst;
pTemp->wID = wBMID;
pTemp->nButtons = nButtons;
pTemp->hbm = NULL;
++pTBState->nBitmaps;
for (nButtons=0, --pTemp; pTemp>=pTBState->pBitmaps; --pTemp)
nButtons += pTemp->nButtons;
return(nButtons);
}
static BOOL NEAR PASCAL InsertButtons(HWND hWnd, PTBSTATE pTBState,
UINT uWhere, UINT uButtons, LPTBBUTTON lpButtons)
{
PTBBUTTON pIn, pOut;
if (!pTBState || !pTBState->uStructSize)
return(FALSE);
pTBState = (PTBSTATE)LocalReAlloc(pTBState, sizeof(TBSTATE)-sizeof(TBBUTTON)
+ (pTBState->iNumButtons+uButtons)*sizeof(TBBUTTON), LMEM_MOVEABLE);
if (!pTBState)
return(FALSE);
SETWINDOWPOINTER(hWnd, PTBSTATE, pTBState);
if (uWhere > (UINT)pTBState->iNumButtons)
uWhere = pTBState->iNumButtons;
for (pIn=pTBState->Buttons+pTBState->iNumButtons-1, pOut=pIn+uButtons,
uWhere=(UINT)pTBState->iNumButtons-uWhere; uWhere>0;
--pIn, --pOut, --uWhere)
*pOut = *pIn;
for (lpButtons=(LPTBBUTTON)((LPSTR)lpButtons+pTBState->uStructSize*(uButtons-1)), pTBState->iNumButtons+=(int)uButtons; uButtons>0;
--pOut, lpButtons=(LPTBBUTTON)((LPSTR)lpButtons-pTBState->uStructSize), --uButtons)
{
TBInputStruct(pTBState, pOut, lpButtons);
if ((pOut->fsStyle&TBSTYLE_SEP) && pOut->iBitmap<=0)
pOut->iBitmap = dxButtonSep;
}
// flush the cache
FlushButtonCache(pTBState);
/* We need to completely redraw the toolbar at this point.
*/
InvalidateRect(hWnd, NULL, TRUE);
return(TRUE);
}
/* Notice that the state structure is not realloc'ed smaller at this
* point. This is a time optimization, and the fact that the structure
* will not move is used in other places.
*/
static BOOL NEAR PASCAL DeleteButton(HWND hWnd, PTBSTATE pTBState, UINT uIndex)
{
PTBBUTTON pIn, pOut;
if (uIndex >= (UINT)pTBState->iNumButtons)
return(FALSE);
--pTBState->iNumButtons;
for (pOut=pTBState->Buttons+uIndex, pIn=pOut+1;
uIndex<(UINT)pTBState->iNumButtons; ++uIndex, ++pIn, ++pOut)
*pOut = *pIn;
// flush the cache
FlushButtonCache(pTBState);
/* We need to completely redraw the toolbar at this point.
*/
InvalidateRect(hWnd, NULL, TRUE);
return(TRUE);
}
static void FAR PASCAL TBInputStruct(PTBSTATE pTBState, LPTBBUTTON pButtonInt, LPTBBUTTON pButtonExt)
{
if (pTBState->uStructSize >= sizeof(TBBUTTON))
{
*pButtonInt = *pButtonExt;
}
else
/* It is assumed the only other possibility is the OLDBUTTON struct */
{
*(LPOLDTBBUTTON)pButtonInt = *(LPOLDTBBUTTON)pButtonExt;
/* We don't care about dwData */
pButtonInt->iString = -1;
}
}
static void FAR PASCAL TBOutputStruct(PTBSTATE pTBState, LPTBBUTTON pButtonInt, LPTBBUTTON pButtonExt)
{
if (pTBState->uStructSize >= sizeof(TBBUTTON))
{
LPSTR pOut;
int i;
/* Fill the part we know about and fill the rest with 0's
*/
*pButtonExt = *pButtonInt;
for (i=pTBState->uStructSize-sizeof(TBBUTTON), pOut=(LPSTR)(pButtonExt+1);
i>0; --i, ++pOut)
{
*pOut = 0;
}
}
else
/* It is assumed the only other possibility is the OLDBUTTON struct */
{
*(LPOLDTBBUTTON)pButtonExt = *(LPOLDTBBUTTON)pButtonInt;
}
}
LRESULT CALLBACK _loadds ToolbarWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
BOOL fSameButton;
PTBBUTTON ptbButton;
PTBSTATE pTBState;
int iPos;
BYTE fsState;
#if WINVER >= 0x0400
DWORD dw;
#endif
pTBState = GETWINDOWPOINTER(hWnd, PTBSTATE);
switch (wMsg) {
case WM_CREATE:
#define lpcs ((LPCREATESTRUCT)lParam)
if (!CreateDitherBrush(FALSE))
return -1;
if (!InitGlobalObjects()) {
FreeGlobalObjects();
return -1;
}
/* create the state data for this toolbar */
pTBState = ALLOCWINDOWPOINTER(PTBSTATE, sizeof(TBSTATE)-sizeof(TBBUTTON));
if (!pTBState)
return -1;
/* The struct is initialized to all NULL when created.
*/
pTBState->hwndCommand = lpcs->hwndParent;
pTBState->uStructSize = 0;
// grow the button size to the appropriate girth
if (!SetBitmapSize(pTBState, DEFAULTBITMAPX, DEFAULTBITMAPX))
return -1;
SETWINDOWPOINTER(hWnd, PTBSTATE, pTBState);
if (!(lpcs->style&(CCS_TOP|CCS_NOMOVEY|CCS_BOTTOM)))
{
lpcs->style |= CCS_TOP;
SetWindowLong(hWnd, GWL_STYLE, lpcs->style);
}
break;
case WM_DESTROY:
if (pTBState)
{
PTBBMINFO pTemp;
int i;
/* Free all the bitmaps before exiting
*/
for (pTemp=pTBState->pBitmaps, i=pTBState->nBitmaps-1; i>=0;
++pTemp, --i)
{
if (pTemp->hInst && pTemp->hbm)
DeleteObject(pTemp->hbm);
}
FlushButtonCache(pTBState);
if (pTBState->nStrings > 0)
DestroyStrings(pTBState);
FREEWINDOWPOINTER(pTBState);
SETWINDOWPOINTER(hWnd, PTBSTATE, 0);
}
FreeGlobalObjects();
FreeDitherBrush();
break;
case WM_NCCALCSIZE:
#if WINVER >= 0x0400
/*
* This is sent when the window manager wants to find out
* how big our client area is to be. If we have a mini-caption
* then we trap this message and calculate the cleint area rect,
* which is the client area rect calculated by DefWindowProc()
* minus the width/height of the mini-caption bar
*/
// let defwindowproc handle the standard borders etc...
dw = DefWindowProc(hWnd, wMsg, wParam, lParam ) ;
if (!(GetWindowLong(hWnd, GWL_STYLE) & CCS_NODIVIDER))
{
NCCALCSIZE_PARAMS FAR *lpNCP;
lpNCP = (NCCALCSIZE_PARAMS FAR *)lParam;
lpNCP->rgrc[0].top += 2;
}
return dw;
#endif
break;
case WM_NCACTIVATE:
case WM_NCPAINT:
#if WINVER >= 0x0400
// old-style toolbars are forced to be without dividers above
if (!(GetWindowLong(hWnd, GWL_STYLE) & CCS_NODIVIDER))
{
HDC hdc;
RECT rc;
hdc = GetWindowDC(hWnd);
GetWindowRect(hWnd, &rc);
ScreenToClient(hWnd, (LPPOINT)&(rc.left));
ScreenToClient(hWnd, (LPPOINT)&(rc.right));
rc.bottom = (-rc.top); // bottom of NC area
rc.top = rc.bottom - (2 * GetSystemMetrics(SM_CYBORDER));
DrawBorder(hdc, &rc, BDR_SUNKENOUTER, BF_TOP | BF_BOTTOM);
ReleaseDC(hWnd, hdc);
}
else
goto DoDefault;
#endif
break;
case WM_PAINT:
ToolbarPaint(hWnd, pTBState);
break;
#if 0
case TB_AUTOSIZE:
case WM_SIZE:
{
RECT rc;
HWND hwndParent;
GetWindowRect(hWnd, &rc);
rc.right -= rc.left;
rc.bottom -= rc.top;
/* If there is no parent, then this is a top level window
*/
hwndParent = GetParent(hWnd);
if (hwndParent)
ScreenToClient(hwndParent, (LPPOINT)&rc);
NewSize(hWnd, pTBState->iBarHeight, GetWindowLong(hWnd, GWL_STYLE),
rc.left, rc.top, rc.right, rc.bottom);
break;
}
#endif
case WM_HSCROLL: //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
case WM_COMMAND:
case WM_DRAWITEM:
case WM_MEASUREITEM:
case WM_VKEYTOITEM:
case WM_CHARTOITEM:
SendMessage(pTBState->hwndCommand, wMsg, wParam, lParam);
break;
case WM_CTLCOLOR:
//!!!!! ack use COLOR_BTNFACE
return (LRESULT)(UINT)GetStockObject(LTGRAY_BRUSH);
#if 0
case WM_LBUTTONDBLCLK:
iPos = TBHitTest(pTBState, LOWORD(lParam), HIWORD(lParam));
if (iPos<0 && (GetWindowLong(hWnd, GWL_STYLE)&CCS_ADJUSTABLE))
{
iPos = -1 - iPos;
CustomizeTB(hWnd, pTBState, iPos);
}
break;
#endif
case WM_LBUTTONDOWN:
iPos = TBHitTest(pTBState, LOWORD(lParam), HIWORD(lParam));
#if 0
if ((wParam&MK_SHIFT) &&(GetWindowLong(hWnd, GWL_STYLE)&CCS_ADJUSTABLE))
{
MoveButton(hWnd, pTBState, iPos);
} else
#endif
if (iPos >= 0)
{
ptbButton = pTBState->Buttons + iPos;
pTBState->pCaptureButton = ptbButton;
SetCapture(hWnd);
if (ptbButton->fsState & TBSTATE_ENABLED)
{
ptbButton->fsState |= TBSTATE_PRESSED;
InvalidateButton(hWnd, pTBState, ptbButton);
UpdateWindow(hWnd); // imedeate feedback
}
SendMessage(pTBState->hwndCommand, WM_COMMAND, GETWINDOWID(hWnd), MAKELONG(pTBState->pCaptureButton->idCommand, TBN_BEGINDRAG));
}
break;
case WM_MOUSEMOVE:
// if the toolbar has lost the capture for some reason, stop
if (hWnd != GetCapture()) {
SendMessage(pTBState->hwndCommand, WM_COMMAND, GETWINDOWID(hWnd),
MAKELONG(pTBState->pCaptureButton->idCommand, TBN_ENDDRAG));
// if the button is still pressed, unpress it.
if (pTBState->pCaptureButton->fsState & TBSTATE_PRESSED)
SendMessage(hWnd, TB_PRESSBUTTON, pTBState->pCaptureButton->idCommand, 0L);
pTBState->pCaptureButton = NULL;
}
else if (pTBState->pCaptureButton!=NULL
&& (pTBState->pCaptureButton->fsState & TBSTATE_ENABLED)) {
iPos = TBHitTest(pTBState, LOWORD(lParam), HIWORD(lParam));
fSameButton = (iPos>=0
&& pTBState->pCaptureButton==pTBState->Buttons+iPos);
if (fSameButton == !(pTBState->pCaptureButton->fsState & TBSTATE_PRESSED)) {
pTBState->pCaptureButton->fsState ^= TBSTATE_PRESSED;
InvalidateButton(hWnd, pTBState, pTBState->pCaptureButton);
}
}
break;
case WM_LBUTTONUP:
if (pTBState->pCaptureButton != NULL) {
int idCommand;
idCommand = pTBState->pCaptureButton->idCommand;
ReleaseCapture();
SendMessage(pTBState->hwndCommand, WM_COMMAND, GETWINDOWID(hWnd), MAKELONG(idCommand, TBN_ENDDRAG));
iPos = TBHitTest(pTBState, LOWORD(lParam), HIWORD(lParam));
if ((pTBState->pCaptureButton->fsState&TBSTATE_ENABLED) && iPos>=0
&& (pTBState->pCaptureButton==pTBState->Buttons+iPos)) {
pTBState->pCaptureButton->fsState &= ~TBSTATE_PRESSED;
if (pTBState->pCaptureButton->fsStyle & TBSTYLE_CHECK) {
if (pTBState->pCaptureButton->fsStyle & TBSTYLE_GROUP) {
// group buttons already checked can't be force
// up by the user.
if (pTBState->pCaptureButton->fsState & TBSTATE_CHECKED) {
pTBState->pCaptureButton = NULL;
break; // bail!
}
pTBState->pCaptureButton->fsState |= TBSTATE_CHECKED;
MakeGroupConsistant(hWnd, pTBState, idCommand);
} else {
pTBState->pCaptureButton->fsState ^= TBSTATE_CHECKED; // toggle
}
// if we change a button's state, we need to flush the
// cache
FlushButtonCache(pTBState);
}
InvalidateButton(hWnd, pTBState, pTBState->pCaptureButton);
pTBState->pCaptureButton = NULL;
SendMessage(pTBState->hwndCommand, WM_COMMAND, idCommand, 0L);
}
else {
pTBState->pCaptureButton = NULL;
}
}
break;
case TB_SETSTATE:
iPos = PositionFromID(pTBState, (int)wParam);
if (iPos < 0)
return(FALSE);
ptbButton = pTBState->Buttons + iPos;
fsState = (BYTE)(LOWORD(lParam) ^ ptbButton->fsState);
ptbButton->fsState = (BYTE)LOWORD(lParam);
if (fsState)
// flush the button cache
//!!!! this could be much more intelligent
FlushButtonCache(pTBState);
if (fsState & TBSTATE_HIDDEN)
InvalidateRect(hWnd, NULL, TRUE);
else if (fsState)
InvalidateButton(hWnd, pTBState, ptbButton);
return(TRUE);
case TB_GETSTATE:
iPos = PositionFromID(pTBState, (int)wParam);
if (iPos < 0)
return(-1L);
return(pTBState->Buttons[iPos].fsState);
case TB_ENABLEBUTTON:
case TB_CHECKBUTTON:
case TB_PRESSBUTTON:
case TB_HIDEBUTTON:
case TB_INDETERMINATE:
iPos = PositionFromID(pTBState, (int)wParam);
if (iPos < 0)
return(FALSE);
ptbButton = &pTBState->Buttons[iPos];
fsState = ptbButton->fsState;
if (LOWORD(lParam))
ptbButton->fsState |= wStateMasks[wMsg - TB_ENABLEBUTTON];
else
ptbButton->fsState &= ~wStateMasks[wMsg - TB_ENABLEBUTTON];
// did this actually change the state?
if (fsState != ptbButton->fsState) {
// is this button a member of a group?
if ((wMsg == TB_CHECKBUTTON) && (ptbButton->fsStyle & TBSTYLE_GROUP))
MakeGroupConsistant(hWnd, pTBState, (int)wParam);
// flush the button cache
//!!!! this could be much more intelligent
FlushButtonCache(pTBState);
if (wMsg == TB_HIDEBUTTON)
InvalidateRect(hWnd, NULL, TRUE);
else
InvalidateButton(hWnd, pTBState, ptbButton);
}
return(TRUE);
case TB_ISBUTTONENABLED:
case TB_ISBUTTONCHECKED:
case TB_ISBUTTONPRESSED:
case TB_ISBUTTONHIDDEN:
case TB_ISBUTTONINDETERMINATE:
iPos = PositionFromID(pTBState, (int)wParam);
if (iPos < 0)
return(-1L);
return (LRESULT)pTBState->Buttons[iPos].fsState
& wStateMasks[wMsg - TB_ISBUTTONENABLED];
case TB_ADDBITMAP:
return(AddBitmap(pTBState, wParam,
(HINSTANCE)LOWORD(lParam), HIWORD(lParam)));
#if 0 // not needed for MCIWnd
case TB_ADDSTRING:
return(AddStrings(pTBState, wParam, lParam));
#endif
case TB_ADDBUTTONS:
return(InsertButtons(hWnd, pTBState, (UINT)-1, wParam,
(LPTBBUTTON)lParam));
case TB_INSERTBUTTON:
return(InsertButtons(hWnd, pTBState, wParam, 1, (LPTBBUTTON)lParam));
case TB_DELETEBUTTON:
return(DeleteButton(hWnd, pTBState, wParam));
case TB_GETBUTTON:
if (wParam >= (UINT)pTBState->iNumButtons)
return(FALSE);
TBOutputStruct(pTBState, pTBState->Buttons+wParam, (LPTBBUTTON)lParam);
return(TRUE);
case TB_BUTTONCOUNT:
return(pTBState->iNumButtons);
case TB_COMMANDTOINDEX:
return(PositionFromID(pTBState, (int)wParam));
#if 0
case TB_SAVERESTORE:
return(SaveRestore(hWnd, pTBState, wParam, (LPSTR FAR *)lParam));
case TB_CUSTOMIZE:
CustomizeTB(hWnd, pTBState, pTBState->iNumButtons);
break;
#endif
case TB_GETITEMRECT:
return(MAKELRESULT(GetItemRect(pTBState, wParam, (LPRECT)lParam), 0));
break;
case TB_BUTTONSTRUCTSIZE:
/* You are not allowed to change this after adding buttons.
*/
if (!pTBState || pTBState->iNumButtons)
{
break;
}
pTBState->uStructSize = wParam;
break;
case TB_SETBUTTONSIZE:
if (!LOWORD(lParam))
lParam = MAKELONG(DEFAULTBUTTONX, HIWORD(lParam));
if (!HIWORD(lParam))
lParam = MAKELONG(LOWORD(lParam), DEFAULTBUTTONY);
return(GrowToolbar(pTBState, LOWORD(lParam), HIWORD(lParam), FALSE));
case TB_SETBITMAPSIZE:
return(SetBitmapSize(pTBState, LOWORD(lParam), HIWORD(lParam)));
case TB_SETBUTTONTYPE:
pTBState->wButtonType = wParam;
break;
default:
#if WINVER >= 0x0400
DoDefault:
#endif
return DefWindowProc(hWnd, wMsg, wParam, lParam);
}
return 0L;
}