3539 lines
100 KiB
Plaintext
3539 lines
100 KiB
Plaintext
/*
|
|
** Toolbar.c
|
|
**
|
|
** This is it, the incredibly famous toolbar control. Most of
|
|
** the customization stuff is in another file.
|
|
*/
|
|
|
|
#include "ctlspriv.h"
|
|
#include "toolbar.h"
|
|
#include "image.h"
|
|
#include <limits.h>
|
|
|
|
#define LPARAM_TO_POINT(lParam, pt) ((pt).x = LOWORD(lParam), \
|
|
(pt).y = HIWORD(lParam))
|
|
#define TBIMAGELIST
|
|
// these values are defined by the UI gods...
|
|
#define DEFAULTBITMAPX 16
|
|
#define DEFAULTBITMAPY 15
|
|
|
|
#define LIST_GAP g_cxEdge * 2
|
|
|
|
#define SMALL_DXYBITMAP 16 // new dx dy for sdt images
|
|
#define LARGE_DXYBITMAP 24
|
|
|
|
#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
|
|
|
|
const int g_dxButtonSep = 8;
|
|
const int s_xFirstButton = 0; // was 8 in 3.1
|
|
#define s_dxOverlap 0 // was 1 in 3.1
|
|
|
|
// Globals - since all of these globals are used durring a paint we have to
|
|
// take a criticial section around all toolbar paints. this sucks.
|
|
//
|
|
|
|
const UINT wStateMasks[] = {
|
|
TBSTATE_ENABLED,
|
|
TBSTATE_CHECKED,
|
|
TBSTATE_PRESSED,
|
|
TBSTATE_HIDDEN,
|
|
TBSTATE_INDETERMINATE,
|
|
TBSTATE_HIGHLIGHTED
|
|
};
|
|
|
|
#define TBISSTRINGPTR(iString) (((iString) != -1) && (HIWORD(iString)))
|
|
|
|
LRESULT CALLBACK ToolbarWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
|
|
void NEAR PASCAL TBOnButtonStructSize(PTBSTATE ptb, UINT uStructSize);
|
|
BOOL NEAR PASCAL SetBitmapSize(PTBSTATE ptb, int width, int height);
|
|
int NEAR PASCAL AddBitmap(PTBSTATE ptb, int nButtons, HINSTANCE hBMInst, UINT wBMID);
|
|
BOOL NEAR PASCAL GrowToolbar(PTBSTATE ptb, int newButWidth, int newButHeight, BOOL bInside);
|
|
void NEAR PASCAL TBBuildImageList(PTBSTATE ptb);
|
|
BOOL NEAR PASCAL GetItemRect(PTBSTATE ptb, UINT uButton, LPRECT lpRect);
|
|
LPTSTR TB_StrForButton(PTBSTATE ptb, LPTBBUTTON pTBButton);
|
|
|
|
#define TBInvalidateImageList(ptb) ((ptb)->fHimlValid = FALSE)
|
|
#define TBHasStrings(ptb) ((ptb)->nStrings || (ptb)->fNoStringPool)
|
|
|
|
LRESULT ToolbarDragCallback(HWND hwnd, UINT code, WPARAM wp, LPARAM lp)
|
|
{
|
|
PTBSTATE ptb = (PTBSTATE)GetWindowInt(hwnd, 0);
|
|
LRESULT lres;
|
|
|
|
switch (code)
|
|
{
|
|
case DPX_DRAGHIT:
|
|
if (lp)
|
|
{
|
|
POINT pt = {((POINTL *)lp)->x, ((POINTL *)lp)->y};
|
|
int item;
|
|
|
|
MapWindowPoints(NULL, ptb->ci.hwnd, &pt, 1);
|
|
item = TBHitTest(ptb, pt.x, pt.y);
|
|
|
|
lres = (LRESULT)((item >= 0)? ptb->Buttons[item].idCommand : -1);
|
|
}
|
|
else
|
|
lres = -1;
|
|
break;
|
|
|
|
case DPX_GETOBJECT:
|
|
lres = (LRESULT)GetItemObject(&ptb->ci, TBN_GETOBJECT, &IID_IDropTarget, (LPNMOBJECTNOTIFY)lp);
|
|
break;
|
|
|
|
case DPX_SELECT:
|
|
if ((int)wp >= 0)
|
|
{
|
|
SendMessage(ptb->ci.hwnd, TB_HIGHLIGHTBUTTON, wp,
|
|
MAKELPARAM((lp != DROPEFFECT_NONE), 0));
|
|
}
|
|
lres = 0;
|
|
break;
|
|
|
|
default:
|
|
lres = -1;
|
|
break;
|
|
}
|
|
|
|
return lres;
|
|
}
|
|
|
|
|
|
//#define HeightWithString(ptb, h) (h + ptb->dyIconFont + 1)
|
|
|
|
int HeightWithString(PTBSTATE ptb, int h)
|
|
{
|
|
if (ptb->ci.style & TBSTYLE_LIST)
|
|
return (max(h, ptb->dyIconFont));
|
|
else if (ptb->dyIconFont)
|
|
return (h + ptb->dyIconFont + 1);
|
|
else
|
|
return (h);
|
|
}
|
|
|
|
int TBWidthOfButton(PTBSTATE ptb, PTBBUTTON pButton)
|
|
{
|
|
if (pButton->fsStyle & TBSTYLE_SEP)
|
|
return pButton->iBitmap;
|
|
else if ((pButton->fsStyle & TBSTYLE_DROPDOWN) && !(ptb->ci.style & TBSTYLE_FLAT))
|
|
return ptb->iButWidth + (ptb->iButWidth /2);
|
|
else
|
|
return ptb->iButWidth;
|
|
}
|
|
|
|
BOOL NEAR PASCAL TBRecalc(PTBSTATE ptb)
|
|
{
|
|
TEXTMETRIC tm;
|
|
int i;
|
|
HDC hdc;
|
|
UINT uiStyle = 0;
|
|
int cxMax;
|
|
HFONT hOldFont;
|
|
|
|
ptb->dyIconFont = 0;
|
|
|
|
if (!TBHasStrings(ptb)) {
|
|
|
|
cxMax = ptb->iDxBitmap;
|
|
|
|
} else {
|
|
|
|
SIZE size;
|
|
LPCTSTR pstr;
|
|
RECT rcText = {0,0,0,0};
|
|
int cxExtra = XSLOP;
|
|
|
|
hdc = GetDC(ptb->ci.hwnd);
|
|
if (!hdc)
|
|
return(FALSE);
|
|
|
|
hOldFont = SelectObject(hdc, ptb->hfontIcon);
|
|
GetTextMetrics(hdc, &tm);
|
|
if (ptb->nTextRows)
|
|
ptb->dyIconFont = (tm.tmHeight * ptb->nTextRows) +
|
|
(tm.tmExternalLeading * (ptb->nTextRows - 1)); // add an edge ?
|
|
|
|
if (ptb->ci.style & TBSTYLE_LIST)
|
|
cxExtra += ptb->iDxBitmap + LIST_GAP;
|
|
|
|
cxMax = 0;
|
|
|
|
// walk strings to find max width
|
|
for (i = 0; i < ptb->iNumButtons; i++)
|
|
{
|
|
pstr = TB_StrForButton(ptb, &ptb->Buttons[i]);
|
|
if (pstr) {
|
|
GetTextExtentPoint(hdc, pstr, lstrlen(pstr), &size);
|
|
if (cxMax < size.cx)
|
|
cxMax = size.cx;
|
|
}
|
|
}
|
|
|
|
// if cxMax is less than the iButMinWidth - dxBitmap (if LIST) then
|
|
// cxMax = iButMinWidth
|
|
if (ptb->iButMinWidth && (ptb->iButMinWidth > (cxMax + cxExtra)))
|
|
cxMax = ptb->iButMinWidth - cxExtra;
|
|
|
|
// Is the cxMax + dxBitmap (if LIST) more than the max width ?
|
|
if (ptb->iButMaxWidth && (ptb->iButMaxWidth < (cxMax + cxExtra)))
|
|
{
|
|
int cyMax = 0;
|
|
|
|
cxMax = ptb->iButMaxWidth - cxExtra;
|
|
|
|
uiStyle = DT_CALCRECT;
|
|
if (ptb->nTextRows > 1)
|
|
uiStyle |= DT_WORDBREAK | DT_EDITCONTROL;
|
|
else
|
|
uiStyle |= DT_SINGLELINE;
|
|
|
|
// walk strings to set the TBSTATE_ELLIPSES
|
|
for (i = 0; i < ptb->iNumButtons; i++)
|
|
{
|
|
BOOL fEllipsed = FALSE;
|
|
|
|
pstr = TB_StrForButton(ptb, &ptb->Buttons[i]);
|
|
if (pstr) {
|
|
rcText.bottom = ptb->dyIconFont;
|
|
rcText.right = cxMax;
|
|
|
|
DrawText(hdc, pstr, lstrlen(pstr), &rcText, uiStyle);
|
|
if (ptb->nTextRows > 1)
|
|
fEllipsed = (BOOL)(rcText.bottom > ptb->dyIconFont);
|
|
else
|
|
fEllipsed = (BOOL)(rcText.right > cxMax);
|
|
|
|
if (cyMax < rcText.bottom)
|
|
cyMax = rcText.bottom;
|
|
}
|
|
|
|
if (fEllipsed)
|
|
ptb->Buttons[i].fsState |= TBSTATE_ELLIPSES;
|
|
else
|
|
ptb->Buttons[i].fsState &= ~TBSTATE_ELLIPSES;
|
|
}
|
|
|
|
// Set the text height to the tallest text, with the top end being the number
|
|
// of rows specified by MAXTEXTROWS
|
|
if (ptb->dyIconFont > cyMax)
|
|
ptb->dyIconFont = cyMax;
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < ptb->iNumButtons; i++)
|
|
ptb->Buttons[i].fsState &= ~TBSTATE_ELLIPSES;
|
|
|
|
if ((ptb->nTextRows) && (ptb->dyIconFont > size.cy))
|
|
ptb->dyIconFont = size.cy;
|
|
}
|
|
|
|
if (ptb->iButMinWidth && (ptb->iButMinWidth > (cxMax + cxExtra)))
|
|
cxMax = ptb->iButMinWidth - cxExtra;
|
|
|
|
if (hOldFont)
|
|
SelectObject(hdc, hOldFont);
|
|
ReleaseDC(ptb->ci.hwnd, hdc);
|
|
}
|
|
|
|
|
|
return(GrowToolbar(ptb, cxMax, HeightWithString(ptb, ptb->iDyBitmap), TRUE));
|
|
}
|
|
|
|
BOOL NEAR PASCAL TBChangeFont(PTBSTATE ptb, WPARAM wParam, HFONT hFont)
|
|
{
|
|
LOGFONT lf;
|
|
BOOL fWasFontCreated = ptb->fFontCreated;
|
|
|
|
if ((wParam != 0) && (wParam != SPI_SETICONTITLELOGFONT) && (wParam != SPI_SETNONCLIENTMETRICS))
|
|
return(FALSE);
|
|
|
|
if (!SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0))
|
|
return(FALSE);
|
|
|
|
if (!hFont) {
|
|
if (!(hFont = CreateFontIndirect(&lf)))
|
|
return(FALSE);
|
|
ptb->fFontCreated = TRUE;
|
|
}
|
|
|
|
if (ptb->hfontIcon && fWasFontCreated)
|
|
DeleteObject(ptb->hfontIcon);
|
|
|
|
ptb->hfontIcon = hFont;
|
|
|
|
return(TBRecalc(ptb));
|
|
}
|
|
|
|
void TBSetFont(PTBSTATE ptb, HFONT hFont, BOOL fInval)
|
|
{
|
|
TBChangeFont(ptb, 0, hFont);
|
|
if (fInval)
|
|
InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
|
|
|
|
}
|
|
|
|
HWND WINAPI CreateToolbarEx(HWND hwnd, DWORD ws, UINT wID, int nBitmaps,
|
|
HINSTANCE hBMInst, UINT wBMID, LPCTBBUTTON lpButtons,
|
|
int iNumButtons, int dxButton, int dyButton,
|
|
int dxBitmap, int dyBitmap, UINT uStructSize)
|
|
{
|
|
|
|
HWND hwndToolbar = CreateWindow(c_szToolbarClass, NULL, WS_CHILD | ws,
|
|
0, 0, 100, 30, hwnd, (HMENU)wID, HINST_THISDLL, NULL);
|
|
if (hwndToolbar)
|
|
{
|
|
PTBSTATE ptb = (PTBSTATE)GetWindowInt(hwndToolbar, 0);
|
|
TBOnButtonStructSize(ptb, uStructSize);
|
|
|
|
if ((dxBitmap && dyBitmap && !SetBitmapSize(ptb, dxBitmap, dyBitmap)) ||
|
|
(dxButton && dyButton && !SetBitmapSize(ptb,dxButton, dyButton)))
|
|
{
|
|
//!!!! do we actually need to deal with this?
|
|
DestroyWindow(hwndToolbar);
|
|
hwndToolbar = NULL;
|
|
goto Error;
|
|
}
|
|
|
|
AddBitmap(ptb, nBitmaps, hBMInst, wBMID);
|
|
InsertButtons(ptb, (UINT)-1, iNumButtons, (LPTBBUTTON)lpButtons, TRUE);
|
|
|
|
// ptb may be bogus now after above button insert
|
|
}
|
|
Error:
|
|
return hwndToolbar;
|
|
}
|
|
|
|
/* 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, UINT wID, int nBitmaps, HINSTANCE hBMInst, UINT wBMID, LPCTBBUTTON lpButtons, int iNumButtons)
|
|
{
|
|
// old-style toolbar, so no divider.
|
|
return CreateToolbarEx(hwnd, ws | CCS_NODIVIDER, wID, nBitmaps, hBMInst, wBMID,
|
|
lpButtons, iNumButtons, 0, 0, 0, 0, sizeof(OLDTBBUTTON));
|
|
}
|
|
|
|
#pragma code_seg(CODESEG_INIT)
|
|
|
|
BOOL FAR PASCAL InitToolbarClass(HINSTANCE hInstance)
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
if (!GetClassInfo(hInstance, c_szToolbarClass, &wc)) {
|
|
#ifndef WIN32
|
|
extern LRESULT CALLBACK _ToolbarWndProc(HWND, UINT, WPARAM, LPARAM);
|
|
wc.lpfnWndProc = _ToolbarWndProc;
|
|
#else
|
|
wc.lpfnWndProc = (WNDPROC)ToolbarWndProc;
|
|
#endif
|
|
|
|
wc.lpszClassName = c_szToolbarClass;
|
|
wc.style = CS_DBLCLKS | CS_GLOBALCLASS;
|
|
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;
|
|
}
|
|
#pragma code_seg()
|
|
|
|
#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);
|
|
}
|
|
|
|
// Parameter fHighlight determines whether to draw text highlighted, for
|
|
// new TBSTATE_HIGHLIGHTED
|
|
//
|
|
void NEAR PASCAL DrawString(PTBSTATE ptb, HDC hdc, int x, int y, int dx, int dy, PTSTR pszString,
|
|
BOOL fHighlight)
|
|
{
|
|
int oldMode;
|
|
COLORREF oldBkColor;
|
|
COLORREF oldTextColor;
|
|
int len;
|
|
RECT rcText;
|
|
UINT uiStyle = 0;
|
|
|
|
if (!(ptb->ci.style & TBSTYLE_LIST) && ((ptb->iDyBitmap + YSLOP + g_cyEdge) >= ptb->iButHeight))
|
|
// there's no room to show the text -- bail out
|
|
return;
|
|
|
|
if (fHighlight)
|
|
{
|
|
oldMode = SetBkMode (hdc, OPAQUE);
|
|
oldBkColor = SetBkColor (hdc, g_clrHighlight);
|
|
oldTextColor = SetTextColor (hdc, g_clrHighlightText);
|
|
}
|
|
else
|
|
oldMode = SetBkMode(hdc, TRANSPARENT);
|
|
|
|
len = lstrlen(pszString);
|
|
|
|
uiStyle = DT_END_ELLIPSIS;
|
|
|
|
|
|
if (ptb->nTextRows > 1)
|
|
uiStyle |= DT_WORDBREAK | DT_EDITCONTROL;
|
|
|
|
if (ptb->ci.style & TBSTYLE_LIST)
|
|
{
|
|
uiStyle |= DT_LEFT | DT_VCENTER | DT_SINGLELINE;
|
|
dy = max(ptb->dyIconFont, ptb->iDyBitmap);
|
|
}
|
|
else
|
|
{
|
|
uiStyle |= DT_CENTER;
|
|
|
|
if (!dy || ptb->dyIconFont < dy)
|
|
dy = ptb->dyIconFont;
|
|
}
|
|
|
|
SetRect( &rcText, x, y, x + dx, y + dy);
|
|
|
|
DrawTextEx(hdc, (LPTSTR)pszString, len, &rcText, uiStyle, NULL);
|
|
|
|
SetBkMode(hdc, oldMode);
|
|
if (fHighlight)
|
|
{
|
|
SetBkColor (hdc, oldBkColor);
|
|
SetTextColor (hdc, oldTextColor);
|
|
}
|
|
}
|
|
|
|
LPTSTR TB_StrForButton(PTBSTATE ptb, LPTBBUTTON pTBButton)
|
|
{
|
|
if (TBISSTRINGPTR(pTBButton->iString))
|
|
return (LPTSTR)pTBButton->iString;
|
|
else {
|
|
if (pTBButton->iString != -1 &&
|
|
pTBButton->iString < ptb->nStrings)
|
|
return ptb->pStrings[pTBButton->iString];
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// create a mono bitmap mask:
|
|
// 1's where color == COLOR_BTNFACE || COLOR_3DHILIGHT
|
|
// 0's everywhere else
|
|
|
|
void NEAR PASCAL CreateMask(PTBSTATE ptb, LPTBBUTTON pTBButton, int xoffset, int yoffset, int dx, int dy, BOOL fDrawGlyph)
|
|
{
|
|
IMAGELISTDRAWPARAMS imldp;
|
|
// initalize whole area with 1's
|
|
PatBlt(ptb->hdcMono, 0, 0, dx, dy, WHITENESS);
|
|
|
|
// create mask based on color bitmap
|
|
// convert this to 1's
|
|
|
|
if (fDrawGlyph)
|
|
{
|
|
imldp.cbSize = sizeof(imldp);
|
|
imldp.himl = ptb->himl;
|
|
imldp.i = pTBButton->iBitmap;
|
|
imldp.hdcDst = ptb->hdcMono;
|
|
imldp.x = xoffset;
|
|
imldp.y = yoffset;
|
|
imldp.cx = 0;
|
|
imldp.cy = 0;
|
|
imldp.xBitmap= 0;
|
|
imldp.yBitmap= 0;
|
|
imldp.rgbBk = g_clrBtnFace;
|
|
imldp.rgbFg = CLR_DEFAULT;
|
|
imldp.fStyle = ILD_ROP | ILD_MASK;
|
|
imldp.dwRop = SRCCOPY;
|
|
|
|
ImageList_DrawIndirect(&imldp);
|
|
|
|
imldp.fStyle = ILD_ROP | ILD_IMAGE;
|
|
imldp.rgbBk = g_clrBtnHighlight;
|
|
imldp.dwRop = SRCPAINT;
|
|
ImageList_DrawIndirect(&imldp);
|
|
}
|
|
|
|
if (pTBButton->iString != -1 && (pTBButton->iString < ptb->nStrings))
|
|
{
|
|
xoffset = 1;
|
|
|
|
if (ptb->ci.style & TBSTYLE_LIST)
|
|
{
|
|
xoffset += ptb->iDxBitmap + LIST_GAP;
|
|
dx -= ptb->iDxBitmap + LIST_GAP;
|
|
}
|
|
else {
|
|
yoffset += ptb->iDyBitmap + 1;
|
|
dy -= ptb->iDyBitmap + 1;
|
|
}
|
|
|
|
// The FALSE in 4th param is so we don't get a box in the mask.
|
|
DrawString(ptb, ptb->hdcMono, xoffset, yoffset, dx - g_cxEdge, dy - g_cyEdge, TB_StrForButton(ptb, pTBButton),
|
|
FALSE);
|
|
}
|
|
}
|
|
|
|
void FAR PASCAL DrawBlankButton(HDC hdc, int x, int y, int dx, int dy, UINT state)
|
|
{
|
|
RECT r1;
|
|
|
|
// face color
|
|
// The Office toolbar sends us bitmaps that are smaller than they claim they are
|
|
// So we need to do the PatB or the window background shows through around the
|
|
// edges of the button bitmap -jjk
|
|
if (!(state & TBSTATE_CHECKED))
|
|
PatB(hdc, x, y, dx, dy, g_clrBtnFace);
|
|
|
|
r1.left = x;
|
|
r1.top = y;
|
|
r1.right = x + dx;
|
|
r1.bottom = y + dy;
|
|
|
|
DrawEdge(hdc, &r1, (state & (TBSTATE_CHECKED | TBSTATE_PRESSED)) ? EDGE_SUNKEN : EDGE_RAISED, BF_RECT | BF_SOFT);
|
|
}
|
|
|
|
|
|
#define DSPDxax 0x00E20746
|
|
#define PSDPxax 0x00B8074A
|
|
|
|
void NEAR PASCAL DrawFace(PTBSTATE ptb, LPTBBUTTON ptButton, HDC hdc, int x, int y,
|
|
int offx, int offy, int dx, int dy, UINT state)
|
|
{
|
|
LPTSTR psz;
|
|
IMAGELISTDRAWPARAMS imldp;
|
|
BOOL fHotTrack = FALSE;
|
|
|
|
if (state & TBSTATE_ENABLED)
|
|
{
|
|
if ((ptb->ci.style & TBSTYLE_FLAT) && (&ptb->Buttons[ptb->nCurHTButton]==ptButton))
|
|
fHotTrack = TRUE;
|
|
|
|
// The following is in place to prevent hot tracking during the following conds:
|
|
// - drag & drop toolbar customization
|
|
// - when the mouse capture is on a particular button-press.
|
|
// This does _not_ drop out of the loop because we don't want to break update
|
|
// behavior; thus we'll have a little flickering on refresh as we pass over
|
|
// these buttons.
|
|
if (!(state & TBSTATE_PRESSED) && (GetKeyState (VK_LBUTTON) < 0))
|
|
fHotTrack = FALSE;
|
|
|
|
if (ptb->ci.style & TBSTYLE_FLAT)
|
|
{
|
|
UINT bdr = 0;
|
|
|
|
if (state & (TBSTATE_CHECKED | TBSTATE_PRESSED))
|
|
bdr = BDR_SUNKENOUTER;
|
|
else if (fHotTrack)
|
|
bdr = BDR_RAISEDINNER;
|
|
|
|
if (bdr)
|
|
{
|
|
RECT rect;
|
|
GetItemRect(ptb, ((DWORD) ptButton - (DWORD) ptb->Buttons) / sizeof(TBBUTTON), &rect);
|
|
DrawEdge(hdc, &rect, bdr, BF_RECT);
|
|
}
|
|
}
|
|
}
|
|
|
|
imldp.himl = NULL;
|
|
|
|
if (fHotTrack)
|
|
imldp.himl = ptb->himlHot ? ptb->himlHot : ptb->himl;
|
|
else if (!(state & TBSTATE_ENABLED) && ptb->himlDisabled)
|
|
imldp.himl = ptb->himlDisabled;
|
|
else if (ptb->himl)
|
|
imldp.himl = ptb->himl;
|
|
|
|
if (imldp.himl)
|
|
{
|
|
imldp.cbSize = sizeof(imldp);
|
|
imldp.i = ptButton->iBitmap;
|
|
imldp.hdcDst = hdc;
|
|
imldp.x = x + offx;
|
|
imldp.y = y + offy;
|
|
imldp.cx = 0;
|
|
imldp.cy = 0;
|
|
imldp.xBitmap= 0;
|
|
imldp.yBitmap= 0;
|
|
imldp.rgbBk = (ptb->ci.style & TBSTYLE_TRANSPARENT) ? CLR_NONE : g_clrBtnFace;
|
|
imldp.rgbFg = CLR_DEFAULT;
|
|
imldp.fStyle = ILD_NORMAL;
|
|
if (state & (TBSTATE_CHECKED | TBSTATE_INDETERMINATE))
|
|
imldp.fStyle = ILD_TRANSPARENT;
|
|
|
|
#ifdef TBHIGHLIGHT_GLYPH
|
|
if (state & TBSTATE_HIGHLIGHTED)
|
|
imldp.fStyle = ILD_TRANSPARENT | ILD_BLEND50;
|
|
#endif
|
|
ImageList_DrawIndirect(&imldp);
|
|
}
|
|
|
|
psz = TB_StrForButton(ptb, ptButton);
|
|
if (psz)
|
|
{
|
|
if (state & (TBSTATE_PRESSED | TBSTATE_CHECKED))
|
|
{
|
|
x++;
|
|
if (ptb->ci.style & TBSTYLE_LIST)
|
|
y++;
|
|
}
|
|
|
|
if (ptb->ci.style & TBSTYLE_LIST)
|
|
{
|
|
x += ptb->iDxBitmap + LIST_GAP;
|
|
dx -= ptb->iDxBitmap + LIST_GAP;
|
|
}
|
|
else {
|
|
|
|
y += offy + ptb->iDyBitmap;
|
|
dy -= offy + ptb->iDyBitmap;
|
|
}
|
|
|
|
DrawString(ptb, hdc, x + 1, y + 1, dx - g_cxEdge, dy - g_cyEdge,
|
|
psz,
|
|
(state & (TBSTATE_HIGHLIGHTED)) && (ptb->ci.style & TBSTYLE_LIST));
|
|
}
|
|
}
|
|
|
|
void FAR PASCAL DrawButton(HDC hdc, int x, int y, PTBSTATE ptb, LPTBBUTTON ptButton, BOOL fActive)
|
|
{
|
|
int yOffset;
|
|
HBRUSH hbrOld;
|
|
UINT state;
|
|
int dxFace, dyFace;
|
|
int xCenterOffset;
|
|
int dx = TBWidthOfButton(ptb, ptButton);
|
|
HFONT oldhFont;
|
|
int dy = ptb->iButHeight;
|
|
NMCUSTOMDRAW nmcd;
|
|
DWORD dwRet;
|
|
COLORREF clrSave = SetTextColor(hdc, g_clrBtnText);
|
|
|
|
state = (UINT)ptButton->fsState;
|
|
// make local copy of state and do proper overriding
|
|
if (state & TBSTATE_INDETERMINATE) {
|
|
if (state & TBSTATE_PRESSED)
|
|
state &= ~TBSTATE_INDETERMINATE;
|
|
else if (state & TBSTATE_ENABLED)
|
|
state = TBSTATE_INDETERMINATE;
|
|
else
|
|
state &= ~TBSTATE_INDETERMINATE;
|
|
}
|
|
|
|
if (!fActive) {
|
|
state &= ~TBSTATE_ENABLED;
|
|
}
|
|
|
|
oldhFont = SelectObject(hdc, ptb->hfontIcon);
|
|
nmcd.hdc = hdc;
|
|
nmcd.dwItemSpec = ptButton->idCommand;
|
|
nmcd.uItemState = 0;
|
|
|
|
if (state & TBSTATE_CHECKED)
|
|
nmcd.uItemState |= CDIS_CHECKED;
|
|
|
|
if (state & TBSTATE_PRESSED)
|
|
nmcd.uItemState |= CDIS_SELECTED;
|
|
|
|
if (!(state & TBSTATE_ENABLED))
|
|
nmcd.uItemState |= CDIS_DISABLED;
|
|
|
|
if ((ptb->ci.style & TBSTYLE_FLAT) && (&ptb->Buttons[ptb->nCurHTButton]==ptButton))
|
|
nmcd.uItemState |= CDIS_HOT;
|
|
|
|
nmcd.lItemlParam = 0;
|
|
dwRet = CICustomDrawNotify(&ptb->ci, CDDS_ITEMPREPAINT, &nmcd);
|
|
|
|
if (!(dwRet & CDRF_SKIPDEFAULT))
|
|
{
|
|
dxFace = ptb->iButWidth - (2 * g_cxEdge);// this the witdh of the face, not the entire button (dropdown case)
|
|
dyFace = dy - (2 * g_cyEdge);
|
|
|
|
if (!(ptb->ci.style & TBSTYLE_FLAT))
|
|
DrawBlankButton(hdc, x, y, dx, dy, state);
|
|
|
|
// move coordinates inside border and away from upper left highlight.
|
|
// the extents change accordingly.
|
|
x += g_cxEdge;
|
|
y += g_cyEdge;
|
|
|
|
// 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;
|
|
if (ptb->ci.style & TBSTYLE_LIST)
|
|
xCenterOffset = XSLOP / 2;
|
|
else
|
|
xCenterOffset = (dxFace - ptb->iDxBitmap)/2;
|
|
|
|
|
|
if (state & (TBSTATE_PRESSED | TBSTATE_CHECKED))
|
|
{
|
|
// pressed state moves down and to the right
|
|
xCenterOffset++;
|
|
yOffset++;
|
|
}
|
|
|
|
|
|
// draw the dithered background
|
|
if ((state & (TBSTATE_CHECKED | TBSTATE_INDETERMINATE)) || ((state & TBSTATE_HIGHLIGHTED)
|
|
&& !(ptb->ci.style & TBSTYLE_FLAT)))
|
|
{
|
|
hbrOld = SelectObject(hdc, g_hbrMonoDither);
|
|
if (hbrOld)
|
|
{
|
|
COLORREF clrText, clrBack;
|
|
#ifdef TBHIGHLIGHT_BACK
|
|
if (state & TBSTATE_HIGHLIGHTED)
|
|
clrText = SetTextColor(hdc, g_clrHighlight);
|
|
else
|
|
#endif
|
|
clrText = SetTextColor(hdc, g_clrBtnHighlight); // 0 -> 0
|
|
clrBack = SetBkColor(hdc, g_clrBtnFace); // 1 -> 1
|
|
|
|
// only draw the dither brush where the mask is 1's
|
|
PatBlt(hdc, x, y, dxFace, dyFace, PATCOPY);
|
|
|
|
SelectObject(hdc, hbrOld);
|
|
SetTextColor(hdc, clrText);
|
|
SetBkColor(hdc, clrBack);
|
|
}
|
|
}
|
|
|
|
// now put on the face
|
|
// TODO: Validate himlDisabled and ensure that the index is in range
|
|
if ((state & TBSTATE_ENABLED) || ptb->himlDisabled)
|
|
{
|
|
// regular version
|
|
DrawFace(ptb, ptButton, hdc, x, y, xCenterOffset, yOffset, dxFace, dyFace, state);
|
|
}
|
|
|
|
if (!(state & TBSTATE_ENABLED))
|
|
{
|
|
HBITMAP hbmOld;
|
|
|
|
//initialize the monochrome dc
|
|
if (!ptb->hdcMono) {
|
|
ptb->hdcMono = CreateCompatibleDC(hdc);
|
|
if (!ptb->hdcMono)
|
|
return;
|
|
SetTextColor(ptb->hdcMono, 0L);
|
|
SelectObject(ptb->hdcMono, ptb->hfontIcon);
|
|
}
|
|
|
|
hbmOld = SelectObject(ptb->hdcMono, ptb->hbmMono);
|
|
|
|
|
|
// disabled version (or indeterminate)
|
|
CreateMask(ptb, ptButton, xCenterOffset, yOffset, dxFace, dyFace, (ptb->himlDisabled == NULL));
|
|
|
|
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)) {
|
|
hbrOld = SelectObject(hdc, g_hbrBtnHighlight);
|
|
if (hbrOld) {
|
|
// draw hilight color where we have 0's in the mask
|
|
BitBlt(hdc, x + 1, y + 1, dxFace, dyFace, ptb->hdcMono, 0, 0, PSDPxax);
|
|
SelectObject(hdc, hbrOld);
|
|
}
|
|
}
|
|
|
|
// gray out glyph
|
|
hbrOld = SelectObject(hdc, g_hbrBtnShadow);
|
|
if (hbrOld) {
|
|
// draw the shadow color where we have 0's in the mask
|
|
BitBlt(hdc, x, y, dxFace, dyFace, ptb->hdcMono, 0, 0, PSDPxax);
|
|
SelectObject(hdc, hbrOld);
|
|
}
|
|
|
|
if (state & TBSTATE_CHECKED) {
|
|
BitBlt(ptb->hdcMono, 1, 1, dxFace - 1, dyFace - 1, ptb->hdcMono, 0, 0, SRCAND);
|
|
}
|
|
|
|
SelectObject(ptb->hdcMono, hbmOld);
|
|
}
|
|
|
|
if ((ptButton->fsStyle & TBSTYLE_DROPDOWN) && !(ptb->ci.style & TBSTYLE_FLAT))
|
|
{
|
|
RECT rc;
|
|
POINT pts[3];
|
|
int iHeight;
|
|
int iWidth;
|
|
HBRUSH hbr;
|
|
HPEN hpen;
|
|
|
|
rc.left = x - (2*g_cxEdge) + ptb->iButWidth + (yOffset-1);
|
|
rc.top = y + g_cyBorder;
|
|
rc.bottom = y + dy - (2 * g_cyEdge) - (2*g_cyBorder);
|
|
rc.right = rc.left - ptb->iButWidth + dx;
|
|
|
|
DrawEdge(hdc, &rc, EDGE_ETCHED, BF_LEFT);
|
|
|
|
rc.left += g_cxEdge;
|
|
rc.right -= g_cxEdge;
|
|
rc.top += yOffset;
|
|
iWidth = RECTWIDTH(rc);
|
|
iWidth -= g_cxEdge; // make it a little smaller than the rect
|
|
if (iWidth < 3) iWidth = 3;
|
|
iWidth &= (~1); // make it even
|
|
iHeight = (iWidth) / 2;
|
|
|
|
|
|
pts[0].y = pts[1].y = (RECTHEIGHT(rc) - iHeight + 1)/2 + rc.top; // +1 to bias lower
|
|
pts[2].y = pts[0].y + iHeight;
|
|
|
|
pts[0].x = (RECTWIDTH(rc) - iWidth + 1) / 2 + rc.left; // +1 to bias right
|
|
pts[1].x = pts[0].x + iWidth;
|
|
pts[2].x = pts[0].x + iWidth/2;
|
|
|
|
hbr = GetStockObject(BLACK_BRUSH);
|
|
hpen = GetStockObject(BLACK_PEN);
|
|
hbr = SelectObject(hdc, hbr);
|
|
hpen = SelectObject(hdc, hpen);
|
|
Polygon(hdc, pts, 3);
|
|
|
|
SelectObject(hdc, hbr);
|
|
SelectObject(hdc, hpen);
|
|
|
|
}
|
|
}
|
|
|
|
if (dwRet & CDRF_NOTIFYPOSTPAINT)
|
|
CICustomDrawNotify(&ptb->ci, CDDS_ITEMPOSTPAINT, &nmcd);
|
|
|
|
SetTextColor(hdc, clrSave);
|
|
if (oldhFont)
|
|
SelectObject(hdc, oldhFont);
|
|
}
|
|
|
|
// make sure that g_hbmMono is big enough to do masks for this
|
|
// size of button. if not, fail.
|
|
BOOL NEAR PASCAL CheckMonoMask(PTBSTATE ptb, int width, int height)
|
|
{
|
|
BITMAP bm;
|
|
HBITMAP hbmTemp;
|
|
|
|
if (ptb->hbmMono) {
|
|
GetObject(ptb->hbmMono, sizeof(BITMAP), &bm);
|
|
if (width <= bm.bmWidth && height <= bm.bmHeight) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
hbmTemp = CreateMonoBitmap(width, height);
|
|
if (!hbmTemp)
|
|
return FALSE;
|
|
|
|
if (ptb->hbmMono)
|
|
DeleteObject(ptb->hbmMono);
|
|
ptb->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.
|
|
*/
|
|
BOOL NEAR PASCAL GrowToolbar(PTBSTATE ptb, int newButWidth, int newButHeight, BOOL bInside)
|
|
{
|
|
if (!newButWidth)
|
|
newButWidth = DEFAULTBUTTONX;
|
|
if (!newButHeight)
|
|
newButHeight = DEFAULTBUTTONY;
|
|
|
|
// if growing based on inside measurement, get full size
|
|
if (bInside)
|
|
{
|
|
if (ptb->ci.style & TBSTYLE_LIST)
|
|
newButWidth += ptb->iDxBitmap + LIST_GAP;
|
|
|
|
newButHeight += YSLOP;
|
|
newButWidth += XSLOP;
|
|
|
|
// if toolbar already has strings, don't shrink width it because it
|
|
// might clip room for the string
|
|
if ((newButWidth < ptb->iButWidth) && ptb->nStrings)
|
|
newButWidth = ptb->iButWidth;
|
|
}
|
|
else {
|
|
if (newButHeight == -1)
|
|
newButHeight = ptb->iButHeight;
|
|
if (newButWidth == -1)
|
|
newButWidth = ptb->iButWidth;
|
|
|
|
if (newButHeight < ptb->iDyBitmap + YSLOP)
|
|
newButHeight = ptb->iDyBitmap + YSLOP;
|
|
if (newButWidth < ptb->iDxBitmap + XSLOP)
|
|
newButWidth = ptb->iDxBitmap + XSLOP;
|
|
}
|
|
|
|
// if the size of the toolbar is actually growing, see if shadow
|
|
// bitmaps can be made sufficiently large.
|
|
if (!ptb->hbmMono || (newButWidth > ptb->iButWidth) || (newButHeight > ptb->iButHeight)) {
|
|
if (!CheckMonoMask(ptb, newButWidth, newButHeight))
|
|
return(FALSE);
|
|
}
|
|
|
|
if (!bInside && (ptb->iButWidth != newButWidth) || (ptb->iButHeight != newButHeight))
|
|
InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
|
|
|
|
ptb->iButWidth = newButWidth;
|
|
ptb->iButHeight = newButHeight;
|
|
|
|
// bar height has 2 pixels above, 2 below
|
|
if (ptb->ci.style & TBSTYLE_TRANSPARENT)
|
|
ptb->iYPos = 0;
|
|
else
|
|
ptb->iYPos = 2;
|
|
|
|
FlushToolTipsMgr(ptb);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL NEAR PASCAL SetBitmapSize(PTBSTATE ptb, int width, int height)
|
|
{
|
|
int realh;
|
|
|
|
if (!width)
|
|
width = 1;
|
|
if (!height)
|
|
height = 1;
|
|
|
|
if (width == -1)
|
|
width = ptb->iDxBitmap;
|
|
|
|
if (height == -1)
|
|
height = ptb->iDyBitmap;
|
|
|
|
realh = height;
|
|
|
|
if ((ptb->iDxBitmap == width) && (ptb->iDyBitmap == height))
|
|
return TRUE;
|
|
|
|
if (TBHasStrings(ptb))
|
|
realh = HeightWithString(ptb, height);
|
|
|
|
if (GrowToolbar(ptb, width, realh, TRUE)) {
|
|
ptb->iDxBitmap = width;
|
|
ptb->iDyBitmap = height;
|
|
|
|
// the size changed, we need to rebuild the imagelist
|
|
TBInvalidateImageList(ptb);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void NEAR PASCAL TB_OnSysColorChange(PTBSTATE ptb)
|
|
{
|
|
|
|
InitGlobalColors();
|
|
// Reset all of the bitmaps
|
|
if (ptb->himl)
|
|
ImageList_SetBkColor(ptb->himl, (ptb->ci.style & TBSTYLE_TRANSPARENT) ? CLR_NONE : g_clrBtnFace);
|
|
if (ptb->himlHot)
|
|
ImageList_SetBkColor(ptb->himlHot, (ptb->ci.style & TBSTYLE_TRANSPARENT) ? CLR_NONE : g_clrBtnFace);
|
|
}
|
|
|
|
#define CACHE 0x01
|
|
#define BUILD 0x02
|
|
|
|
|
|
void PASCAL ReleaseMonoDC(PTBSTATE ptb)
|
|
{
|
|
if (ptb->hdcMono) {
|
|
SelectObject(ptb->hdcMono, g_hfontSystem);
|
|
DeleteDC(ptb->hdcMono);
|
|
ptb->hdcMono = NULL;
|
|
}
|
|
}
|
|
|
|
void TB_OnEraseBkgnd(PTBSTATE ptb, HDC hdc)
|
|
{
|
|
NMCUSTOMDRAW nmcd;
|
|
DWORD dwRes = FALSE;
|
|
|
|
nmcd.hdc = hdc;
|
|
nmcd.uItemState = 0;
|
|
nmcd.lItemlParam = 0;
|
|
|
|
if (ptb->ci.style & TBSTYLE_CUSTOMERASE) {
|
|
ptb->ci.dwCustom = CICustomDrawNotify(&ptb->ci, CDDS_PREERASE, &nmcd);
|
|
} else {
|
|
ptb->ci.dwCustom = CDRF_DODEFAULT;
|
|
}
|
|
|
|
if (!(ptb->ci.dwCustom & CDRF_SKIPDEFAULT))
|
|
{
|
|
// for transparent toolbars, forward erase background to parent
|
|
// but handle thru DefWindowProc in the event parent doesn't paint
|
|
if (!(ptb->ci.style & TBSTYLE_TRANSPARENT) ||
|
|
!CCForwardEraseBackground(ptb->ci.hwnd, hdc))
|
|
DefWindowProc(ptb->ci.hwnd, WM_ERASEBKGND, (WPARAM) hdc, 0);
|
|
}
|
|
|
|
if (ptb->ci.dwCustom & CDRF_NOTIFYPOSTERASE)
|
|
CICustomDrawNotify(&ptb->ci, CDDS_POSTERASE, &nmcd);
|
|
}
|
|
|
|
void NEAR PASCAL ToolbarPaint(PTBSTATE ptb, HDC hdcIn)
|
|
{
|
|
RECT rc;
|
|
HDC hdc;
|
|
PAINTSTRUCT ps;
|
|
int iButton, xButton, yButton, cxBar;
|
|
PTBBUTTON pAllButtons = ptb->Buttons;
|
|
NMCUSTOMDRAW nmcd;
|
|
|
|
GetClientRect(ptb->ci.hwnd, &rc);
|
|
|
|
cxBar = rc.right - rc.left;
|
|
|
|
if (hdcIn)
|
|
{
|
|
hdc = hdcIn;
|
|
}
|
|
else
|
|
hdc = BeginPaint(ptb->ci.hwnd, &ps);
|
|
|
|
if (!rc.right)
|
|
goto Error1;
|
|
|
|
nmcd.hdc = hdc;
|
|
nmcd.uItemState = 0;
|
|
nmcd.lItemlParam = 0;
|
|
ptb->ci.dwCustom = CICustomDrawNotify(&ptb->ci, CDDS_PREPAINT, &nmcd);
|
|
|
|
if (!(ptb->ci.dwCustom & CDRF_SKIPDEFAULT))
|
|
{
|
|
if (!ptb->fHimlValid)
|
|
TBBuildImageList(ptb);
|
|
|
|
yButton = ptb->iYPos;
|
|
rc.top = ptb->iYPos;
|
|
rc.bottom = ptb->iYPos + ptb->iButHeight;
|
|
|
|
|
|
for (iButton = 0, xButton = ptb->xFirstButton;
|
|
iButton < ptb->iNumButtons; iButton++)
|
|
{
|
|
PTBBUTTON pButton = &pAllButtons[iButton];
|
|
|
|
if (!(pButton->fsState & TBSTATE_HIDDEN))
|
|
{
|
|
int cxButton = TBWidthOfButton(ptb, pButton);
|
|
|
|
if (!(pButton->fsStyle & TBSTYLE_SEP) || (ptb->ci.style & TBSTYLE_FLAT))
|
|
{
|
|
// is there anything to draw?
|
|
rc.left = xButton;
|
|
rc.right = xButton + cxButton;
|
|
|
|
if (RectVisible(hdc, &rc))
|
|
{
|
|
if (pButton->fsStyle & TBSTYLE_SEP)
|
|
{
|
|
// must be a flat separator
|
|
if (ptb->ci.style & CCS_VERT)
|
|
{
|
|
int iSave = rc.top;
|
|
rc.top += ((rc.bottom - rc.top) - 1) / 2;
|
|
rc.top = iSave;
|
|
InflateRect(&rc, -g_cxEdge, 0);
|
|
DrawEdge(hdc, &rc, EDGE_ETCHED, BF_TOP);
|
|
InflateRect(&rc, g_cxEdge, 0);
|
|
}
|
|
else
|
|
{
|
|
rc.left += (cxButton - 1) / 2;
|
|
InflateRect(&rc, 0, -g_cyEdge);
|
|
DrawEdge(hdc, &rc, EDGE_ETCHED, BF_LEFT);
|
|
InflateRect(&rc, 0, g_cyEdge);
|
|
}
|
|
}
|
|
else
|
|
DrawButton(hdc, xButton, yButton, ptb, pButton, ptb->fActive);
|
|
}
|
|
}
|
|
|
|
xButton += (cxButton - s_dxOverlap);
|
|
|
|
if (pButton->fsState & TBSTATE_WRAP)
|
|
{
|
|
int dy;
|
|
|
|
if (pButton->fsStyle & TBSTYLE_SEP)
|
|
{
|
|
if (ptb->ci.style & TBSTYLE_FLAT)
|
|
{
|
|
RECT rcMid;
|
|
|
|
rcMid.top = rc.top + ptb->iButHeight + ((pButton->iBitmap - 1) / 2);
|
|
rcMid.bottom = rcMid.top + g_cxEdge;
|
|
rcMid.left = g_cxEdge;
|
|
rcMid.right = cxBar - g_cxEdge;
|
|
DrawEdge(hdc, &rcMid, EDGE_ETCHED, BF_TOP);
|
|
dy = ptb->iButHeight + pButton->iBitmap;
|
|
}
|
|
else
|
|
dy = ptb->iButHeight + pButton->iBitmap * 2 / 3;
|
|
}
|
|
else
|
|
dy = ptb->iButHeight;
|
|
|
|
xButton = ptb->xFirstButton;
|
|
yButton += dy;
|
|
rc.top += dy;
|
|
rc.bottom += dy;
|
|
}
|
|
}
|
|
}
|
|
ReleaseMonoDC(ptb);
|
|
}
|
|
|
|
if (ptb->ci.dwCustom & CDRF_NOTIFYPOSTPAINT)
|
|
{
|
|
nmcd.hdc = hdc;
|
|
nmcd.uItemState = 0;
|
|
nmcd.lItemlParam = 0;
|
|
CICustomDrawNotify(&ptb->ci, CDDS_POSTPAINT, &nmcd);
|
|
}
|
|
|
|
Error1:
|
|
if (hdcIn == NULL)
|
|
EndPaint(ptb->ci.hwnd, &ps);
|
|
|
|
}
|
|
|
|
|
|
BOOL NEAR PASCAL GetItemRect(PTBSTATE ptb, UINT uButton, LPRECT lpRect)
|
|
{
|
|
UINT iButton, xPos, yPos;
|
|
PTBBUTTON pButton;
|
|
|
|
if (uButton >= (UINT)ptb->iNumButtons
|
|
|| (ptb->Buttons[uButton].fsState & TBSTATE_HIDDEN))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
xPos = ptb->xFirstButton;
|
|
yPos = ptb->iYPos;
|
|
|
|
for (iButton = 0, pButton = ptb->Buttons; iButton < uButton; iButton++, pButton++)
|
|
{
|
|
if (!(pButton->fsState & TBSTATE_HIDDEN))
|
|
{
|
|
xPos += TBWidthOfButton(ptb, pButton) - s_dxOverlap;
|
|
|
|
if (pButton->fsState & TBSTATE_WRAP)
|
|
{
|
|
yPos += ptb->iButHeight;
|
|
if (pButton->fsStyle & TBSTYLE_SEP)
|
|
{
|
|
if (ptb->ci.style & TBSTYLE_FLAT)
|
|
yPos += pButton->iBitmap;
|
|
else
|
|
yPos += pButton->iBitmap * 2 / 3;
|
|
}
|
|
xPos = ptb->xFirstButton;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 + TBWidthOfButton(ptb, pButton);
|
|
lpRect->top = yPos;
|
|
lpRect->bottom = yPos + ptb->iButHeight;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void NEAR PASCAL InvalidateButton(PTBSTATE ptb, PTBBUTTON pButtonToPaint, BOOL fErase)
|
|
{
|
|
RECT rc;
|
|
|
|
if (GetItemRect(ptb, pButtonToPaint-ptb->Buttons, &rc))
|
|
{
|
|
InvalidateRect(ptb->ci.hwnd, &rc, fErase);
|
|
}
|
|
}
|
|
|
|
// do hit testing by sliding the origin of the supplied point
|
|
//
|
|
// returns:
|
|
// >= 0 index of non sperator item hit
|
|
// < 0 index of seperator or nearest non seperator item (area just below and to the left)
|
|
//
|
|
// +--------------------------------------
|
|
// | -1 -1 -1 -1
|
|
// | btn sep btn
|
|
// | +-----+ +-----+
|
|
// | | | | |
|
|
// | -1 | 0 | -1 | 2 | -3
|
|
// | | | | |
|
|
// | +-----+ +-----+
|
|
// |
|
|
// | -1 -1 -1 -2 -3
|
|
//
|
|
|
|
int FAR PASCAL TBHitTest(PTBSTATE ptb, int xPos, int yPos)
|
|
{
|
|
int prev = 0;
|
|
int last = 0;
|
|
int i;
|
|
RECT rc;
|
|
|
|
if (ptb->iNumButtons == 0)
|
|
return(-1);
|
|
|
|
for (i=0; i<ptb->iNumButtons; i++)
|
|
{
|
|
// BUGBUG.. this makes it n**2
|
|
if (GetItemRect(ptb, i, &rc))
|
|
{
|
|
if (yPos >= rc.top && yPos <= rc.bottom)
|
|
{
|
|
if (xPos >= rc.left && xPos <= rc.right)
|
|
{
|
|
if (ptb->Buttons[i].fsStyle & TBSTYLE_SEP)
|
|
return - i - 1;
|
|
else
|
|
return i;
|
|
}
|
|
else
|
|
{
|
|
prev = i + 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
last = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (prev)
|
|
return -1 - prev;
|
|
else if (yPos > rc.bottom)
|
|
// this means that we are off the bottom of the toolbar
|
|
return(- i - 1);
|
|
|
|
return last + 1;
|
|
}
|
|
|
|
int NEAR PASCAL CountRows(PTBSTATE ptb)
|
|
{
|
|
PTBBUTTON pButton, pBtnLast;
|
|
int rows = 1;
|
|
|
|
pBtnLast = &(ptb->Buttons[ptb->iNumButtons]);
|
|
for (pButton = ptb->Buttons; pButton<pBtnLast; pButton++) {
|
|
if (pButton->fsState & TBSTATE_WRAP) {
|
|
rows++;
|
|
if (pButton->fsStyle & TBSTYLE_SEP)
|
|
rows++;
|
|
}
|
|
}
|
|
|
|
return rows;
|
|
}
|
|
|
|
|
|
/**** WrapToolbar:
|
|
* The buttons in the toolbar is layed out from left to right,
|
|
* top to bottom. If adding another button to the current row,
|
|
* while computing the layout, would cause that button to extend
|
|
* beyond the right edge or the client area, then locate a break-
|
|
* point (marked with the TBSTATE_WRAP flag). A break-point is:
|
|
*
|
|
* a) The right-most separator on the current row.
|
|
*
|
|
* b) The right-most button if there is no separator on the current row.
|
|
*
|
|
* A new row is also started at the end of any button group (sequence
|
|
* of buttons that are dlimited by separators) that are taller than
|
|
* or equal to two rows.
|
|
*/
|
|
|
|
void NEAR PASCAL WrapToolbar(PTBSTATE ptb, int dx, LPRECT lpRect, int FAR *pRows)
|
|
{
|
|
BOOL fInvalidate = FALSE;
|
|
PTBBUTTON pButton, pBtnT, pBtnLast;
|
|
int xPos, yPos, xMax;
|
|
BOOL bFoundIt;
|
|
BOOL bNextBreak = FALSE;
|
|
|
|
xMax = ptb->iButWidth;
|
|
xPos = ptb->xFirstButton;
|
|
yPos = ptb->iYPos;
|
|
pBtnLast = &(ptb->Buttons[ptb->iNumButtons]);
|
|
|
|
|
|
if (pRows)
|
|
(*pRows)=1;
|
|
|
|
for (pButton = ptb->Buttons; pButton<pBtnLast; pButton++)
|
|
{
|
|
pButton->fsState &= ~TBSTATE_WRAP;
|
|
|
|
if (!(pButton->fsState & TBSTATE_HIDDEN))
|
|
{
|
|
xPos += TBWidthOfButton(ptb, pButton) - s_dxOverlap;
|
|
|
|
// The current row exceeds the right edge. Wrap it.
|
|
if (!(pButton->fsStyle&TBSTYLE_SEP) && (xPos > dx)) {
|
|
|
|
for (pBtnT=pButton, bFoundIt = FALSE;
|
|
pBtnT>ptb->Buttons && !(pBtnT->fsState & TBSTATE_WRAP);
|
|
pBtnT--)
|
|
{
|
|
if ((pBtnT->fsStyle & TBSTYLE_SEP) &&
|
|
!(pBtnT->fsState & TBSTATE_HIDDEN))
|
|
{
|
|
fInvalidate = TRUE;
|
|
pBtnT->fsState |= TBSTATE_WRAP;
|
|
xPos = ptb->xFirstButton;
|
|
if (ptb->ci.style & TBSTYLE_FLAT)
|
|
yPos += pBtnT->iBitmap + ptb->iButHeight;
|
|
else
|
|
yPos += pBtnT->iBitmap * 2 / 3 + ptb->iButHeight;
|
|
bFoundIt = TRUE;
|
|
pButton = pBtnT;
|
|
bNextBreak = FALSE;
|
|
if (pRows)
|
|
(*pRows)++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Did we find a separator? Force a wrap anyway!
|
|
if (bFoundIt==FALSE)
|
|
{
|
|
pBtnT = pButton;
|
|
if (pButton!=ptb->Buttons) {
|
|
/* Back-up to first non-hidden button. */
|
|
do {
|
|
pBtnT--;
|
|
} while ((pBtnT>ptb->Buttons) &&
|
|
(pBtnT->fsState & TBSTATE_HIDDEN));
|
|
|
|
/* Is it already wrapped? */
|
|
if (pBtnT->fsState & TBSTATE_WRAP)
|
|
pBtnT = pButton;
|
|
}
|
|
|
|
fInvalidate = TRUE;
|
|
pBtnT->fsState |= TBSTATE_WRAP;
|
|
xPos = ptb->xFirstButton;
|
|
yPos += ptb->iButHeight;
|
|
pButton = pBtnT;
|
|
bNextBreak = TRUE;
|
|
}
|
|
|
|
// Count another row.
|
|
if (pRows)
|
|
(*pRows)++;
|
|
}
|
|
else
|
|
{
|
|
UINT fOldState = pButton->fsState;
|
|
|
|
pButton->fsState &= ~TBSTATE_WRAP;
|
|
if ((pButton->fsStyle&TBSTYLE_SEP) && (bNextBreak))
|
|
{
|
|
bNextBreak = FALSE;
|
|
pButton->fsState |= TBSTATE_WRAP;
|
|
xPos = ptb->xFirstButton;
|
|
if (ptb->ci.style & TBSTYLE_FLAT)
|
|
yPos += ptb->iButHeight + pButton->iBitmap;
|
|
else
|
|
yPos += ptb->iButHeight + pButton->iBitmap * 2 / 3 ;
|
|
if (pRows)
|
|
(*pRows)+=2;
|
|
}
|
|
if (fOldState != pButton->fsState) {
|
|
fInvalidate = TRUE;
|
|
}
|
|
}
|
|
if (!(pButton->fsStyle&TBSTYLE_SEP))
|
|
xMax = max(xPos, xMax);
|
|
}
|
|
}
|
|
|
|
if (lpRect)
|
|
{
|
|
lpRect->left = 0;
|
|
lpRect->right = xMax;
|
|
lpRect->top = 0;
|
|
lpRect->bottom = yPos + ptb->iYPos + ptb->iButHeight;
|
|
}
|
|
|
|
if (fInvalidate)
|
|
InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
|
|
}
|
|
|
|
|
|
|
|
BOOL NEAR PASCAL BoxIt(PTBSTATE ptb, int height, BOOL fLarger, LPRECT lpRect)
|
|
{
|
|
int dx, bwidth;
|
|
int rows, prevRows, prevWidth;
|
|
RECT rcCur;
|
|
|
|
if (height<1)
|
|
height = 1;
|
|
|
|
rows = CountRows(ptb);
|
|
if (height==rows || ptb->iNumButtons==0)
|
|
{
|
|
GetClientRect(ptb->ci.hwnd, lpRect);
|
|
return FALSE;
|
|
}
|
|
|
|
bwidth = ptb->iButWidth-s_dxOverlap;
|
|
prevRows = ptb->iNumButtons+1;
|
|
prevWidth = bwidth;
|
|
for (rows=height+1, dx = bwidth; rows>height;dx+=bwidth/4)
|
|
{
|
|
WrapToolbar(ptb, dx, &rcCur, &rows);
|
|
if (rows<prevRows && rows>height)
|
|
{
|
|
prevWidth = dx;
|
|
prevRows = rows;
|
|
}
|
|
}
|
|
|
|
if (rows<height && fLarger)
|
|
{
|
|
WrapToolbar(ptb, prevWidth, &rcCur, NULL);
|
|
}
|
|
|
|
if (lpRect)
|
|
*lpRect = rcCur;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int FAR PASCAL PositionFromID(PTBSTATE ptb, int id)
|
|
{
|
|
int i;
|
|
|
|
// Handle case where this is sent at the wrong time..
|
|
if (ptb == NULL)
|
|
return -1;
|
|
|
|
// note, we don't skip seperators, so you better not have conflicting
|
|
// cmd ids and seperator ids.
|
|
for (i = 0; i < ptb->iNumButtons; i++)
|
|
if (ptb->Buttons[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(PTBSTATE ptb, int idCommand)
|
|
{
|
|
int i, iFirst, iLast, iButton;
|
|
int cButtons = ptb->iNumButtons;
|
|
PTBBUTTON pAllButtons = ptb->Buttons;
|
|
|
|
iButton = PositionFromID(ptb, 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(ptb, &pAllButtons[i], TRUE);
|
|
break; // only one button is down right?
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void NEAR PASCAL DestroyStrings(PTBSTATE ptb)
|
|
{
|
|
PTSTR *p;
|
|
PTSTR end = 0, start = 0;
|
|
int i;
|
|
|
|
p = ptb->pStrings;
|
|
for (i = 0; i < ptb->nStrings; i++) {
|
|
if (!((*p < end) && (*p > start))) {
|
|
start = (*p);
|
|
end = start + (LocalSize((HANDLE)*p) / sizeof(TCHAR));
|
|
LocalFree((HANDLE)*p);
|
|
}
|
|
p++;
|
|
}
|
|
|
|
LocalFree((HANDLE)ptb->pStrings);
|
|
}
|
|
|
|
#define MAXSTRINGSIZE 1024
|
|
int NEAR PASCAL AddStrings(PTBSTATE ptb, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
int i = 0,j = 0, cxMax = 0;
|
|
LPTSTR lpsz;
|
|
PTSTR pString, psz;
|
|
int numstr;
|
|
PTSTR *pFoo;
|
|
PTSTR *pOffset;
|
|
TCHAR cSeparator;
|
|
int len;
|
|
|
|
// read the string as a resource
|
|
if (wParam != 0) {
|
|
pString = (PTSTR)LocalAlloc(LPTR, (MAXSTRINGSIZE * sizeof (TCHAR)));
|
|
if (!pString)
|
|
return -1;
|
|
i = LoadString((HINSTANCE)wParam, LOWORD(lParam), (LPTSTR)pString, MAXSTRINGSIZE);
|
|
if (!i) {
|
|
LocalFree(pString);
|
|
return -1;
|
|
}
|
|
// realloc string buffer to actual needed size
|
|
LocalReAlloc(pString, (i+1) * sizeof (TCHAR), LMEM_MOVEABLE);
|
|
|
|
// convert separators to '\0' and count number of strings
|
|
cSeparator = *pString;
|
|
#if !defined(UNICODE) // && defined(DBCS)
|
|
for (numstr = 0, psz = pString + 1, i--; i; i--, psz++ ) {
|
|
if (*psz == cSeparator) {
|
|
if (i != 1) // We don't want to count the second terminator as another string
|
|
numstr++;
|
|
|
|
*psz = 0; // terminate with 0
|
|
}
|
|
// extra i-- if DBCS
|
|
if (IsDBCSLeadByte(*psz))
|
|
{
|
|
*(WORD *)(psz-1) = *(WORD *)psz;
|
|
psz++;
|
|
i--;
|
|
}
|
|
else
|
|
{
|
|
// shift string to the left to overwrite separator identifier
|
|
*(psz - 1) = *psz;
|
|
}
|
|
}
|
|
#else
|
|
for (numstr = 0, psz = pString + 1, i--; i; i--, psz++) {
|
|
if (*psz == cSeparator) {
|
|
if (i != 1) // We don't want to count the second terminator as another string
|
|
numstr++;
|
|
|
|
*psz = 0; // terminate with 0
|
|
}
|
|
// shift string to the left to overwrite separator identifier
|
|
*(psz - 1) = *psz;
|
|
}
|
|
#endif
|
|
}
|
|
// read explicit string. copy it into local memory, too.
|
|
else {
|
|
// find total length and number of strings
|
|
for (i = 0, numstr = 0, lpsz = (LPTSTR)lParam;;) {
|
|
i++;
|
|
if (*lpsz == 0) {
|
|
numstr++;
|
|
if (*(lpsz + 1) == 0)
|
|
break;
|
|
}
|
|
lpsz++;
|
|
}
|
|
pString = (PTSTR)LocalAlloc(LPTR, (i * sizeof (TCHAR)));
|
|
if (!pString)
|
|
return -1;
|
|
hmemcpy(pString, (void FAR *)lParam, i * sizeof(TCHAR));
|
|
}
|
|
|
|
// make room for increased string pointer table
|
|
pFoo = (PTSTR *)CCLocalReAlloc(ptb->pStrings,
|
|
(ptb->nStrings + numstr) * sizeof(PTSTR));
|
|
if (!pFoo) {
|
|
goto Failure;
|
|
}
|
|
|
|
ptb->pStrings = pFoo;
|
|
// pointer to next open slot in string index table.
|
|
pOffset = ptb->pStrings + ptb->nStrings;
|
|
|
|
for (i = 0; i < numstr; i++, pOffset++)
|
|
{
|
|
*pOffset = pString;
|
|
len = lstrlen(pString);
|
|
pString += len + 1;
|
|
}
|
|
// is the world big enough to handle the larger buttons?
|
|
i = ptb->nStrings;
|
|
ptb->nStrings += numstr;
|
|
if (!TBRecalc(ptb))
|
|
{
|
|
ptb->nStrings -= numstr;
|
|
// back out changes.
|
|
ptb->pStrings = (PTSTR *)CCLocalReAlloc(ptb->pStrings,
|
|
ptb->nStrings * sizeof(PTSTR));
|
|
|
|
Failure:
|
|
LocalFree(pString);
|
|
return -1;
|
|
}
|
|
|
|
return i; // index of first added string
|
|
}
|
|
|
|
#ifdef WIN32
|
|
void NEAR PASCAL MapToStandardBitmaps(HINSTANCE FAR *phinst, UINT FAR * pidBM, int FAR *pnButtons)
|
|
{
|
|
if (*phinst == HINST_COMMCTRL) {
|
|
*phinst = g_hinst;
|
|
|
|
// low 2 bits are coded M(mono == ~color) L(large == ~small)
|
|
// 0 0 -> color small
|
|
// 0 1 -> color large
|
|
// ...
|
|
// 1 1 -> mono large
|
|
|
|
switch (*pidBM)
|
|
{
|
|
case IDB_STD_SMALL_COLOR:
|
|
case IDB_STD_LARGE_COLOR:
|
|
case IDB_STD_SMALL_MONO:
|
|
case IDB_STD_LARGE_MONO:
|
|
*pidBM = IDB_STDTB_SMALL_COLOR + (*pidBM & 1);
|
|
*pnButtons = STD_PRINT + 1;
|
|
break;
|
|
|
|
case IDB_HIST_SMALL_COLOR:
|
|
case IDB_HIST_LARGE_COLOR:
|
|
//case IDB_HIST_SMALL_MONO:
|
|
//case IDB_HIST_LARGE_MONO:
|
|
*pidBM = IDB_HISTTB_SMALL_COLOR + (*pidBM & 1);
|
|
*pnButtons = HIST_LAST + 1;
|
|
break;
|
|
|
|
case IDB_VIEW_SMALL_COLOR:
|
|
case IDB_VIEW_LARGE_COLOR:
|
|
case IDB_VIEW_SMALL_MONO:
|
|
case IDB_VIEW_LARGE_MONO:
|
|
*pidBM = IDB_VIEWTB_SMALL_COLOR + (*pidBM & 1);
|
|
*pnButtons = VIEW_NEWFOLDER + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
HBITMAP _CopyBitmap(PTBSTATE ptb, HBITMAP hbm, int cx, int cy)
|
|
{
|
|
HBITMAP hbmCopy;
|
|
RECT rc = {0 ,0, cx, cy};
|
|
HDC hdcWin;
|
|
HDC hdcSrc, hdcDest;
|
|
|
|
hbmCopy = CreateColorBitmap(cx, cy);
|
|
|
|
hdcWin = GetDC(ptb->ci.hwnd);
|
|
hdcSrc = CreateCompatibleDC(hdcWin);
|
|
hdcDest = CreateCompatibleDC(hdcWin);
|
|
|
|
if (hdcWin && hdcSrc && hdcDest) {
|
|
|
|
SelectObject(hdcSrc, hbm);
|
|
SelectObject(hdcDest, hbmCopy);
|
|
|
|
// fill the background
|
|
PatB(hdcDest, 0, 0, cx, cy, g_clrBtnFace);
|
|
|
|
BitBlt(hdcDest, 0, 0, cx, cy,
|
|
hdcSrc, 0, 0, SRCCOPY);
|
|
|
|
}
|
|
|
|
if (hdcWin)
|
|
ReleaseDC(ptb->ci.hwnd, hdcWin);
|
|
|
|
if (hdcSrc)
|
|
DeleteDC(hdcSrc);
|
|
if (hdcDest)
|
|
DeleteDC(hdcDest);
|
|
return hbmCopy;
|
|
}
|
|
|
|
|
|
BOOL TBAddBitmapToImageList(PTBSTATE ptb, PTBBMINFO pTemp)
|
|
{
|
|
HBITMAP hbm = NULL, hbmTemp = NULL;
|
|
|
|
if (!ptb->himl) {
|
|
ptb->himl = ImageList_Create(ptb->iDxBitmap, ptb->iDyBitmap, ILC_MASK | ILC_COLORDDB, 4, 4);
|
|
if (!ptb->himl)
|
|
return(FALSE);
|
|
ImageList_SetBkColor(ptb->himl, (ptb->ci.style & TBSTYLE_TRANSPARENT) ? CLR_NONE : g_clrBtnFace);
|
|
}
|
|
|
|
|
|
if (pTemp->hInst) {
|
|
hbm = hbmTemp = CreateMappedBitmap(pTemp->hInst, pTemp->wID, 0, NULL, 0);
|
|
|
|
} else if (pTemp->wID) {
|
|
hbm = (HBITMAP)pTemp->wID;
|
|
}
|
|
|
|
if (hbm) {
|
|
|
|
//
|
|
// Fix up bitmaps that aren't iDxBitmap x iDyBitmap
|
|
//
|
|
|
|
BITMAP bm;
|
|
|
|
GetObject( hbm, sizeof(bm), &bm);
|
|
|
|
if (bm.bmWidth < ptb->iDxBitmap) {
|
|
bm.bmWidth = ptb->iDxBitmap;
|
|
}
|
|
|
|
if (bm.bmHeight < ptb->iDyBitmap) {
|
|
bm.bmHeight = ptb->iDyBitmap;
|
|
}
|
|
|
|
// Handle case when the bitmaps is smaller or larger than what it is supposed to be
|
|
// ptb->iDxBitmaps specifies the width a bitmap should be.
|
|
// The error cases we are catching are:
|
|
// If the pTemp->nButtons is 0 then we assume there is one button
|
|
// If the pTemp->nButtons specifies a number but the width of the bitmap is not right,
|
|
// then we assume that nButtons is right and change the width of the bitmap
|
|
if (!pTemp->nButtons)
|
|
bm.bmWidth = ptb->iDxBitmap;
|
|
else
|
|
bm.bmWidth = ptb->iDxBitmap * pTemp->nButtons;
|
|
|
|
hbm = (HBITMAP)_CopyBitmap(ptb, hbm, bm.bmWidth, bm.bmHeight);
|
|
|
|
|
|
}
|
|
|
|
// AddMasked parties on the bitmap, so we want to use a local copy
|
|
if (hbm) {
|
|
ImageList_AddMasked(ptb->himl, hbm, g_clrBtnFace);
|
|
|
|
DeleteObject(hbm);
|
|
}
|
|
|
|
if (hbmTemp) {
|
|
DeleteObject(hbmTemp);
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
void NEAR PASCAL TBBuildImageList(PTBSTATE ptb)
|
|
{
|
|
int i;
|
|
PTBBMINFO pTemp;
|
|
|
|
ptb->fHimlValid = TRUE;
|
|
|
|
// is the parent dealing natively with imagelists? if so,
|
|
// don't do this back compat building
|
|
if (ptb->fHimlNative)
|
|
return;
|
|
|
|
if (ptb->himl) {
|
|
ImageList_Destroy(ptb->himl);
|
|
ptb->himl = NULL;
|
|
}
|
|
|
|
for (i = 0, pTemp = ptb->pBitmaps; i < ptb->nBitmaps; i++, pTemp++) {
|
|
|
|
TBAddBitmapToImageList(ptb, pTemp);
|
|
}
|
|
|
|
}
|
|
|
|
/* 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.
|
|
*/
|
|
int NEAR PASCAL AddBitmap(PTBSTATE ptb, int nButtons, HINSTANCE hBMInst, UINT idBM)
|
|
{
|
|
PTBBMINFO pTemp;
|
|
int nBM, nIndex;
|
|
|
|
// map things to the standard toolbar images
|
|
#ifdef WIN32
|
|
if (hBMInst == HINST_COMMCTRL) // -1
|
|
{
|
|
// set the proper dimensions...
|
|
if (idBM & 1)
|
|
SetBitmapSize(ptb, LARGE_DXYBITMAP, LARGE_DXYBITMAP);
|
|
else
|
|
SetBitmapSize(ptb, SMALL_DXYBITMAP, SMALL_DXYBITMAP);
|
|
|
|
MapToStandardBitmaps(&hBMInst, &idBM, &nButtons);
|
|
}
|
|
#endif
|
|
|
|
if (ptb->pBitmaps)
|
|
{
|
|
/* Check if the bitmap has already been added
|
|
*/
|
|
for (nBM=ptb->nBitmaps, pTemp=ptb->pBitmaps, nIndex=0;
|
|
nBM>0; --nBM, ++pTemp)
|
|
{
|
|
if (pTemp->hInst==hBMInst && pTemp->wID==idBM)
|
|
{
|
|
/* 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)CCLocalReAlloc(ptb->pBitmaps,
|
|
(ptb->nBitmaps + 1)*sizeof(TBBMINFO));
|
|
if (!pTemp)
|
|
return(-1);
|
|
ptb->pBitmaps = pTemp;
|
|
|
|
pTemp = ptb->pBitmaps + ptb->nBitmaps;
|
|
|
|
pTemp->hInst = hBMInst;
|
|
pTemp->wID = idBM;
|
|
pTemp->nButtons = nButtons;
|
|
|
|
if (!TBAddBitmapToImageList(ptb, pTemp))
|
|
return(-1);
|
|
|
|
++ptb->nBitmaps;
|
|
|
|
for (nButtons=0, --pTemp; pTemp>=ptb->pBitmaps; --pTemp)
|
|
nButtons += pTemp->nButtons;
|
|
|
|
|
|
return(nButtons);
|
|
}
|
|
|
|
/* Adds a 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.
|
|
*/
|
|
|
|
int PASCAL TBLoadImages(PTBSTATE ptb, int id, HINSTANCE hinst)
|
|
{
|
|
int iTemp;
|
|
TBBMINFO bmi;
|
|
|
|
MapToStandardBitmaps(&hinst, &id, &iTemp);
|
|
|
|
bmi.hInst = hinst;
|
|
bmi.wID = id;
|
|
bmi.nButtons = iTemp;
|
|
if (ptb->himl)
|
|
iTemp = ImageList_GetImageCount(ptb->himl);
|
|
else
|
|
iTemp = 0;
|
|
|
|
if (!TBAddBitmapToImageList(ptb, &bmi))
|
|
return(-1);
|
|
|
|
ptb->fHimlNative = TRUE;
|
|
return iTemp;
|
|
}
|
|
|
|
BOOL NEAR PASCAL ReplaceBitmap(PTBSTATE ptb, LPTBREPLACEBITMAP lprb)
|
|
{
|
|
int nBM;
|
|
PTBBMINFO pTemp;
|
|
|
|
#ifdef WIN32
|
|
int iTemp;
|
|
|
|
MapToStandardBitmaps(&lprb->hInstOld, &lprb->nIDOld, &iTemp);
|
|
MapToStandardBitmaps(&lprb->hInstNew, &lprb->nIDNew, &lprb->nButtons);
|
|
#endif
|
|
|
|
for (nBM=ptb->nBitmaps, pTemp=ptb->pBitmaps;
|
|
nBM>0; --nBM, ++pTemp)
|
|
{
|
|
if (pTemp->hInst==lprb->hInstOld && pTemp->wID==lprb->nIDOld)
|
|
{
|
|
// number of buttons must match
|
|
pTemp->hInst = lprb->hInstNew;
|
|
pTemp->wID = lprb->nIDNew;
|
|
pTemp->nButtons = lprb->nButtons;
|
|
TBInvalidateImageList(ptb);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void FAR PASCAL FlushToolTipsMgr(PTBSTATE ptb) {
|
|
|
|
// change all the rects for the tool tips mgr. this is
|
|
// cheap, and we don't do it often, so go ahead
|
|
// and do them all.
|
|
if(ptb->hwndToolTips) {
|
|
UINT i;
|
|
TOOLINFO ti;
|
|
PTBBUTTON pButton;
|
|
|
|
ti.cbSize = sizeof(ti);
|
|
ti.hwnd = ptb->ci.hwnd;
|
|
ti.lpszText = LPSTR_TEXTCALLBACK;
|
|
for ( i = 0, pButton = ptb->Buttons;
|
|
i < (UINT)ptb->iNumButtons;
|
|
i++, pButton++) {
|
|
|
|
if (!(pButton->fsStyle & TBSTYLE_SEP)) {
|
|
ti.uId = pButton->idCommand;
|
|
if (!GetItemRect(ptb, i, &ti.rect))
|
|
ti.rect.left = ti.rect.right = ti.rect.top = ti.rect.bottom = 0;
|
|
|
|
SendMessage(ptb->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL FAR PASCAL InsertButtons(PTBSTATE ptb, UINT uWhere, UINT uButtons, LPTBBUTTON lpButtons, BOOL fNative)
|
|
{
|
|
PTBBUTTON pIn, pOut;
|
|
PTBSTATE ptbNew;
|
|
UINT uAdded;
|
|
UINT uStart;
|
|
BOOL fRecalc;
|
|
|
|
// comments by chee (not the original author) so they not be
|
|
// exactly right... be warned.
|
|
|
|
if (!ptb || !ptb->uStructSize)
|
|
return FALSE;
|
|
|
|
// enlarge the main structure
|
|
ptbNew = (PTBSTATE)CCLocalReAlloc(ptb, sizeof(TBSTATE) - sizeof(TBBUTTON)
|
|
+ (ptb->iNumButtons + uButtons) * sizeof(TBBUTTON));
|
|
|
|
if (!ptbNew)
|
|
return FALSE;
|
|
|
|
ptb = ptbNew;
|
|
SetWindowInt(ptb->ci.hwnd, 0, (int)ptb);
|
|
|
|
// if where points beyond the end, set it at the end
|
|
if (uWhere > (UINT)ptb->iNumButtons)
|
|
uWhere = ptb->iNumButtons;
|
|
|
|
// Need to save these since the values gues toasted.
|
|
uAdded = uButtons;
|
|
uStart = uWhere;
|
|
|
|
// move buttons above uWhere up uButton spaces
|
|
// the uWhere gets inverted and counts to zero..
|
|
for (pIn=ptb->Buttons+ptb->iNumButtons-1, pOut=pIn+uButtons,
|
|
uWhere=(UINT)ptb->iNumButtons-uWhere; uWhere>0;
|
|
--pIn, --pOut, --uWhere)
|
|
*pOut = *pIn;
|
|
|
|
// only need to recalc if there are strings & room enough to actually show them
|
|
fRecalc = (TBHasStrings(ptb) && ((ptb->ci.style & TBSTYLE_LIST) || ((ptb->iDyBitmap + YSLOP + g_cyEdge) < ptb->iButHeight)));
|
|
|
|
// now do the copy.
|
|
for (lpButtons=(LPTBBUTTON)((LPBYTE)lpButtons+ptb->uStructSize*(uButtons-1)),
|
|
ptb->iNumButtons+=(int)uButtons; // init
|
|
uButtons>0; //test
|
|
--pOut, lpButtons=(LPTBBUTTON)((LPBYTE)lpButtons-ptb->uStructSize), --uButtons)
|
|
{
|
|
TBInputStruct(ptb, pOut, lpButtons);
|
|
|
|
if (TBISSTRINGPTR(pOut->iString)) {
|
|
LPTSTR psz = (LPTSTR)pOut->iString;
|
|
#ifdef UNICODE
|
|
if (!fNative) {
|
|
psz = ProduceWFromA(ptb->ci.uiCodePage, (LPSTR)psz);
|
|
}
|
|
#endif
|
|
pOut->iString = 0;
|
|
Str_Set((LPTSTR*)&pOut->iString, psz);
|
|
|
|
#ifdef UNICODE
|
|
if (!fNative)
|
|
FreeProducedString(psz);
|
|
#endif
|
|
if (!ptb->fNoStringPool)
|
|
fRecalc = TRUE;
|
|
|
|
ptb->fNoStringPool = TRUE;
|
|
}
|
|
|
|
if(ptb->hwndToolTips && !(lpButtons->fsStyle & TBSTYLE_SEP)) {
|
|
TOOLINFO ti;
|
|
// don't bother setting the rect because we'll do it below
|
|
// in FlushToolTipsMgr;
|
|
ti.cbSize = sizeof(ti);
|
|
ti.uFlags = 0;
|
|
ti.hwnd = ptb->ci.hwnd;
|
|
ti.uId = lpButtons->idCommand;
|
|
ti.lpszText = LPSTR_TEXTCALLBACK;
|
|
SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
|
|
(LPARAM)(LPTOOLINFO)&ti);
|
|
}
|
|
|
|
if ((pOut->fsStyle & TBSTYLE_SEP) && pOut->iBitmap <=0)
|
|
pOut->iBitmap = g_dxButtonSep;
|
|
}
|
|
|
|
// Re-compute layout if toolbar is wrapable.
|
|
if (ptb->ci.style & TBSTYLE_WRAPABLE) {
|
|
SendMessage(ptb->ci.hwnd, TB_AUTOSIZE, 0, 0);
|
|
}
|
|
|
|
FlushToolTipsMgr(ptb);
|
|
|
|
if (fRecalc)
|
|
TBRecalc(ptb);
|
|
|
|
//
|
|
// Reorder notification so apps can go requery what's on the toolbar if
|
|
// more than 1 button was added; otherwise, just say create.
|
|
//
|
|
if (uAdded == 1)
|
|
MyNotifyWinEvent(EVENT_OBJECT_CREATE, ptb->ci.hwnd, OBJID_CLIENT,
|
|
uWhere+1);
|
|
else
|
|
MyNotifyWinEvent(EVENT_OBJECT_REORDER, ptb->ci.hwnd, OBJID_CLIENT, 0);
|
|
|
|
// We need to completely redraw the toolbar at this point.
|
|
// this MUST be done last!
|
|
// tbrecalc and others will nuke out invalid area and we won't paint if this isn't last
|
|
InvalidateRect(ptb->ci.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.
|
|
*/
|
|
BOOL FAR PASCAL DeleteButton(PTBSTATE ptb, UINT uIndex)
|
|
{
|
|
PTBBUTTON pIn, pOut;
|
|
BOOL fRecalc;
|
|
|
|
if (uIndex >= (UINT)ptb->iNumButtons)
|
|
return FALSE;
|
|
|
|
MyNotifyWinEvent(EVENT_OBJECT_DESTROY, ptb->ci.hwnd, OBJID_CLIENT, uIndex+1);
|
|
|
|
if (ptb->hwndToolTips) {
|
|
TOOLINFO ti;
|
|
|
|
ti.cbSize = sizeof(ti);
|
|
ti.hwnd = ptb->ci.hwnd;
|
|
ti.uId = ptb->Buttons[uIndex].idCommand;
|
|
SendMessage(ptb->hwndToolTips, TTM_DELTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
|
|
}
|
|
|
|
--ptb->iNumButtons;
|
|
|
|
pOut = ptb->Buttons + uIndex;
|
|
|
|
fRecalc = (pOut->fsState & TBSTATE_WRAP);
|
|
|
|
for (pIn = pOut + 1; uIndex<(UINT)ptb->iNumButtons; ++uIndex, ++pIn, ++pOut)
|
|
{
|
|
fRecalc |= (pIn->fsState & TBSTATE_WRAP);
|
|
*pOut = *pIn;
|
|
}
|
|
|
|
// We need to completely recalc or redraw the toolbar at this point.
|
|
if ((ptb->ci.style & TBSTYLE_WRAPABLE) && fRecalc)
|
|
{
|
|
RECT rc;
|
|
HWND hwnd = ptb->ci.hwnd;
|
|
|
|
if (!(ptb->ci.style & CCS_NORESIZE) && !(ptb->ci.style & CCS_NOPARENTALIGN))
|
|
hwnd = GetParent(hwnd);
|
|
|
|
GetWindowRect(hwnd, &rc);
|
|
|
|
WrapToolbar(ptb, rc.right - rc.left, &rc, NULL);
|
|
FlushToolTipsMgr(ptb);
|
|
}
|
|
|
|
InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
|
|
|
|
FlushToolTipsMgr(ptb);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// deal with old TBBUTON structs for compatibility
|
|
|
|
void FAR PASCAL TBInputStruct(PTBSTATE ptb, LPTBBUTTON pButtonInt, LPTBBUTTON pButtonExt)
|
|
{
|
|
if (ptb->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->dwData = 0;
|
|
pButtonInt->iString = -1;
|
|
}
|
|
}
|
|
|
|
|
|
void NEAR PASCAL TBOutputStruct(PTBSTATE ptb, LPTBBUTTON pButtonInt, LPTBBUTTON pButtonExt)
|
|
{
|
|
if (ptb->uStructSize >= sizeof(TBBUTTON))
|
|
{
|
|
LPBYTE pOut;
|
|
int i;
|
|
|
|
/* Fill the part we know about and fill the rest with 0's
|
|
*/
|
|
*pButtonExt = *pButtonInt;
|
|
for (i = ptb->uStructSize - sizeof(TBBUTTON), pOut = (LPBYTE)(pButtonExt + 1);
|
|
i > 0; --i, ++pOut)
|
|
{
|
|
*pOut = 0;
|
|
}
|
|
}
|
|
else
|
|
/* It is assumed the only other possibility is the OLDBUTTON struct */
|
|
{
|
|
*(LPOLDTBBUTTON)pButtonExt = *(LPOLDTBBUTTON)pButtonInt;
|
|
}
|
|
}
|
|
|
|
void NEAR PASCAL TBOnButtonStructSize(PTBSTATE ptb, UINT uStructSize)
|
|
{
|
|
/* You are not allowed to change this after adding buttons.
|
|
*/
|
|
if (ptb && !ptb->iNumButtons)
|
|
{
|
|
ptb->uStructSize = uStructSize;
|
|
}
|
|
}
|
|
|
|
void TBAutoSize(PTBSTATE ptb)
|
|
{
|
|
if (ptb)
|
|
{
|
|
HWND hwndParent;
|
|
RECT rc;
|
|
|
|
hwndParent = GetParent(ptb->ci.hwnd);
|
|
if (!hwndParent)
|
|
return;
|
|
|
|
if (ptb->ci.style & TBSTYLE_WRAPABLE)
|
|
{
|
|
RECT rcNew;
|
|
if ((ptb->ci.style & CCS_NORESIZE) || (ptb->ci.style & CCS_NOPARENTALIGN)) {
|
|
GetWindowRect(ptb->ci.hwnd, &rc);
|
|
} else {
|
|
GetWindowRect(hwndParent, &rc);
|
|
}
|
|
|
|
WrapToolbar(ptb, rc.right - rc.left, &rcNew, NULL);
|
|
FlushToolTipsMgr(ptb);
|
|
}
|
|
|
|
GetWindowRect(ptb->ci.hwnd, &rc);
|
|
MapWindowPoints(HWND_DESKTOP, hwndParent, (LPPOINT)&rc, 2);
|
|
NewSize(ptb->ci.hwnd, ptb->iButHeight * CountRows(ptb) + g_cxEdge * 2, ptb->ci.style,
|
|
rc.left, rc.top, rc.right, rc.bottom);
|
|
}
|
|
}
|
|
|
|
|
|
void TBSetStyle(PTBSTATE ptb, DWORD dwStyle)
|
|
{
|
|
if (ptb)
|
|
{
|
|
BOOL fSizeChanged = FALSE;
|
|
|
|
if ((BOOL)(ptb->ci.style & TBSTYLE_WRAPABLE) != (BOOL)(dwStyle & TBSTYLE_WRAPABLE))
|
|
{
|
|
int i;
|
|
fSizeChanged = TRUE;
|
|
|
|
for (i=0; i<ptb->iNumButtons; i++)
|
|
ptb->Buttons[i].fsState &= ~TBSTATE_WRAP;
|
|
}
|
|
|
|
ptb->ci.style = dwStyle;
|
|
|
|
if (fSizeChanged)
|
|
TBRecalc(ptb);
|
|
|
|
TBAutoSize(ptb);
|
|
#ifndef WINNT
|
|
DebugMsg(DM_TRACE, TEXT("toolbar window style changed %x"), ptb->ci.style);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
LRESULT TB_OnSetImage(PTBSTATE ptb, PTBBUTTON ptbButton, int iImage)
|
|
{
|
|
if (!ptb->fHimlNative) {
|
|
if (ptb->fHimlValid) {
|
|
if (!ptb->himl ||
|
|
iImage >= ImageList_GetImageCount(ptb->himl))
|
|
return FALSE;
|
|
} else {
|
|
|
|
PTBBMINFO pTemp;
|
|
int nBitmap;
|
|
UINT nTot;
|
|
|
|
// we're not natively himl and we've got some invalid
|
|
// image state, so we need to count the bitmaps ourselvesa
|
|
pTemp = ptb->pBitmaps;
|
|
nTot = 0;
|
|
|
|
for (nBitmap=0; nBitmap < ptb->nBitmaps; nBitmap++) {
|
|
nTot += pTemp->nButtons;
|
|
pTemp++;
|
|
}
|
|
|
|
if (iImage >= (int)nTot)
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
ptbButton->iBitmap = iImage;
|
|
InvalidateButton(ptb, ptbButton, FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
void TB_OnDestroy(PTBSTATE ptb)
|
|
{
|
|
if (ptb)
|
|
{
|
|
HWND hwnd = ptb->ci.hwnd;
|
|
int i;
|
|
|
|
for (i = 0; i < ptb->iNumButtons; i++) {
|
|
if (TBISSTRINGPTR(ptb->Buttons[i].iString))
|
|
Str_Set((LPTSTR*)&ptb->Buttons[i].iString, NULL);
|
|
}
|
|
|
|
//
|
|
// If the toolbar created tooltips, then destroy them.
|
|
//
|
|
if ((ptb->ci.style & TBSTYLE_TOOLTIPS) && IsWindow(ptb->hwndToolTips)) {
|
|
DestroyWindow (ptb->hwndToolTips);
|
|
ptb->hwndToolTips = NULL;
|
|
}
|
|
|
|
if (ptb->hDragProxy)
|
|
DestroyDragProxy(ptb->hDragProxy);
|
|
|
|
if (ptb->hbmMono)
|
|
DeleteObject(ptb->hbmMono);
|
|
|
|
ReleaseMonoDC(ptb);
|
|
|
|
if (ptb->nStrings > 0)
|
|
DestroyStrings(ptb);
|
|
|
|
if (ptb->hfontIcon && ptb->fFontCreated)
|
|
DeleteObject(ptb->hfontIcon);
|
|
|
|
// only do this destroy if pBitmaps exists..
|
|
// this is our signal that it was from an old style toolba
|
|
// and we created it ourselves.
|
|
if (ptb->pBitmaps && ptb->himl)
|
|
ImageList_Destroy(ptb->himl);
|
|
|
|
if (ptb->pBitmaps)
|
|
LocalFree(ptb->pBitmaps);
|
|
|
|
|
|
LocalFree((HLOCAL)ptb);
|
|
SetWindowInt(hwnd, 0, 0);
|
|
}
|
|
TerminateDitherBrush();
|
|
}
|
|
|
|
void TB_OnSetState(PTBSTATE ptb, PTBBUTTON ptbButton, BYTE bState, int iPos)
|
|
{
|
|
BYTE fsState;
|
|
fsState = bState ^ ptbButton->fsState;
|
|
ptbButton->fsState = bState;
|
|
|
|
if (fsState)
|
|
{
|
|
if (fsState & TBSTATE_HIDDEN)
|
|
InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
|
|
else
|
|
InvalidateButton(ptb, ptbButton, TRUE);
|
|
|
|
MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, ptb->ci.hwnd, OBJID_CLIENT,
|
|
iPos+1);
|
|
}
|
|
}
|
|
|
|
void TB_OnSetCmdID(PTBSTATE ptb, PTBBUTTON ptbButton, UINT idCommand)
|
|
{
|
|
UINT uiOldID;
|
|
|
|
uiOldID = ptbButton->idCommand;
|
|
ptbButton->idCommand = idCommand;
|
|
|
|
//
|
|
// If the app was using tooltips, then
|
|
// we need to update the command id there also.
|
|
//
|
|
|
|
if(ptb->hwndToolTips) {
|
|
TOOLINFO ti;
|
|
|
|
//
|
|
// Query the old information
|
|
//
|
|
|
|
ti.cbSize = sizeof(ti);
|
|
ti.hwnd = ptb->ci.hwnd;
|
|
ti.uId = uiOldID;
|
|
SendMessage(ptb->hwndToolTips, TTM_GETTOOLINFO, 0,
|
|
(LPARAM)(LPTOOLINFO)&ti);
|
|
|
|
//
|
|
// Delete the old tool since we can't just
|
|
// change the command id.
|
|
//
|
|
|
|
SendMessage(ptb->hwndToolTips, TTM_DELTOOL, 0,
|
|
(LPARAM)(LPTOOLINFO)&ti);
|
|
|
|
//
|
|
// Add the new tool with the new command id.
|
|
//
|
|
|
|
ti.uId = idCommand;
|
|
SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
|
|
(LPARAM)(LPTOOLINFO)&ti);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
LRESULT TB_OnSetButtonInfo(PTBSTATE ptb, int idBtn, LPTBBUTTONINFO ptbbi)
|
|
{
|
|
int iPos;
|
|
|
|
if (ptbbi->cbSize != SIZEOF(TBBUTTONINFO))
|
|
return 0;
|
|
|
|
iPos = PositionFromID(ptb, idBtn);
|
|
|
|
if (iPos != -1)
|
|
{
|
|
PTBBUTTON ptbButton;
|
|
BOOL fInvalidate = FALSE;
|
|
|
|
ptbButton = ptb->Buttons + iPos;
|
|
|
|
if (ptbbi->dwMask & TBIF_STYLE) {
|
|
ptbbi->fsStyle = ptbbi->fsStyle;
|
|
fInvalidate = TRUE;
|
|
}
|
|
|
|
if (ptbbi->dwMask & TBIF_STATE) {
|
|
TB_OnSetState(ptb, ptbButton, ptbbi->fsState, iPos);
|
|
}
|
|
|
|
if (ptbbi->dwMask & TBIF_IMAGE) {
|
|
TB_OnSetImage(ptb, ptbButton, ptbbi->iImage);
|
|
}
|
|
|
|
if (ptbbi->dwMask & TBIF_TEXT) {
|
|
ptb->fNoStringPool = TRUE;
|
|
if (!TBISSTRINGPTR(ptbButton->iString)) {
|
|
ptbButton->iString = 0;
|
|
}
|
|
|
|
Str_Set((LPTSTR*)&ptbButton->iString, ptbbi->pszText);
|
|
fInvalidate = TRUE;
|
|
}
|
|
|
|
if (ptbbi->dwMask & TBIF_LPARAM) {
|
|
ptbButton->dwData = ptbbi->lParam;
|
|
}
|
|
|
|
if (ptbbi->dwMask & TBIF_COMMAND) {
|
|
TB_OnSetCmdID(ptb, ptbButton, ptbbi->idCommand);
|
|
}
|
|
|
|
if (fInvalidate)
|
|
InvalidateButton(ptb, ptbButton, FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
LRESULT TB_OnGetButtonInfo(PTBSTATE ptb, int idBtn, LPTBBUTTONINFO ptbbi)
|
|
{
|
|
int iPos;
|
|
|
|
if (ptbbi->cbSize != SIZEOF(TBBUTTONINFO))
|
|
return 0;
|
|
|
|
iPos = PositionFromID(ptb, idBtn);
|
|
if (iPos != -1)
|
|
{
|
|
PTBBUTTON ptbButton;
|
|
ptbButton = ptb->Buttons + iPos;
|
|
|
|
if (ptbbi->dwMask & TBIF_STYLE) {
|
|
ptbbi->fsStyle = ptbButton->fsStyle;
|
|
}
|
|
|
|
if (ptbbi->dwMask & TBIF_STATE) {
|
|
ptbbi->fsState = ptbButton->fsState;
|
|
}
|
|
|
|
if (ptbbi->dwMask & TBIF_IMAGE) {
|
|
ptbbi->iImage = ptbButton->iBitmap;
|
|
}
|
|
|
|
if (ptbbi->dwMask & TBIF_TEXT) {
|
|
|
|
if (TBISSTRINGPTR(ptbButton->iString)) {
|
|
lstrcpyn(ptbbi->pszText, (LPCTSTR)ptbButton->iString, ptbbi->cchText);
|
|
}
|
|
}
|
|
|
|
if (ptbbi->dwMask & TBIF_LPARAM) {
|
|
ptbbi->lParam = ptbButton->dwData;
|
|
}
|
|
|
|
if (ptbbi->dwMask & TBIF_COMMAND) {
|
|
ptbbi->idCommand = ptbButton->idCommand;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
LRESULT TB_OnSetButtonInfoA(PTBSTATE ptb, int idBtn, LPTBBUTTONINFOA ptbbiA)
|
|
{
|
|
TBBUTTONINFO tbbi = *(LPTBBUTTONINFO)ptbbiA;
|
|
WCHAR szText[256];
|
|
|
|
if (ptbbiA->dwMask & TBIF_TEXT) {
|
|
tbbi.pszText = szText;
|
|
tbbi.cchText = ARRAYSIZE(szText);
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, (LPCSTR) ptbbiA->pszText, -1,
|
|
szText, ARRAYSIZE(szText));
|
|
}
|
|
|
|
return TB_OnSetButtonInfo(ptb, idBtn, (LPTBBUTTONINFO)&tbbi);
|
|
}
|
|
|
|
LRESULT TB_OnGetButtonInfoA(PTBSTATE ptb, int idBtn, LPTBBUTTONINFOA ptbbiA)
|
|
{
|
|
PTBBUTTON ptbButton;
|
|
int iPos;
|
|
DWORD dwMask = ptbbiA->dwMask;
|
|
|
|
ptbbiA->dwMask &= ~TBIF_TEXT;
|
|
|
|
if (!TB_OnGetButtonInfo(ptb, idBtn, (LPTBBUTTONINFO)ptbbiA))
|
|
return 0;
|
|
|
|
iPos = PositionFromID(ptb, (int)ptbbiA->idCommand);
|
|
ptbButton = ptb->Buttons + iPos;
|
|
|
|
ptbbiA->dwMask = dwMask;
|
|
if (ptbbiA->dwMask & TBIF_TEXT) {
|
|
if (TBISSTRINGPTR(ptbButton->iString)) {
|
|
WideCharToMultiByte (CP_ACP, 0, (LPCTSTR)ptbButton->iString,
|
|
-1, ptbbiA->pszText , ptbbiA->cchText, NULL, NULL);
|
|
} else {
|
|
ptbbiA->pszText[0] = 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
LRESULT CALLBACK ToolbarWndProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BOOL fSameButton;
|
|
PTBBUTTON ptbButton;
|
|
int iPos;
|
|
DWORD dw;
|
|
PTBSTATE ptb = (PTBSTATE)GetWindowInt(hwnd, 0);
|
|
|
|
if (!ptb && wMsg > WM_USER)
|
|
goto DoDefault;
|
|
|
|
switch (wMsg) {
|
|
case WM_NCCREATE:
|
|
|
|
#define lpcs ((LPCREATESTRUCT)lParam)
|
|
|
|
InitDitherBrush();
|
|
InitGlobalColors();
|
|
|
|
/* create the state data for this toolbar */
|
|
|
|
ptb = (PTBSTATE)LocalAlloc(LPTR, sizeof(TBSTATE) - sizeof(TBBUTTON));
|
|
if (!ptb)
|
|
return 0; // WM_NCCREATE failure is 0
|
|
|
|
// note, zero init memory from above
|
|
CIInitialize(&ptb->ci, hwnd, lpcs);
|
|
ptb->xFirstButton = s_xFirstButton;
|
|
ptb->nCurHTButton = -1;
|
|
|
|
Assert(ptb->uStructSize == 0);
|
|
Assert(ptb->hfontIcon == NULL); // initialize to null.
|
|
Assert(ptb->iButMinWidth == 0);
|
|
Assert(ptb->iButMaxWidth == 0);
|
|
|
|
ptb->nTextRows = 1;
|
|
ptb->fActive = TRUE;
|
|
|
|
// IE 3 passes in TBSTYLE_FLAT, but they really
|
|
// wanted TBSTYLE_TRANSPARENT also.
|
|
//
|
|
if (ptb->ci.style & TBSTYLE_FLAT) {
|
|
ptb->ci.style |= TBSTYLE_TRANSPARENT;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (IsFlagSet(g_dwPrototype, PTF_FLATLOOK))
|
|
{
|
|
TraceMsg(TF_GENERAL, "Using flat look for toolbars.");
|
|
ptb->ci.style |= TBSTYLE_FLAT;
|
|
}
|
|
#endif
|
|
|
|
// Now Initialize the hfont we will use.
|
|
TBChangeFont(ptb, 0, NULL);
|
|
|
|
// grow the button size to the appropriate girth
|
|
if (!SetBitmapSize(ptb, DEFAULTBITMAPX, DEFAULTBITMAPX))
|
|
{
|
|
LocalFree((HLOCAL)ptb);
|
|
return 0; // WM_NCCREATE failure is 0
|
|
}
|
|
|
|
SetWindowInt(hwnd, 0, (int)ptb);
|
|
|
|
if (!(ptb->ci.style & (CCS_TOP | CCS_NOMOVEY | CCS_BOTTOM)))
|
|
{
|
|
ptb->ci.style |= CCS_TOP;
|
|
SetWindowLong(hwnd, GWL_STYLE, ptb->ci.style);
|
|
}
|
|
|
|
|
|
if (ptb->ci.style & TBSTYLE_TOOLTIPS)
|
|
{
|
|
TOOLINFO ti;
|
|
// don't bother setting the rect because we'll do it below
|
|
// in FlushToolTipsMgr;
|
|
ti.cbSize = sizeof(ti);
|
|
ti.uFlags = TTF_IDISHWND;
|
|
ti.hwnd = hwnd;
|
|
ti.uId = (UINT)hwnd;
|
|
ti.lpszText = 0;
|
|
|
|
ptb->hwndToolTips = CreateWindow(c_szSToolTipsClass, NULL,
|
|
WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
hwnd, NULL, lpcs->hInstance, NULL);
|
|
|
|
SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
|
|
(LPARAM)(LPTOOLINFO)&ti);
|
|
}
|
|
return TRUE;
|
|
|
|
case WM_CREATE:
|
|
if (ptb->ci.style & TBSTYLE_REGISTERDROP)
|
|
{
|
|
ptb->hDragProxy = CreateDragProxy(ptb->ci.hwnd, ToolbarDragCallback, TRUE);
|
|
}
|
|
goto DoDefault;
|
|
|
|
case WM_DESTROY:
|
|
TB_OnDestroy(ptb);
|
|
break;
|
|
|
|
case WM_SETFONT:
|
|
if (ptb)
|
|
TBSetFont(ptb, (HFONT)wParam, (BOOL)lParam);
|
|
return TRUE;
|
|
|
|
case WM_NCCALCSIZE:
|
|
// let defwindowproc handle the standard borders etc...
|
|
dw = DefWindowProc(hwnd, wMsg, wParam, lParam ) ;
|
|
|
|
// add the extra edge at the top of the toolbar to seperate from the menu bar
|
|
if (ptb && !(ptb->ci.style & CCS_NODIVIDER))
|
|
{
|
|
((NCCALCSIZE_PARAMS FAR *)lParam)->rgrc[0].top += g_cyEdge;
|
|
}
|
|
|
|
return dw;
|
|
|
|
case WM_NCHITTEST:
|
|
return HTCLIENT;
|
|
|
|
case WM_NCACTIVATE:
|
|
if (ptb && (ptb->fActive != (BOOL)wParam)) {
|
|
int iButton;
|
|
PTBBUTTON pButton;
|
|
|
|
ptb->fActive = (BOOL) wParam;
|
|
|
|
for (iButton = 0; iButton < ptb->iNumButtons; iButton++) {
|
|
pButton = &ptb->Buttons[iButton];
|
|
InvalidateButton(ptb, pButton, FALSE);
|
|
}
|
|
}
|
|
// fall through...
|
|
|
|
case WM_NCPAINT:
|
|
// old-style toolbars are forced to be without dividers above
|
|
if (ptb && !(ptb->ci.style & CCS_NODIVIDER))
|
|
{
|
|
RECT rc;
|
|
HDC hdc = GetWindowDC(hwnd);
|
|
GetWindowRect(hwnd, &rc);
|
|
MapWindowRect(NULL, hwnd, &rc); // screen -> client
|
|
|
|
rc.bottom = -rc.top; // bottom of NC area
|
|
rc.top = rc.bottom - g_cyEdge;
|
|
|
|
DrawEdge(hdc, &rc, BDR_SUNKENOUTER, BF_TOP | BF_BOTTOM);
|
|
ReleaseDC(hwnd, hdc);
|
|
}
|
|
goto DoDefault;
|
|
|
|
case WM_PRINTCLIENT:
|
|
case WM_PAINT:
|
|
if (ptb)
|
|
ToolbarPaint(ptb, (HDC)wParam);
|
|
break;
|
|
|
|
case WM_ERASEBKGND:
|
|
if (ptb)
|
|
TB_OnEraseBkgnd(ptb, (HDC) wParam);
|
|
return(TRUE);
|
|
|
|
case WM_SYSCOLORCHANGE:
|
|
if (ptb)
|
|
{
|
|
TB_OnSysColorChange(ptb);
|
|
if (ptb->hwndToolTips)
|
|
SendMessage(ptb->hwndToolTips, wMsg, wParam, lParam);
|
|
}
|
|
break;
|
|
|
|
case TB_GETROWS:
|
|
if (ptb)
|
|
return CountRows(ptb);
|
|
break;
|
|
|
|
case TB_SETROWS:
|
|
if (ptb)
|
|
{
|
|
RECT rc;
|
|
|
|
if (BoxIt(ptb, LOWORD(wParam), HIWORD(wParam), &rc))
|
|
{
|
|
FlushToolTipsMgr(ptb);
|
|
SetWindowPos(hwnd, NULL, 0, 0, rc.right, rc.bottom,
|
|
SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
}
|
|
if (lParam)
|
|
*((RECT FAR *)lParam) = rc;
|
|
}
|
|
break;
|
|
|
|
case WM_WINDOWPOSCHANGING:
|
|
if (ptb && (ptb->ci.style & TBSTYLE_FLAT))
|
|
{
|
|
LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
|
|
RECT rc;
|
|
HWND hwndParent;
|
|
|
|
if (!(lpwp->flags & SWP_NOMOVE) && (hwndParent = GetParent(hwnd)))
|
|
{
|
|
GetWindowRect(hwnd, &rc);
|
|
MapWindowPoints(HWND_DESKTOP, hwndParent, (LPPOINT)&rc, 1);
|
|
if ((rc.left != lpwp->x) || (rc.top != lpwp->y))
|
|
lpwp->flags |= SWP_NOREDRAW;
|
|
}
|
|
}
|
|
goto DoDefault;
|
|
|
|
case WM_MOVE:
|
|
// JJK TODO: This needs to be double buffered to get rid of the flicker
|
|
if (ptb && (ptb->ci.style & TBSTYLE_FLAT))
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
goto DoDefault;
|
|
|
|
case TB_AUTOSIZE:
|
|
case WM_SIZE:
|
|
TBAutoSize(ptb);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
case WM_DRAWITEM:
|
|
case WM_MEASUREITEM:
|
|
case WM_VKEYTOITEM:
|
|
case WM_CHARTOITEM:
|
|
if (ptb)
|
|
SendMessage(ptb->ci.hwndParent, wMsg, wParam, lParam);
|
|
break;
|
|
|
|
case WM_RBUTTONDBLCLK:
|
|
if (ptb && !CCSendNotify(&ptb->ci, NM_RDBLCLK, NULL))
|
|
goto DoDefault;
|
|
break;
|
|
|
|
case WM_RBUTTONUP:
|
|
if (ptb) {
|
|
NMTBRCLICK nm;
|
|
HWND hwndParent = NULL;
|
|
|
|
nm.uiIndex = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
|
if (nm.uiIndex >= 0)
|
|
nm.uiCmd = ptb->Buttons[nm.uiIndex].idCommand;
|
|
else
|
|
nm.uiCmd = (UINT) -1;
|
|
|
|
LPARAM_TO_POINT(lParam, nm.ptMouse);
|
|
if (hwndParent = GetParent(hwnd))
|
|
MapWindowPoints(hwndParent, HWND_DESKTOP, &nm.ptMouse, 1);
|
|
|
|
if (!CCSendNotify(&ptb->ci, NM_RCLICK, (LPNMHDR )&nm))
|
|
goto DoDefault;
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
if (!ptb) {
|
|
break;
|
|
}
|
|
|
|
iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
|
if (iPos < 0 && (ptb->ci.style & CCS_ADJUSTABLE))
|
|
{
|
|
iPos = -1 - iPos;
|
|
CustomizeTB(ptb, iPos);
|
|
} else {
|
|
goto HandleLButtonDown;
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
if (!ptb) {
|
|
break;
|
|
}
|
|
|
|
RelayToToolTips(ptb->hwndToolTips, hwnd, wMsg, wParam, lParam);
|
|
|
|
iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
|
if ((ptb->ci.style & CCS_ADJUSTABLE) &&
|
|
(((wParam & MK_SHIFT) && !(ptb->ci.style & TBSTYLE_ALTDRAG)) ||
|
|
((GetKeyState(VK_MENU) & ~1) && (ptb->ci.style & TBSTYLE_ALTDRAG))))
|
|
{
|
|
MoveButton(ptb, iPos);
|
|
}
|
|
else {
|
|
|
|
HandleLButtonDown:
|
|
if (iPos >= 0 && iPos < ptb->iNumButtons)
|
|
{
|
|
// should this check for the size of the button struct?
|
|
ptbButton = ptb->Buttons + iPos;
|
|
|
|
if (ptbButton->fsStyle & TBSTYLE_DROPDOWN) {
|
|
|
|
if (ptbButton->fsState & TBSTATE_ENABLED) {
|
|
ptbButton->fsState ^= TBSTATE_PRESSED;
|
|
InvalidateButton(ptb, ptbButton, TRUE);
|
|
UpdateWindow(hwnd);
|
|
|
|
MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd,
|
|
OBJID_CLIENT, iPos+1);
|
|
|
|
if (!SendItemNotify(ptb, ptbButton->idCommand, TBN_DROPDOWN)) {
|
|
|
|
MSG msg;
|
|
|
|
PeekMessage(&msg, ptb->ci.hwnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE);
|
|
|
|
ptbButton->fsState ^= TBSTATE_PRESSED;
|
|
InvalidateButton(ptb, ptbButton, TRUE);
|
|
UpdateWindow(hwnd);
|
|
|
|
MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd,
|
|
OBJID_CLIENT, iPos+1);
|
|
}
|
|
|
|
}
|
|
} else {
|
|
ptb->pCaptureButton = ptbButton;
|
|
SetCapture(hwnd);
|
|
|
|
if (ptbButton->fsState & TBSTATE_ENABLED)
|
|
{
|
|
ptbButton->fsState |= TBSTATE_PRESSED;
|
|
InvalidateButton(ptb, ptbButton, TRUE);
|
|
UpdateWindow(hwnd); // imedeate feedback
|
|
|
|
MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd,
|
|
OBJID_CLIENT, iPos+1);
|
|
}
|
|
|
|
SendItemNotify(ptb, ptb->pCaptureButton->idCommand, TBN_BEGINDRAG);
|
|
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_MOUSELEAVE:
|
|
if (ptb)
|
|
{
|
|
RECT rectButton;
|
|
TRACKMOUSEEVENT tme;
|
|
|
|
ASSERT(ptb->ci.style & TBSTYLE_FLAT);
|
|
tme.cbSize = sizeof(TRACKMOUSEEVENT);
|
|
tme.dwFlags = TME_CANCEL | TME_LEAVE;
|
|
tme.hwndTrack = hwnd;
|
|
TrackMouseEvent(&tme);
|
|
ptb->fMouseTrack = FALSE;
|
|
if (ptb->nCurHTButton != -1)
|
|
if (GetItemRect(ptb, ptb->nCurHTButton, &rectButton) )
|
|
InvalidateRect(hwnd, &rectButton, TRUE);
|
|
ptb->nCurHTButton = -1;
|
|
}
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
if (ptb && ptb->fActive)
|
|
{
|
|
RECT rectButton;
|
|
|
|
if ((ptb->ci.style & TBSTYLE_FLAT) )
|
|
{
|
|
iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
|
if (!ptb->fMouseTrack)
|
|
{
|
|
TRACKMOUSEEVENT tme;
|
|
|
|
tme.cbSize = sizeof(TRACKMOUSEEVENT);
|
|
tme.dwFlags = TME_LEAVE;
|
|
tme.hwndTrack = hwnd;
|
|
ptb->fMouseTrack = TRUE;
|
|
TrackMouseEvent(&tme);
|
|
}
|
|
|
|
if ((ptb->nCurHTButton != iPos) && (ptb->nCurHTButton != -1))
|
|
{
|
|
if (GetItemRect(ptb, ptb->nCurHTButton, &rectButton) )
|
|
InvalidateRect(hwnd, &rectButton, TRUE);
|
|
ptb->nCurHTButton = -1;
|
|
|
|
}
|
|
if ((iPos >= 0) && (ptb->nCurHTButton != iPos))
|
|
{
|
|
if ((ptb->Buttons[iPos].fsState & TBSTATE_ENABLED) )
|
|
{
|
|
ptb->nCurHTButton = iPos;
|
|
if (GetItemRect(ptb, iPos, &rectButton) )
|
|
InvalidateRect(hwnd, &rectButton, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
RelayToToolTips(ptb->hwndToolTips, hwnd, wMsg, wParam, lParam);
|
|
|
|
// if the toolbar has lost the capture for some reason, stop
|
|
if (ptb->pCaptureButton == NULL) {
|
|
//DebugMsg(DM_TRACE, TEXT("Bail because pCaptureButton == NULL"));
|
|
break;
|
|
}
|
|
|
|
if (hwnd != GetCapture())
|
|
{
|
|
//DebugMsg(DM_TRACE, TEXT("capture isn't us"));
|
|
SendItemNotify(ptb, ptb->pCaptureButton->idCommand, TBN_ENDDRAG);
|
|
|
|
// if the button is still pressed, unpress it.
|
|
if (ptb->pCaptureButton->fsState & TBSTATE_PRESSED)
|
|
SendMessage(hwnd, TB_PRESSBUTTON, ptb->pCaptureButton->idCommand, 0L);
|
|
ptb->pCaptureButton = NULL;
|
|
}
|
|
else if (ptb->pCaptureButton->fsState & TBSTATE_ENABLED)
|
|
{
|
|
//DebugMsg(DM_TRACE, TEXT("capture IS us, and state is enabled"));
|
|
iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
|
fSameButton = (iPos >= 0 && ptb->pCaptureButton == ptb->Buttons + iPos);
|
|
if (fSameButton == !(ptb->pCaptureButton->fsState & TBSTATE_PRESSED))
|
|
{
|
|
//DebugMsg(DM_TRACE, TEXT("capture IS us, and Button is different"));
|
|
ptb->pCaptureButton->fsState ^= TBSTATE_PRESSED;
|
|
InvalidateButton(ptb, ptb->pCaptureButton, TRUE);
|
|
|
|
MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd,
|
|
OBJID_CLIENT, (ptb->pCaptureButton - ptb->Buttons) + 1);
|
|
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
if (!ptb) {
|
|
break;
|
|
}
|
|
|
|
RelayToToolTips(ptb->hwndToolTips, hwnd, wMsg, wParam, lParam);
|
|
|
|
if (ptb->pCaptureButton != NULL) {
|
|
|
|
int idCommand = ptb->pCaptureButton->idCommand;
|
|
|
|
ReleaseCapture();
|
|
|
|
SendItemNotify(ptb, idCommand, TBN_ENDDRAG);
|
|
|
|
iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
|
if ((ptb->pCaptureButton->fsState & TBSTATE_ENABLED) && iPos >=0
|
|
&& (ptb->pCaptureButton == ptb->Buttons+iPos)) {
|
|
ptb->pCaptureButton->fsState &= ~TBSTATE_PRESSED;
|
|
|
|
if (ptb->pCaptureButton->fsStyle & TBSTYLE_CHECK) {
|
|
if (ptb->pCaptureButton->fsStyle & TBSTYLE_GROUP) {
|
|
|
|
// group buttons already checked can't be force
|
|
// up by the user.
|
|
|
|
if (ptb->pCaptureButton->fsState & TBSTATE_CHECKED) {
|
|
ptb->pCaptureButton = NULL;
|
|
break; // bail!
|
|
}
|
|
|
|
ptb->pCaptureButton->fsState |= TBSTATE_CHECKED;
|
|
MakeGroupConsistant(ptb, idCommand);
|
|
} else {
|
|
ptb->pCaptureButton->fsState ^= TBSTATE_CHECKED; // toggle
|
|
}
|
|
}
|
|
InvalidateButton(ptb, ptb->pCaptureButton, TRUE);
|
|
ptb->pCaptureButton = NULL;
|
|
|
|
MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT,
|
|
iPos+1);
|
|
|
|
FORWARD_WM_COMMAND(ptb->ci.hwndParent, idCommand, hwnd, BN_CLICKED, SendMessage);
|
|
}
|
|
else {
|
|
ptb->pCaptureButton = NULL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_WININICHANGE:
|
|
if (!ptb) {
|
|
break;
|
|
}
|
|
|
|
InitGlobalMetrics(wParam);
|
|
if (ptb->fFontCreated)
|
|
TBChangeFont(ptb, wParam, NULL);
|
|
if (ptb->hwndToolTips)
|
|
SendMessage(ptb->hwndToolTips, wMsg, wParam, lParam);
|
|
break;
|
|
|
|
case WM_NOTIFYFORMAT:
|
|
if (ptb)
|
|
return CIHandleNotifyFormat(&ptb->ci, lParam);
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
#define lpNmhdr ((LPNMHDR)(lParam))
|
|
|
|
if (ptb)
|
|
{
|
|
int i = PositionFromID(ptb, lpNmhdr->idFrom);
|
|
BOOL fEllipsied = FALSE;
|
|
|
|
//
|
|
// We are just going to pass this on to the
|
|
// real parent. Note that -1 is used as
|
|
// the hwndFrom. This prevents SendNotifyEx
|
|
// from updating the NMHDR structure.
|
|
//
|
|
LRESULT lres = SendNotifyEx(ptb->ci.hwndParent, (HWND) -1,
|
|
lpNmhdr->code, lpNmhdr, ptb->ci.bUnicode);
|
|
|
|
#define lpnmTT ((LPTOOLTIPTEXT) lParam)
|
|
|
|
if (i != -1) {
|
|
fEllipsied = (BOOL)(ptb->Buttons[i].fsState & TBSTATE_ELLIPSES);
|
|
|
|
if ((lpNmhdr->code == TTN_NEEDTEXT) &&
|
|
(!ptb->nTextRows || fEllipsied) &&
|
|
lpnmTT->lpszText && !lpnmTT->lpszText[0])
|
|
{
|
|
LPCTSTR psz = TB_StrForButton(ptb, &ptb->Buttons[i]);
|
|
if (psz)
|
|
lpnmTT->lpszText = (LPTSTR)psz;
|
|
}
|
|
}
|
|
return(lres);
|
|
}
|
|
break;
|
|
|
|
case WM_STYLECHANGING:
|
|
if (!ptb)
|
|
break;
|
|
|
|
if (wParam == GWL_STYLE)
|
|
{
|
|
LPSTYLESTRUCT lpStyle = (LPSTYLESTRUCT) lParam;
|
|
|
|
if ((lpStyle->styleOld ^ lpStyle->styleNew) == WS_VISIBLE)
|
|
// MFC can't hide a window to save their lives -- and they
|
|
// think that flipping bits directly will actually give them
|
|
// something -- don't let 'em yank the visible bit directly
|
|
// ... morons ... jeffbog 9/13/96
|
|
lpStyle->styleNew |= WS_VISIBLE;
|
|
}
|
|
break;
|
|
|
|
case WM_STYLECHANGED:
|
|
if (wParam == GWL_STYLE)
|
|
{
|
|
TBSetStyle(ptb, ((LPSTYLESTRUCT)lParam)->styleNew);
|
|
}
|
|
return 0;
|
|
|
|
case TB_SETSTYLE:
|
|
TBSetStyle(ptb, lParam);
|
|
break;
|
|
|
|
case TB_GETSTYLE:
|
|
return (ptb->ci.style);
|
|
|
|
case TB_GETBUTTONSIZE:
|
|
return (MAKELONG(ptb->iButWidth,ptb->iButHeight));
|
|
|
|
case TB_SETBUTTONWIDTH:
|
|
ptb->iButMinWidth = LOWORD(lParam);
|
|
ptb->iButMaxWidth = HIWORD(lParam);
|
|
ptb->iButWidth = 0;
|
|
TBRecalc(ptb);
|
|
return TRUE;
|
|
|
|
case TB_SETSTATE:
|
|
iPos = PositionFromID(ptb, (int)wParam);
|
|
if (iPos < 0)
|
|
return FALSE;
|
|
ptbButton = ptb->Buttons + iPos;
|
|
|
|
TB_OnSetState(ptb, ptbButton, (BYTE)(LOWORD(lParam)), iPos);
|
|
return TRUE;
|
|
|
|
// set the cmd ID of a button based on its position
|
|
case TB_SETCMDID:
|
|
if (wParam >= (UINT)ptb->iNumButtons)
|
|
return FALSE;
|
|
|
|
TB_OnSetCmdID(ptb, &ptb->Buttons[wParam], (UINT)lParam);
|
|
return TRUE;
|
|
|
|
case TB_GETSTATE:
|
|
iPos = PositionFromID(ptb, (int)wParam);
|
|
if (iPos < 0)
|
|
return -1L;
|
|
return ptb->Buttons[iPos].fsState;
|
|
|
|
case TB_ENABLEBUTTON:
|
|
case TB_CHECKBUTTON:
|
|
case TB_PRESSBUTTON:
|
|
case TB_HIDEBUTTON:
|
|
case TB_INDETERMINATE:
|
|
case TB_HIGHLIGHTBUTTON:
|
|
{
|
|
BYTE fsState;
|
|
|
|
iPos = PositionFromID(ptb, (int)wParam);
|
|
if (iPos < 0)
|
|
return FALSE;
|
|
ptbButton = &ptb->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(ptb, (int)wParam);
|
|
|
|
if (wMsg == TB_HIDEBUTTON) {
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
FlushToolTipsMgr(ptb);
|
|
} else
|
|
InvalidateButton(ptb, ptbButton, TRUE);
|
|
|
|
MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, iPos+1);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
case TB_ISBUTTONENABLED:
|
|
case TB_ISBUTTONCHECKED:
|
|
case TB_ISBUTTONPRESSED:
|
|
case TB_ISBUTTONHIDDEN:
|
|
case TB_ISBUTTONINDETERMINATE:
|
|
case TB_ISBUTTONHIGHLIGHTED:
|
|
iPos = PositionFromID(ptb, (int)wParam);
|
|
if (iPos < 0)
|
|
return(-1L);
|
|
return (LRESULT)ptb->Buttons[iPos].fsState & wStateMasks[wMsg - TB_ISBUTTONENABLED];
|
|
|
|
case TB_ADDBITMAP:
|
|
#ifdef WIN32
|
|
case TB_ADDBITMAP32: // only for compatibility with mail
|
|
#define pab ((LPTBADDBITMAP)lParam)
|
|
return AddBitmap(ptb, wParam, pab->hInst, pab->nID);
|
|
#undef pab
|
|
#else
|
|
return AddBitmap(ptb, wParam, (HINSTANCE)LOWORD(lParam), HIWORD(lParam));
|
|
#endif
|
|
|
|
case TB_REPLACEBITMAP:
|
|
return ReplaceBitmap(ptb, (LPTBREPLACEBITMAP)lParam);
|
|
|
|
#ifdef UNICODE
|
|
case TB_ADDSTRINGA:
|
|
{
|
|
LPWSTR lpStrings;
|
|
UINT uiCount;
|
|
LPSTR lpAnsiString = (LPSTR) lParam;
|
|
int iResult;
|
|
BOOL bAllocatedMem = FALSE;
|
|
|
|
if (!wParam && lpAnsiString) {
|
|
//
|
|
// We have to figure out how many characters
|
|
// are in this string.
|
|
//
|
|
uiCount = 0;
|
|
|
|
while (uiCount < MAXSTRINGSIZE) {
|
|
uiCount++;
|
|
if ((*lpAnsiString == 0) && (*(lpAnsiString+1) == 0)) {
|
|
uiCount++; // needed for double null
|
|
break;
|
|
}
|
|
|
|
lpAnsiString++;
|
|
}
|
|
|
|
lpStrings = GlobalAlloc (GPTR, uiCount * sizeof(TCHAR));
|
|
|
|
if (!lpStrings)
|
|
return -1;
|
|
|
|
bAllocatedMem = TRUE;
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, (LPCSTR) lParam, uiCount,
|
|
lpStrings, uiCount);
|
|
|
|
} else {
|
|
lpStrings = (LPWSTR)lParam;
|
|
}
|
|
|
|
iResult = AddStrings(ptb, wParam, (LPARAM)lpStrings);
|
|
|
|
if (bAllocatedMem)
|
|
GlobalFree(lpStrings);
|
|
|
|
return iResult;
|
|
}
|
|
#endif
|
|
|
|
case TB_ADDSTRING:
|
|
return AddStrings(ptb, wParam, lParam);
|
|
|
|
|
|
#ifdef UNICODE
|
|
case TB_ADDBUTTONSA:
|
|
return InsertButtons(ptb, (UINT)-1, wParam, (LPTBBUTTON)lParam, FALSE);
|
|
|
|
case TB_INSERTBUTTONA:
|
|
return InsertButtons(ptb, wParam, 1, (LPTBBUTTON)lParam, FALSE);
|
|
#endif
|
|
|
|
case TB_ADDBUTTONS:
|
|
return InsertButtons(ptb, (UINT)-1, wParam, (LPTBBUTTON)lParam, TRUE);
|
|
|
|
case TB_INSERTBUTTON:
|
|
return InsertButtons(ptb, wParam, 1, (LPTBBUTTON)lParam, TRUE);
|
|
|
|
case TB_DELETEBUTTON:
|
|
return DeleteButton(ptb, wParam);
|
|
|
|
case TB_GETBUTTON:
|
|
if (wParam >= (UINT)ptb->iNumButtons)
|
|
return(FALSE);
|
|
|
|
TBOutputStruct(ptb, ptb->Buttons + wParam, (LPTBBUTTON)lParam);
|
|
return TRUE;
|
|
|
|
case TB_BUTTONCOUNT:
|
|
return ptb->iNumButtons;
|
|
|
|
case TB_COMMANDTOINDEX:
|
|
return PositionFromID(ptb, (int)wParam);
|
|
|
|
#ifdef UNICODE
|
|
case TB_SAVERESTOREA:
|
|
{
|
|
LPWSTR lpSubKeyW, lpValueNameW;
|
|
TBSAVEPARAMSA * lpSaveA = (TBSAVEPARAMSA *) lParam;
|
|
BOOL bResult;
|
|
|
|
lpSubKeyW = ProduceWFromA (CP_ACP, lpSaveA->pszSubKey);
|
|
lpValueNameW = ProduceWFromA (CP_ACP, lpSaveA->pszValueName);
|
|
|
|
bResult = SaveRestoreFromReg(ptb, wParam, lpSaveA->hkr, lpSubKeyW, lpValueNameW);
|
|
|
|
FreeProducedString(lpSubKeyW);
|
|
FreeProducedString(lpValueNameW);
|
|
|
|
return bResult;
|
|
}
|
|
#endif
|
|
|
|
case TB_SAVERESTORE:
|
|
#ifdef WIN32
|
|
#define psr ((TBSAVEPARAMS *)lParam)
|
|
return SaveRestoreFromReg(ptb, wParam, psr->hkr, psr->pszSubKey, psr->pszValueName);
|
|
#undef psr
|
|
#else
|
|
return SaveRestore(ptb, wParam, (LPTSTR FAR *)lParam);
|
|
#endif
|
|
|
|
case TB_CUSTOMIZE:
|
|
CustomizeTB(ptb, ptb->iNumButtons);
|
|
break;
|
|
|
|
case TB_GETRECT:
|
|
// PositionFromID() accepts NULL ptbs!
|
|
wParam = PositionFromID(ptb, wParam);
|
|
// fall through
|
|
case TB_GETITEMRECT:
|
|
if (!lParam)
|
|
break;
|
|
return GetItemRect(ptb, wParam, (LPRECT)lParam);
|
|
|
|
case TB_BUTTONSTRUCTSIZE:
|
|
TBOnButtonStructSize(ptb, wParam);
|
|
break;
|
|
|
|
case TB_SETBUTTONSIZE:
|
|
return GrowToolbar(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), FALSE);
|
|
|
|
case TB_SETBITMAPSIZE:
|
|
return SetBitmapSize(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
|
|
|
case TB_SETIMAGELIST:
|
|
{
|
|
HIMAGELIST himl = ptb->himl;
|
|
int cx, cy;
|
|
|
|
ptb->himl = (HIMAGELIST)lParam;
|
|
ptb->fHimlNative = TRUE;
|
|
|
|
// Update the bitmap size based on this image list
|
|
ImageList_GetIconSize(ptb->himl, &cx, &cy);
|
|
SetBitmapSize(ptb, cx, cy);
|
|
|
|
return (LRESULT)himl;
|
|
}
|
|
|
|
case TB_GETIMAGELIST:
|
|
return (LRESULT)ptb->himl;
|
|
|
|
case TB_SETHOTIMAGELIST:
|
|
{
|
|
HIMAGELIST himl = ptb->himlHot;
|
|
|
|
ptb->himlHot = (HIMAGELIST)lParam;
|
|
return (LRESULT)himl;
|
|
}
|
|
|
|
case TB_GETHOTIMAGELIST:
|
|
return (LRESULT)ptb->himlHot;
|
|
|
|
case TB_GETDISABLEDIMAGELIST:
|
|
return (LRESULT)ptb->himlDisabled;
|
|
|
|
case TB_SETDISABLEDIMAGELIST:
|
|
{
|
|
HIMAGELIST himl = ptb->himlDisabled;
|
|
|
|
ptb->himlDisabled = (HIMAGELIST)lParam;
|
|
return (LRESULT)himl;
|
|
}
|
|
|
|
case TB_GETOBJECT:
|
|
if (IsEqualIID((IID *)wParam, &IID_IDropTarget))
|
|
{
|
|
// if we have not already registered create an unregistered target now
|
|
if (ptb->hDragProxy == NULL)
|
|
ptb->hDragProxy = CreateDragProxy(ptb->ci.hwnd, ToolbarDragCallback, FALSE);
|
|
|
|
if (ptb->hDragProxy)
|
|
return (LRESULT)GetDragProxyTarget(ptb->hDragProxy, (IDropTarget **)lParam);
|
|
}
|
|
return E_FAIL;
|
|
|
|
case WM_GETFONT:
|
|
return (LRESULT)(UINT)(ptb? ptb->hfontIcon : 0);
|
|
|
|
case TB_LOADIMAGES:
|
|
return TBLoadImages(ptb, wParam, (HINSTANCE)lParam);
|
|
|
|
case TB_GETTOOLTIPS:
|
|
return (LRESULT)(UINT)ptb->hwndToolTips;
|
|
|
|
case TB_SETTOOLTIPS:
|
|
ptb->hwndToolTips = (HWND)wParam;
|
|
break;
|
|
|
|
case TB_SETPARENT:
|
|
{
|
|
HWND hwndOld = ptb->ci.hwndParent;
|
|
|
|
ptb->ci.hwndParent = (HWND)wParam;
|
|
return (LRESULT)(UINT)hwndOld;
|
|
}
|
|
|
|
|
|
#ifdef UNICODE
|
|
case TB_GETBUTTONINFOA:
|
|
return TB_OnGetButtonInfoA(ptb, (int)wParam, (LPTBBUTTONINFOA)lParam);
|
|
|
|
case TB_SETBUTTONINFOA:
|
|
return TB_OnSetButtonInfoA(ptb, (int)wParam, (LPTBBUTTONINFOA)lParam);
|
|
#endif
|
|
|
|
case TB_GETBUTTONINFO:
|
|
return TB_OnGetButtonInfo(ptb, (int)wParam, (LPTBBUTTONINFO)lParam);
|
|
|
|
case TB_SETBUTTONINFO:
|
|
return TB_OnSetButtonInfo(ptb, (int)wParam, (LPTBBUTTONINFO)lParam);
|
|
|
|
case TB_CHANGEBITMAP:
|
|
iPos = PositionFromID(ptb, (int)wParam);
|
|
if (iPos < 0)
|
|
return(FALSE);
|
|
|
|
//
|
|
// Check to see if the new bitmap ID is
|
|
// valid.
|
|
//
|
|
ptbButton = &ptb->Buttons[iPos];
|
|
return TB_OnSetImage(ptb, ptbButton, LOWORD(lParam));
|
|
|
|
case TB_GETBITMAP:
|
|
iPos = PositionFromID(ptb, (int)wParam);
|
|
if (iPos < 0)
|
|
return(FALSE);
|
|
ptbButton = &ptb->Buttons[iPos];
|
|
return ptbButton->iBitmap;
|
|
|
|
#ifdef UNICODE
|
|
case TB_GETBUTTONTEXTA:
|
|
iPos = PositionFromID(ptb, (int)wParam);
|
|
if (iPos >= 0) {
|
|
LPTSTR psz;
|
|
|
|
ptbButton = &ptb->Buttons[iPos];
|
|
psz = TB_StrForButton(ptb, ptbButton);
|
|
if (psz) {
|
|
if (lParam) {
|
|
WideCharToMultiByte (CP_ACP, 0, psz,
|
|
-1,(LPSTR)lParam , INT_MAX, NULL, NULL);
|
|
}
|
|
return lstrlen(psz);
|
|
}
|
|
}
|
|
return -1;
|
|
#endif
|
|
|
|
case TB_GETBUTTONTEXT:
|
|
iPos = PositionFromID(ptb, (int)wParam);
|
|
if (iPos >= 0) {
|
|
LPCTSTR psz;
|
|
|
|
ptbButton = &ptb->Buttons[iPos];
|
|
psz = TB_StrForButton(ptb, ptbButton);
|
|
if (psz) {
|
|
if (lParam) {
|
|
lstrcpy((LPTSTR)lParam, psz);
|
|
}
|
|
return lstrlen(psz);
|
|
}
|
|
}
|
|
return -1;
|
|
|
|
|
|
#ifdef WIN32
|
|
case TB_GETBITMAPFLAGS:
|
|
{
|
|
DWORD fFlags = 0;
|
|
HDC hdc = GetDC(NULL);
|
|
|
|
if (GetDeviceCaps(hdc, LOGPIXELSY) >= 120)
|
|
fFlags |= TBBF_LARGE;
|
|
|
|
ReleaseDC(NULL, hdc);
|
|
|
|
return fFlags;
|
|
}
|
|
#endif
|
|
|
|
case TB_SETINDENT:
|
|
ptb->xFirstButton = wParam;
|
|
InvalidateRect (hwnd, NULL, TRUE);
|
|
FlushToolTipsMgr(ptb);
|
|
return 1;
|
|
|
|
case TB_SETMAXTEXTROWS:
|
|
ptb->nTextRows = wParam;
|
|
TBRecalc(ptb);
|
|
return 1;
|
|
|
|
case TB_GETTEXTROWS:
|
|
return ptb->nTextRows;
|
|
|
|
case TB_HITTEST:
|
|
return TBHitTest(ptb, ((LPPOINT)lParam)->x, ((LPPOINT)lParam)->y);
|
|
|
|
default:
|
|
DoDefault:
|
|
return DefWindowProc(hwnd, wMsg, wParam, lParam);
|
|
}
|
|
|
|
return 0L;
|
|
}
|