mirror of https://github.com/lianthony/NT4.0
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.
1168 lines
31 KiB
1168 lines
31 KiB
#include "ctlspriv.h"
|
|
|
|
TCHAR szToolbarClass[] = TEXT("ToolbarWindow");
|
|
|
|
// these values are defined by the UI gods...
|
|
|
|
int dyToolbar = 27;
|
|
int dxButton = 24;
|
|
int dyButton = 22;
|
|
int dxBitmap = 16;
|
|
int dyBitmap = 15;
|
|
|
|
int dxButtonSep = 8;
|
|
int xFirstButton = 8;
|
|
|
|
int iInitCount = 0;
|
|
|
|
int nSelectedBM = -1;
|
|
HDC hdcGlyphs = NULL; // globals for fast drawing
|
|
HDC hdcMono = NULL;
|
|
HDC hdcOffScreen = NULL;
|
|
HBITMAP hbmMono = NULL;
|
|
HBITMAP hbmOffScreen = NULL;
|
|
TBBUTTON tbButtonTemp;
|
|
|
|
WORD wStateMasks[] = {
|
|
TBSTATE_ENABLED,
|
|
TBSTATE_CHECKED,
|
|
TBSTATE_PRESSED,
|
|
TBSTATE_HIDDEN
|
|
};
|
|
|
|
LRESULT CALLBACK ToolbarWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
BOOL NEAR PASCAL InitGlobalObjects(void)
|
|
{
|
|
HDC hdc;
|
|
|
|
iInitCount++;
|
|
|
|
if (iInitCount != 1)
|
|
return TRUE;
|
|
|
|
hdcOffScreen = CreateCompatibleDC(NULL);
|
|
if (!hdcOffScreen)
|
|
return FALSE;
|
|
|
|
hdcGlyphs = CreateCompatibleDC(NULL);
|
|
if (!hdcGlyphs)
|
|
return FALSE;
|
|
hdcMono = CreateCompatibleDC(NULL);
|
|
if (!hdcMono)
|
|
return FALSE;
|
|
|
|
hbmMono = CreateBitmap(dxButton - 2, dyButton - 2, 1, 1, NULL);
|
|
if (!hbmMono)
|
|
return FALSE;
|
|
|
|
hdc = GetDC(NULL);
|
|
hbmOffScreen = CreateBitmap(dxButton, dyButton,
|
|
GetDeviceCaps(hdc, PLANES), GetDeviceCaps(hdc, BITSPIXEL), NULL);
|
|
ReleaseDC(NULL, hdc);
|
|
if (!hdcOffScreen)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL NEAR PASCAL FreeGlobalObjects(void)
|
|
{
|
|
iInitCount--;
|
|
|
|
if (iInitCount != 0)
|
|
return TRUE;
|
|
|
|
if (hdcMono)
|
|
DeleteDC(hdcMono); // toast the DCs
|
|
hdcMono = NULL;
|
|
|
|
if (hdcGlyphs)
|
|
DeleteDC(hdcGlyphs);
|
|
hdcGlyphs = NULL;
|
|
|
|
if (hdcOffScreen)
|
|
DeleteDC(hdcOffScreen);
|
|
hdcOffScreen = NULL;
|
|
|
|
if (hbmOffScreen)
|
|
DeleteObject(hbmOffScreen); // and the bitmaps
|
|
hbmOffScreen = NULL;
|
|
|
|
if (hbmMono)
|
|
DeleteObject(hbmMono);
|
|
hbmMono = NULL;
|
|
}
|
|
|
|
|
|
HWND WINAPI CreateToolbar(
|
|
HWND hwnd,
|
|
DWORD ws,
|
|
WORD wID,
|
|
int nBitmaps,
|
|
HINSTANCE hBMInst,
|
|
WORD wBMID,
|
|
LPTBBUTTON lpButtons,
|
|
int iNumButtons)
|
|
{
|
|
HWND hwndToolbar;
|
|
|
|
hwndToolbar = CreateWindow( szToolbarClass,
|
|
NULL,
|
|
WS_CHILD | ws,
|
|
-100,
|
|
-100,
|
|
10,
|
|
10,
|
|
hwnd,
|
|
(HMENU)wID,
|
|
hInst,
|
|
NULL );
|
|
if (!hwndToolbar)
|
|
goto Error1;
|
|
|
|
SendMessage( hwndToolbar,
|
|
TB_ADDBITMAP,
|
|
GET_WM_COMMAND_MPS(nBitmaps, hBMInst, wBMID) );
|
|
|
|
SendMessage( hwndToolbar,
|
|
TB_ADDBUTTONS,
|
|
iNumButtons,
|
|
(LPARAM)lpButtons );
|
|
|
|
Error1:
|
|
return (hwndToolbar);
|
|
}
|
|
|
|
|
|
BOOL FAR PASCAL InitToolbarClass(
|
|
HINSTANCE hInstance)
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
if (!GetClassInfo(hInstance, szToolbarClass, &wc))
|
|
{
|
|
wc.lpszClassName = szToolbarClass;
|
|
wc.style = CS_DBLCLKS;
|
|
wc.lpfnWndProc = (WNDPROC)ToolbarWndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = sizeof(PTBSTATE);
|
|
wc.hInstance = hInstance; // use DLL instance if in DLL
|
|
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
|
|
|
|
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);
|
|
}
|
|
|
|
// create a mono bitmap mask:
|
|
// 1's where color == COLOR_BTNFACE || COLOR_HILIGHT
|
|
// 0's everywhere else
|
|
|
|
void NEAR PASCAL CreateMask(PTBBUTTON pTBButton, int offset)
|
|
{
|
|
if (offset)
|
|
// initalize whole area with 0's
|
|
PatBlt(hdcMono, 0, 0, dxButton - 2, dyButton - 2, WHITENESS);
|
|
|
|
// create mask based on color bitmap
|
|
// convert this to 1's
|
|
SetBkColor(hdcGlyphs, rgbFace);
|
|
BitBlt(hdcMono, offset, offset, dxBitmap, dyBitmap, hdcGlyphs, pTBButton->iBitmap * dxBitmap, 0, SRCCOPY);
|
|
// convert this to 1's
|
|
SetBkColor(hdcGlyphs, rgbHilight);
|
|
// OR in the new 1's
|
|
BitBlt(hdcMono, offset, offset, dxBitmap, dyBitmap, hdcGlyphs, pTBButton->iBitmap * dxBitmap, 0, SRCPAINT);
|
|
}
|
|
|
|
|
|
/* 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.
|
|
*/
|
|
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 (GetObjectType(pTemp->hInst) == OBJ_BITMAP)
|
|
{
|
|
pTemp->hbm = (HBITMAP)pTemp->hInst;
|
|
pTemp->hInst = 0;
|
|
}
|
|
else
|
|
{
|
|
pTemp->hbm = CreateMappedBitmap( pTemp->hInst,
|
|
pTemp->wID,
|
|
TRUE,
|
|
NULL,
|
|
0 );
|
|
}
|
|
|
|
if (!pTemp->hbm || (hRet = SelectObject(hDC, pTemp->hbm)) == NULL)
|
|
{
|
|
return (NULL);
|
|
}
|
|
}
|
|
|
|
nSelectedBM = nBitmap;
|
|
|
|
SetWindowOrgEx(hDC, nTot * dxBitmap, 0, NULL);
|
|
|
|
return (hRet);
|
|
}
|
|
|
|
|
|
#define DPa 0x00A000C9L
|
|
#define DSPDxax 0x00E20746
|
|
#define PSDPxax 0x00B8074A
|
|
|
|
#define FillBkColor(hdc, prc) ExtTextOut(hdc,0,0,ETO_OPAQUE,prc,NULL,0,NULL)
|
|
|
|
void FAR PASCAL DrawButton(HDC hdc, int x, int y, int dx, int dy, PTBSTATE pTBState, PTBBUTTON ptButton)
|
|
{
|
|
int glyph_offset;
|
|
HBRUSH hbrOld, hbr;
|
|
BOOL bMaskCreated = FALSE;
|
|
|
|
// erase with face color
|
|
// PatB(hdc, x, y, dx, dy, rgbFace);
|
|
|
|
// border around button
|
|
|
|
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);
|
|
|
|
x++; // make the coordinates the interior of the button
|
|
y++;
|
|
dx -= 2;
|
|
dy -= 2;
|
|
|
|
// interior grey
|
|
PatB(hdc, x, y, dx, dy, rgbFace);
|
|
|
|
if (!SelectBM(hdcGlyphs, pTBState, ptButton->iBitmap))
|
|
return;
|
|
|
|
if (ptButton->fsState & (TBSTATE_PRESSED | TBSTATE_CHECKED))
|
|
glyph_offset = 3;
|
|
else
|
|
glyph_offset = 2;
|
|
|
|
if (ptButton->fsState & TBSTATE_PRESSED) {
|
|
// pressed in button
|
|
PatB(hdc, x, y, 1, dy, rgbShadow);
|
|
PatB(hdc, x, y, dx, 1, rgbShadow);
|
|
} else if (!(ptButton->fsState & TBSTATE_CHECKED)) {
|
|
// regular button look
|
|
PatB(hdc, x, y, 1, dy - 1, rgbHilight);
|
|
PatB(hdc, x, y, dx - 1, 1, rgbHilight);
|
|
|
|
PatB(hdc, x + dx - 1, y, 1, dy, rgbShadow);
|
|
PatB(hdc, x, y + dy-1, dx, 1, rgbShadow);
|
|
|
|
PatB(hdc, x + 1 + dx - 3, y + 1, 1, dy - 2, rgbShadow);
|
|
PatB(hdc, x + 1, y + dy - 2, dx - 2, 1, rgbShadow);
|
|
}
|
|
|
|
// now put on the face
|
|
if (ptButton->fsState & TBSTATE_ENABLED) {
|
|
// regular version
|
|
BitBlt(hdc, x + glyph_offset, y + glyph_offset, dxBitmap, dyBitmap,
|
|
hdcGlyphs, ptButton->iBitmap * dxBitmap, 0, SRCCOPY);
|
|
} else {
|
|
|
|
// disabled version
|
|
bMaskCreated = TRUE;
|
|
CreateMask(ptButton, glyph_offset);
|
|
|
|
SetTextColor(hdc, 0L); // 0's in mono -> 0 (for ROP)
|
|
SetBkColor(hdc, 0x00FFFFFF); // 1's in mono -> 1
|
|
|
|
if (!(ptButton->fsState & TBSTATE_CHECKED)) {
|
|
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, dxButton - 2, dyButton - 2, 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, dxButton - 2, dyButton - 2, hdcMono, 0, 0, PSDPxax);
|
|
SelectObject(hdc, hbrOld);
|
|
}
|
|
DeleteObject(hbr);
|
|
}
|
|
}
|
|
|
|
#if 1
|
|
|
|
// if it is checked do the dither brush avoiding the glyph
|
|
|
|
if (ptButton->fsState & TBSTATE_CHECKED) {
|
|
|
|
hbrOld = SelectObject(hdc, hbrDither);
|
|
if (hbrOld) {
|
|
|
|
if (!bMaskCreated)
|
|
CreateMask(ptButton, glyph_offset);
|
|
|
|
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, dxButton - 2, dyButton - 2, hdcMono, 0, 0, DSPDxax);
|
|
// BitBlt(hdc, x, y, dxButton - 2, dyButton - 2, hdcMono, 0, 0, SRCCOPY);
|
|
|
|
SelectObject(hdc, hbrOld);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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, hbmOldMono, hbmOldOffScreen;
|
|
|
|
CheckSysColors();
|
|
UpdateTBState(pTBState);
|
|
|
|
hdc = BeginPaint(hWnd, &ps);
|
|
|
|
GetClientRect(hWnd, &rc);
|
|
if (!rc.right)
|
|
goto Error1;
|
|
|
|
// 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;
|
|
|
|
hbmOldMono = SelectObject(hdcMono, hbmMono);
|
|
hbmOldOffScreen = SelectObject(hdcOffScreen, hbmOffScreen);
|
|
|
|
#if 0
|
|
if (!(GetWindowLong(hWnd, GWL_STYLE)&CCS_NOHILITE))
|
|
{
|
|
HBRUSH hHiliteBrush, hOldBrush;
|
|
int yBorder;
|
|
|
|
yBorder = GetSystemMetrics(SM_CYBORDER);
|
|
|
|
hHiliteBrush = CreateSolidBrush(rgbHilight);
|
|
if (hHiliteBrush)
|
|
{
|
|
hOldBrush = SelectObject(ps.hdc, hHiliteBrush);
|
|
if (hOldBrush)
|
|
{
|
|
PatBlt(ps.hdc, 0, 0, rc.right, yBorder, PATCOPY);
|
|
SelectObject(ps.hdc, hOldBrush);
|
|
}
|
|
|
|
DeleteObject(hHiliteBrush);
|
|
}
|
|
|
|
hHiliteBrush = CreateSolidBrush(rgbShadow);
|
|
if (hHiliteBrush)
|
|
{
|
|
hOldBrush = SelectObject(ps.hdc, hHiliteBrush);
|
|
if (hOldBrush)
|
|
{
|
|
PatBlt(ps.hdc, 0, rc.bottom, rc.right, -yBorder, PATCOPY);
|
|
SelectObject(ps.hdc, hOldBrush);
|
|
}
|
|
|
|
DeleteObject(hHiliteBrush);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
yButton = ((rc.bottom - rc.top) - dyButton) / 2;
|
|
rc.top = yButton;
|
|
rc.bottom = yButton + dyButton;
|
|
|
|
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 {
|
|
rc.left = xButton;
|
|
rc.right = xButton + dxButton;
|
|
if (RectVisible(hdc, &rc))
|
|
DrawButton(hdc, xButton, yButton, dxButton, dyButton, pTBState, ptbButton);
|
|
xButton += dxButton - 1;
|
|
}
|
|
}
|
|
|
|
if (hbmOldMono)
|
|
SelectObject(hdcMono, hbmOldMono);
|
|
if (hbmOldOffScreen)
|
|
SelectObject(hdcOffScreen, hbmOldOffScreen);
|
|
SelectObject(hdcGlyphs, hbmOldGlyphs);
|
|
|
|
Error1:
|
|
EndPaint(hWnd, &ps);
|
|
}
|
|
|
|
|
|
void NEAR PASCAL InvalidateButton(HWND hwnd, PTBSTATE pTBState, PTBBUTTON pButtonToPaint)
|
|
{
|
|
RECT rc;
|
|
int iButton;
|
|
int xButton, yButton;
|
|
PTBBUTTON ptbButton;
|
|
int cButtons = pTBState->iNumButtons;
|
|
PTBBUTTON pAllButtons = pTBState->Buttons;
|
|
|
|
if ((pButtonToPaint->fsStyle&TBSTYLE_SEP)
|
|
|| (pButtonToPaint->fsState&TBSTATE_HIDDEN))
|
|
return;
|
|
|
|
GetClientRect(hwnd, &rc);
|
|
|
|
yButton = ((rc.bottom - rc.top) - dyButton) / 2;
|
|
|
|
for (iButton = 0, xButton = xFirstButton; iButton < cButtons; iButton++) {
|
|
|
|
ptbButton = &pAllButtons[iButton];
|
|
|
|
if (ptbButton == pButtonToPaint)
|
|
break;
|
|
|
|
if (ptbButton->fsState & TBSTATE_HIDDEN)
|
|
/* Do nothing */ ;
|
|
else if (ptbButton->fsStyle & TBSTYLE_SEP)
|
|
xButton += ptbButton->iBitmap;
|
|
else
|
|
xButton += dxButton - 1;
|
|
}
|
|
rc.left = xButton;
|
|
rc.right = xButton + dxButton;
|
|
// rc.top = yButton;
|
|
// rc.bottom = yButton + dyButton;
|
|
|
|
InvalidateRect(hwnd, &rc, FALSE);
|
|
}
|
|
|
|
|
|
INT
|
|
TBHitTest(PTBSTATE pTBState, INT xPos, INT yPos)
|
|
{
|
|
INT iButton;
|
|
INT cButtons = pTBState->iNumButtons;
|
|
PTBBUTTON pButton;
|
|
|
|
xPos -= xFirstButton;
|
|
if (xPos < 0)
|
|
return(-1);
|
|
yPos -= (dyToolbar - dyButton) / 2;
|
|
|
|
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 -= dxButton - 1;
|
|
|
|
if (xPos < 0) {
|
|
if (pButton->fsStyle&TBSTYLE_SEP || (UINT)yPos>=(UINT)dyButton)
|
|
break;
|
|
|
|
return(iButton);
|
|
}
|
|
}
|
|
|
|
return(-1 - iButton);
|
|
}
|
|
|
|
|
|
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
|
|
|
|
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?
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* 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( LMEM_FIXED,
|
|
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)
|
|
return(FALSE);
|
|
|
|
pTBState = (PTBSTATE)LocalReAlloc(pTBState, sizeof(TBSTATE)-sizeof(TBBUTTON)
|
|
+ (pTBState->iNumButtons+uButtons)*sizeof(TBBUTTON), LMEM_MOVEABLE);
|
|
if (!pTBState)
|
|
return(FALSE);
|
|
|
|
SetWindowLong(hWnd, GWL_PTBSTATE, (LONG)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+=uButtons-1, pTBState->iNumButtons+=(int)uButtons; uButtons>0;
|
|
--pOut, --lpButtons, --uButtons)
|
|
{
|
|
*pOut = *lpButtons;
|
|
|
|
if ((pOut->fsStyle&TBSTYLE_SEP) && pOut->iBitmap<=0)
|
|
pOut->iBitmap = dxButtonSep;
|
|
}
|
|
|
|
|
|
/* 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;
|
|
|
|
/* We need to completely redraw the toolbar at this point.
|
|
*/
|
|
InvalidateRect(hWnd, NULL, TRUE);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/* Find the coordinates of the specified button.
|
|
* Uses similar logic to InvalidateButton.
|
|
* (andrewbe)
|
|
*/
|
|
Static BOOL NEAR PASCAL GetButtonRect(HWND hWnd, PTBSTATE pTBState, UINT uIndex, PRECT prc)
|
|
{
|
|
RECT rc;
|
|
int iButton;
|
|
int xButton, yButton;
|
|
PTBBUTTON ptbButton;
|
|
int cButtons = pTBState->iNumButtons;
|
|
PTBBUTTON pAllButtons = pTBState->Buttons;
|
|
|
|
if (uIndex >= (UINT)pTBState->iNumButtons)
|
|
return(FALSE);
|
|
|
|
GetClientRect(hWnd, &rc);
|
|
|
|
yButton = ((rc.bottom - rc.top) - dyButton) / 2;
|
|
|
|
for (iButton = 0, xButton = xFirstButton; uIndex > (UINT)iButton; iButton++)
|
|
{
|
|
ptbButton = &pAllButtons[iButton];
|
|
|
|
if (ptbButton->fsState & TBSTATE_HIDDEN)
|
|
/* Do nothing */ ;
|
|
else if (ptbButton->fsStyle & TBSTYLE_SEP)
|
|
xButton += ptbButton->iBitmap;
|
|
else
|
|
xButton += dxButton - 1;
|
|
}
|
|
|
|
prc->left = xButton;
|
|
prc->top = yButton;
|
|
|
|
if (ptbButton->fsState & TBSTATE_HIDDEN) {
|
|
/* Zero-dimension rectangle if it's hidden:
|
|
*/
|
|
prc->right = prc->left;
|
|
prc->bottom = prc->bottom;
|
|
} else if (ptbButton->fsStyle & TBSTYLE_SEP) {
|
|
prc->right = xButton + ptbButton->iBitmap;
|
|
prc->bottom = yButton + dyButton;
|
|
} else {
|
|
prc->right = xButton + dxButton;
|
|
prc->bottom = yButton + dyButton;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
LRESULT CALLBACK
|
|
ToolbarWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BOOL fSameButton;
|
|
PTBBUTTON ptbButton;
|
|
PTBSTATE pTBState;
|
|
INT iPos;
|
|
BYTE fsState;
|
|
|
|
pTBState = (PTBSTATE)GetWindowLong(hWnd, GWL_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 = (PTBSTATE)LocalAlloc(LPTR, sizeof(TBSTATE) - sizeof(TBBUTTON));
|
|
if (!pTBState)
|
|
return -1;
|
|
|
|
// The struct is initialized to all NULL when created.
|
|
|
|
pTBState->hwndCommand = lpcs->hwndParent;
|
|
|
|
SetWindowLong(hWnd, GWL_PTBSTATE, (LONG)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);
|
|
}
|
|
}
|
|
|
|
LocalFree((HLOCAL)pTBState);
|
|
|
|
SetWindowLong(hWnd, GWL_PTBSTATE, 0L);
|
|
}
|
|
FreeGlobalObjects();
|
|
FreeDitherBrush();
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
ToolbarPaint(hWnd, pTBState);
|
|
break;
|
|
|
|
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, dyToolbar, GetWindowLong(hWnd, GWL_STYLE),
|
|
rc.left, rc.top, rc.right, rc.bottom);
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
case WM_DRAWITEM:
|
|
case WM_MEASUREITEM:
|
|
case WM_VKEYTOITEM:
|
|
case WM_CHARTOITEM:
|
|
SendMessage(pTBState->hwndCommand, wMsg, wParam, lParam);
|
|
break;
|
|
|
|
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;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
iPos = TBHitTest(pTBState, LOWORD(lParam), HIWORD(lParam));
|
|
if ((wParam&MK_SHIFT) &&(GetWindowLong(hWnd, GWL_STYLE)&CCS_ADJUSTABLE)) {
|
|
|
|
MoveButton(hWnd, pTBState, iPos);
|
|
|
|
} else 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,
|
|
GET_WM_COMMAND_MPS(GetWindowLong(hWnd, GWL_ID), pTBState->pCaptureButton->idCommand, TBN_BEGINDRAG));
|
|
|
|
}
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
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,
|
|
GET_WM_COMMAND_MPS(GetWindowLong(hWnd, GWL_ID), 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
|
|
}
|
|
}
|
|
InvalidateButton(hWnd, pTBState, pTBState->pCaptureButton);
|
|
pTBState->pCaptureButton = NULL;
|
|
|
|
SendMessage(pTBState->hwndCommand, WM_COMMAND,
|
|
GET_WM_COMMAND_MPS(idCommand, 0L,0));
|
|
}
|
|
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 & 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:
|
|
|
|
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);
|
|
|
|
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:
|
|
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,
|
|
GET_WM_COMMAND_ID(wParam, lParam),
|
|
(HINSTANCE)GET_WM_COMMAND_HWND(wParam, lParam),
|
|
GET_WM_COMMAND_CMD(wParam, lParam) ) );
|
|
|
|
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);
|
|
*((LPTBBUTTON)lParam) = pTBState->Buttons[wParam];
|
|
return(TRUE);
|
|
|
|
case TB_SETBUTTON:
|
|
if (wParam >= (UINT)pTBState->iNumButtons)
|
|
return(FALSE);
|
|
pTBState->Buttons[wParam] = *((LPTBBUTTON)lParam);
|
|
InvalidateButton(hWnd, pTBState, &pTBState->Buttons[wParam]);
|
|
return(TRUE);
|
|
|
|
case TB_BUTTONCOUNT:
|
|
return(pTBState->iNumButtons);
|
|
|
|
case TB_COMMANDTOINDEX:
|
|
return(PositionFromID(pTBState, (int)wParam));
|
|
|
|
case TB_SAVERESTORE:
|
|
return(SaveRestore(hWnd, pTBState, wParam, (LPTSTR *)lParam));
|
|
|
|
case TB_CUSTOMIZE:
|
|
CustomizeTB(hWnd, pTBState, pTBState->iNumButtons);
|
|
break;
|
|
|
|
case TB_GETBUTTONRECT:
|
|
return GetButtonRect(hWnd, pTBState, wParam, (PRECT)lParam);
|
|
|
|
default:
|
|
return DefWindowProc(hWnd, wMsg, wParam, lParam);
|
|
}
|
|
|
|
return 0L;
|
|
}
|
|
|
|
|