wine/dlls/comctl32/toolbar.c
2023-08-30 21:05:53 +02:00

7103 lines
211 KiB
C

/*
* Toolbar control
*
* Copyright 1998,1999 Eric Kohl
* Copyright 2000 Eric Kohl for CodeWeavers
* Copyright 2004 Robert Shearman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* NOTES
*
* This code was audited for completeness against the documented features
* of Comctl32.dll version 6.0 on Mar. 14, 2004, by Robert Shearman.
*
* Unless otherwise noted, we believe this code to be complete, as per
* the specification mentioned above.
* If you discover missing features or bugs please note them below.
*
* TODO:
* - Styles:
* - TBSTYLE_REGISTERDROP
* - TBSTYLE_EX_DOUBLEBUFFER
* - Messages:
* - TB_GETMETRICS
* - TB_GETOBJECT
* - TB_INSERTMARKHITTEST
* - TB_SAVERESTORE
* - TB_SETMETRICS
* - WM_WININICHANGE
* - Notifications:
* - NM_CHAR
* - TBN_GETOBJECT
* - TBN_SAVE
* - Button wrapping (under construction).
* - Fix TB_SETROWS and Separators.
* - iListGap custom draw support.
*
* Testing:
* - Run tests using Waite Group Windows95 API Bible Volume 2.
* The second cdrom contains executables addstr.exe, btncount.exe,
* btnstate.exe, butstrsz.exe, chkbtn.exe, chngbmp.exe, customiz.exe,
* enablebtn.exe, getbmp.exe, getbtn.exe, getflags.exe, hidebtn.exe,
* indetbtn.exe, insbtn.exe, pressbtn.exe, setbtnsz.exe, setcmdid.exe,
* setparnt.exe, setrows.exe, toolwnd.exe.
* - Microsoft's controlspy examples.
* - Charles Petzold's 'Programming Windows': gadgets.exe
*
* Differences between MSDN and actual native control operation:
* 1. MSDN says: "TBSTYLE_LIST: Creates a flat toolbar with button text
* to the right of the bitmap. Otherwise, this style is
* identical to TBSTYLE_FLAT."
* As implemented by both v4.71 and v5.80 of the native COMCTL32.DLL
* you can create a TBSTYLE_LIST without TBSTYLE_FLAT and the result
* is non-flat non-transparent buttons. Therefore TBSTYLE_LIST does
* *not* imply TBSTYLE_FLAT as documented. (GA 8/2001)
*
*/
#include <stdarg.h>
#include <string.h>
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "commctrl.h"
#include "comctl32.h"
#include "uxtheme.h"
#include "vssym32.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(toolbar);
static HCURSOR hCursorDrag = NULL;
typedef struct
{
INT iBitmap;
INT idCommand;
BYTE fsState;
BYTE fsStyle;
BOOL bHot;
BOOL bDropDownPressed;
DWORD_PTR dwData;
INT_PTR iString;
INT nRow;
RECT rect;
INT cx; /* manually set size */
} TBUTTON_INFO;
typedef struct
{
UINT nButtons;
HINSTANCE hInst;
UINT nID;
} TBITMAP_INFO;
typedef struct
{
HIMAGELIST himl;
INT id;
} IMLENTRY, *PIMLENTRY;
typedef struct
{
DWORD dwStructSize; /* size of TBBUTTON struct */
RECT client_rect;
RECT rcBound; /* bounding rectangle */
INT nButtonHeight;
INT nButtonWidth;
INT nBitmapHeight;
INT nBitmapWidth;
INT nIndent;
INT nRows; /* number of button rows */
INT nMaxTextRows; /* maximum number of text rows */
INT cxMin; /* minimum button width */
INT cxMax; /* maximum button width */
INT nNumButtons; /* number of buttons */
INT nNumBitmaps; /* number of bitmaps */
INT nNumStrings; /* number of strings */
INT nNumBitmapInfos;
INT nButtonDown; /* toolbar button being pressed or -1 if none */
INT nButtonDrag; /* toolbar button being dragged or -1 if none */
INT nOldHit;
INT nHotItem; /* index of the "hot" item */
SIZE szPadding; /* padding values around button */
INT iTopMargin; /* the top margin */
INT iListGap; /* default gap between text and image for toolbar with list style */
HFONT hDefaultFont;
HFONT hFont; /* text font */
HTHEME hTheme; /* theme */
HIMAGELIST himlInt; /* image list created internally */
PIMLENTRY *himlDef; /* default image list array */
INT cimlDef; /* default image list array count */
PIMLENTRY *himlHot; /* hot image list array */
INT cimlHot; /* hot image list array count */
PIMLENTRY *himlDis; /* disabled image list array */
INT cimlDis; /* disabled image list array count */
HWND hwndToolTip; /* handle to tool tip control */
HWND hwndNotify; /* handle to the window that gets notifications */
HWND hwndSelf; /* my own handle */
BOOL bAnchor; /* anchor highlight enabled */
BOOL bDoRedraw; /* Redraw status */
BOOL bDragOutSent; /* has TBN_DRAGOUT notification been sent for this drag? */
BOOL bUnicode; /* Notifications are ANSI (FALSE) or Unicode (TRUE)? */
BOOL bCaptured; /* mouse captured? */
DWORD dwStyle; /* regular toolbar style */
DWORD dwExStyle; /* extended toolbar style */
DWORD dwDTFlags; /* DrawText flags */
COLORREF clrInsertMark; /* insert mark color */
COLORREF clrBtnHighlight; /* color for Flat Separator */
COLORREF clrBtnShadow; /* color for Flag Separator */
INT iVersion;
LPWSTR pszTooltipText; /* temporary store for a string > 80 characters
* for TTN_GETDISPINFOW notification */
TBINSERTMARK tbim; /* info on insertion mark */
TBUTTON_INFO *buttons; /* pointer to button array */
LPWSTR *strings; /* pointer to string array */
TBITMAP_INFO *bitmaps;
} TOOLBAR_INFO, *PTOOLBAR_INFO;
/* used by customization dialog */
typedef struct
{
PTOOLBAR_INFO tbInfo;
HWND tbHwnd;
} CUSTDLG_INFO, *PCUSTDLG_INFO;
typedef struct
{
TBBUTTON btn;
BOOL bVirtual;
BOOL bRemovable;
WCHAR text[64];
} CUSTOMBUTTON, *PCUSTOMBUTTON;
typedef enum
{
IMAGE_LIST_DEFAULT,
IMAGE_LIST_HOT,
IMAGE_LIST_DISABLED
} IMAGE_LIST_TYPE;
#define SEPARATOR_WIDTH 8
#define TOP_BORDER 2
#define BOTTOM_BORDER 2
#define DDARROW_WIDTH 11
#define ARROW_HEIGHT 3
#define INSERTMARK_WIDTH 2
#define DEFPAD_CX 7
#define DEFPAD_CY 6
#define DEFLISTGAP 4
/* vertical padding used in list mode when image is present */
#define LISTPAD_CY 9
/* how wide to treat the bitmap if it isn't present */
#define NONLIST_NOTEXT_OFFSET 2
#define TOOLBAR_NOWHERE (-1)
/* Used to find undocumented extended styles */
#define TBSTYLE_EX_ALL (TBSTYLE_EX_DRAWDDARROWS | \
TBSTYLE_EX_VERTICAL | \
TBSTYLE_EX_MIXEDBUTTONS | \
TBSTYLE_EX_DOUBLEBUFFER | \
TBSTYLE_EX_HIDECLIPPEDBUTTONS)
/* all of the CCS_ styles */
#define COMMON_STYLES (CCS_TOP|CCS_NOMOVEY|CCS_BOTTOM|CCS_NORESIZE| \
CCS_NOPARENTALIGN|CCS_ADJUSTABLE|CCS_NODIVIDER|CCS_VERT)
#define GETIBITMAP(infoPtr, i) (infoPtr->iVersion >= 5 ? LOWORD(i) : i)
#define GETHIMLID(infoPtr, i) (infoPtr->iVersion >= 5 ? HIWORD(i) : 0)
#define GETDEFIMAGELIST(infoPtr, id) TOOLBAR_GetImageList(infoPtr->himlDef, infoPtr->cimlDef, id)
#define GETHOTIMAGELIST(infoPtr, id) TOOLBAR_GetImageList(infoPtr->himlHot, infoPtr->cimlHot, id)
#define GETDISIMAGELIST(infoPtr, id) TOOLBAR_GetImageList(infoPtr->himlDis, infoPtr->cimlDis, id)
static const WCHAR themeClass[] = L"Toolbar";
static BOOL TOOLBAR_GetButtonInfo(const TOOLBAR_INFO *infoPtr, NMTOOLBARW *nmtb);
static BOOL TOOLBAR_IsButtonRemovable(const TOOLBAR_INFO *infoPtr, int iItem, const CUSTOMBUTTON *btnInfo);
static HIMAGELIST TOOLBAR_GetImageList(const PIMLENTRY *pies, INT cies, INT id);
static PIMLENTRY TOOLBAR_GetImageListEntry(const PIMLENTRY *pies, INT cies, INT id);
static VOID TOOLBAR_DeleteImageList(PIMLENTRY **pies, INT *cies);
static HIMAGELIST TOOLBAR_InsertImageList(PIMLENTRY **pies, INT *cies, HIMAGELIST himl, INT id);
static LRESULT TOOLBAR_LButtonDown(TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam);
static void TOOLBAR_LayoutToolbar(TOOLBAR_INFO *infoPtr);
static LRESULT TOOLBAR_AutoSize(TOOLBAR_INFO *infoPtr);
static void TOOLBAR_CheckImageListIconSize(TOOLBAR_INFO *infoPtr);
static void TOOLBAR_TooltipAddTool(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *button);
static void TOOLBAR_TooltipSetRect(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *button);
static LRESULT TOOLBAR_SetButtonInfo(TOOLBAR_INFO *infoPtr, INT Id,
const TBBUTTONINFOW *lptbbi, BOOL isW);
static inline int default_top_margin(const TOOLBAR_INFO *infoPtr)
{
return (infoPtr->dwStyle & TBSTYLE_FLAT ? 0 : TOP_BORDER);
}
static inline BOOL TOOLBAR_HasDropDownArrows(DWORD exStyle)
{
return (exStyle & TBSTYLE_EX_DRAWDDARROWS) != 0;
}
static inline BOOL button_has_ddarrow(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *btnPtr)
{
return (TOOLBAR_HasDropDownArrows( infoPtr->dwExStyle ) && (btnPtr->fsStyle & BTNS_DROPDOWN)) ||
(btnPtr->fsStyle & BTNS_WHOLEDROPDOWN);
}
static LPWSTR
TOOLBAR_GetText(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *btnPtr)
{
LPWSTR lpText = NULL;
/* NOTE: iString == -1 is undocumented */
if (!IS_INTRESOURCE(btnPtr->iString) && (btnPtr->iString != -1))
lpText = (LPWSTR)btnPtr->iString;
else if ((btnPtr->iString >= 0) && (btnPtr->iString < infoPtr->nNumStrings))
lpText = infoPtr->strings[btnPtr->iString];
return lpText;
}
static void
TOOLBAR_DumpTBButton(const TBBUTTON *tbb, BOOL fUnicode)
{
TRACE("TBBUTTON: id %d, bitmap=%d, state=%02x, style=%02x, data=%p, stringid=%p (%s)\n", tbb->idCommand,
tbb->iBitmap, tbb->fsState, tbb->fsStyle, (void *)tbb->dwData, (void *)tbb->iString,
tbb->iString != -1 ? (fUnicode ? debugstr_w((LPWSTR)tbb->iString) : debugstr_a((LPSTR)tbb->iString)) : "");
}
static void
TOOLBAR_DumpButton(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *bP, INT btn_num)
{
TRACE("button %d id %d, bitmap=%d, state=%02x, style=%02x, data=%Ix, stringid=%Ix\n",
btn_num, bP->idCommand, GETIBITMAP(infoPtr, bP->iBitmap), bP->fsState, bP->fsStyle, bP->dwData, bP->iString);
TRACE("string %s\n", debugstr_w(TOOLBAR_GetText(infoPtr,bP)));
TRACE("button %d id %d, hot=%s, row=%d, rect=(%s)\n", btn_num, bP->idCommand, (bP->bHot) ? "TRUE":"FALSE", bP->nRow,
wine_dbgstr_rect(&bP->rect));
}
static void
TOOLBAR_DumpToolbar(const TOOLBAR_INFO *iP, INT line)
{
INT i;
if (!TRACE_ON(toolbar))
return;
TRACE("toolbar %p at line %d, exStyle=%#lx, buttons=%d, bitmaps=%d, strings=%d, style=%#lx\n",
iP->hwndSelf, line, iP->dwExStyle, iP->nNumButtons, iP->nNumBitmaps, iP->nNumStrings, iP->dwStyle);
TRACE("toolbar %p at line %d, himlInt=%p, himlDef=%p, himlHot=%p, himlDis=%p, redrawable=%s\n",
iP->hwndSelf, line, iP->himlInt, iP->himlDef, iP->himlHot, iP->himlDis, (iP->bDoRedraw) ? "TRUE" : "FALSE");
for (i = 0; i < iP->nNumButtons; ++i)
TOOLBAR_DumpButton(iP, &iP->buttons[i], i);
}
static inline BOOL
TOOLBAR_ButtonHasString(const TBUTTON_INFO *btnPtr)
{
return HIWORD(btnPtr->iString) && btnPtr->iString != -1;
}
static void set_string_index( TBUTTON_INFO *btn, INT_PTR str, BOOL unicode )
{
if (!IS_INTRESOURCE( str ) && str != -1)
{
if (!TOOLBAR_ButtonHasString( btn )) btn->iString = 0;
if (unicode)
Str_SetPtrW( (WCHAR **)&btn->iString, (WCHAR *)str );
else
Str_SetPtrAtoW( (WCHAR **)&btn->iString, (char *)str );
}
else
{
if (TOOLBAR_ButtonHasString( btn )) Free( (WCHAR *)btn->iString );
btn->iString = str;
}
}
static void set_stringT( TBUTTON_INFO *btn, const WCHAR *str, BOOL unicode )
{
if (IS_INTRESOURCE( (DWORD_PTR)str ) || (DWORD_PTR)str == -1) return;
set_string_index( btn, (DWORD_PTR)str, unicode );
}
static void free_string( TBUTTON_INFO *btn )
{
set_string_index( btn, 0, TRUE );
}
/***********************************************************************
* TOOLBAR_CheckStyle
*
* This function validates that the styles set are implemented and
* issues FIXMEs warning of possible problems. In a perfect world this
* function should be null.
*/
static void
TOOLBAR_CheckStyle (const TOOLBAR_INFO *infoPtr)
{
if (infoPtr->dwStyle & TBSTYLE_REGISTERDROP)
FIXME("[%p] TBSTYLE_REGISTERDROP not implemented\n", infoPtr->hwndSelf);
}
static INT
TOOLBAR_SendNotify (NMHDR *nmhdr, const TOOLBAR_INFO *infoPtr, UINT code)
{
if(!IsWindow(infoPtr->hwndSelf))
return 0; /* we have just been destroyed */
nmhdr->idFrom = GetDlgCtrlID (infoPtr->hwndSelf);
nmhdr->hwndFrom = infoPtr->hwndSelf;
nmhdr->code = code;
TRACE("to window %p, code=%08x, %s\n", infoPtr->hwndNotify, code,
(infoPtr->bUnicode) ? "via Unicode" : "via ANSI");
return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmhdr->idFrom, (LPARAM)nmhdr);
}
/***********************************************************************
* TOOLBAR_GetBitmapIndex
*
* This function returns the bitmap index associated with a button.
* If the button specifies I_IMAGECALLBACK, then the TBN_GETDISPINFO
* is issued to retrieve the index.
*/
static INT
TOOLBAR_GetBitmapIndex(const TOOLBAR_INFO *infoPtr, TBUTTON_INFO *btnPtr)
{
INT ret = btnPtr->iBitmap;
if (ret == I_IMAGECALLBACK)
{
/* issue TBN_GETDISPINFO */
NMTBDISPINFOW nmgd;
memset(&nmgd, 0, sizeof(nmgd));
nmgd.idCommand = btnPtr->idCommand;
nmgd.lParam = btnPtr->dwData;
nmgd.dwMask = TBNF_IMAGE;
nmgd.iImage = -1;
/* Windows also send TBN_GETDISPINFOW even if the control is ANSI */
TOOLBAR_SendNotify(&nmgd.hdr, infoPtr, TBN_GETDISPINFOW);
if (nmgd.dwMask & TBNF_DI_SETITEM)
btnPtr->iBitmap = nmgd.iImage;
ret = nmgd.iImage;
TRACE("TBN_GETDISPINFO returned bitmap id %d, mask %#lx, nNumBitmaps %d\n",
ret, nmgd.dwMask, infoPtr->nNumBitmaps);
}
if (ret != I_IMAGENONE)
ret = GETIBITMAP(infoPtr, ret);
return ret;
}
static BOOL
TOOLBAR_IsValidBitmapIndex(const TOOLBAR_INFO *infoPtr, INT index)
{
HIMAGELIST himl;
INT id = GETHIMLID(infoPtr, index);
INT iBitmap = GETIBITMAP(infoPtr, index);
if (((himl = GETDEFIMAGELIST(infoPtr, id)) &&
iBitmap >= 0 && iBitmap < ImageList_GetImageCount(himl)) ||
(index == I_IMAGECALLBACK))
return TRUE;
else
return FALSE;
}
static inline BOOL
TOOLBAR_IsValidImageList(const TOOLBAR_INFO *infoPtr, INT index)
{
HIMAGELIST himl = GETDEFIMAGELIST(infoPtr, GETHIMLID(infoPtr, index));
return (himl != NULL) && (ImageList_GetImageCount(himl) > 0);
}
/***********************************************************************
* TOOLBAR_GetImageListForDrawing
*
* This function validates the bitmap index (including I_IMAGECALLBACK
* functionality) and returns the corresponding image list.
*/
static HIMAGELIST
TOOLBAR_GetImageListForDrawing (const TOOLBAR_INFO *infoPtr, TBUTTON_INFO *btnPtr,
IMAGE_LIST_TYPE imagelist, INT * index)
{
HIMAGELIST himl;
if (!TOOLBAR_IsValidBitmapIndex(infoPtr,btnPtr->iBitmap)) {
if (btnPtr->iBitmap == I_IMAGENONE) return NULL;
WARN("bitmap for ID %d, index %d is not valid, number of bitmaps in imagelist: %d\n",
HIWORD(btnPtr->iBitmap), LOWORD(btnPtr->iBitmap), infoPtr->nNumBitmaps);
return NULL;
}
if ((*index = TOOLBAR_GetBitmapIndex(infoPtr, btnPtr)) < 0) {
if ((*index == I_IMAGECALLBACK) ||
(*index == I_IMAGENONE)) return NULL;
ERR("TBN_GETDISPINFO returned invalid index %d\n",
*index);
return NULL;
}
switch(imagelist)
{
case IMAGE_LIST_DEFAULT:
himl = GETDEFIMAGELIST(infoPtr, GETHIMLID(infoPtr, btnPtr->iBitmap));
break;
case IMAGE_LIST_HOT:
himl = GETHOTIMAGELIST(infoPtr, GETHIMLID(infoPtr, btnPtr->iBitmap));
break;
case IMAGE_LIST_DISABLED:
himl = GETDISIMAGELIST(infoPtr, GETHIMLID(infoPtr, btnPtr->iBitmap));
break;
default:
himl = NULL;
FIXME("Shouldn't reach here\n");
}
if (!himl)
TRACE("no image list\n");
return himl;
}
static void
TOOLBAR_DrawFlatSeparator (const RECT *lpRect, HDC hdc, const TOOLBAR_INFO *infoPtr)
{
RECT myrect;
COLORREF oldcolor, newcolor;
myrect.left = (lpRect->left + lpRect->right) / 2 - 1;
myrect.right = myrect.left + 1;
myrect.top = lpRect->top + 2;
myrect.bottom = lpRect->bottom - 2;
newcolor = (infoPtr->clrBtnShadow == CLR_DEFAULT) ?
comctl32_color.clrBtnShadow : infoPtr->clrBtnShadow;
oldcolor = SetBkColor (hdc, newcolor);
ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
myrect.left = myrect.right;
myrect.right = myrect.left + 1;
newcolor = (infoPtr->clrBtnHighlight == CLR_DEFAULT) ?
comctl32_color.clrBtnHighlight : infoPtr->clrBtnHighlight;
SetBkColor (hdc, newcolor);
ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
SetBkColor (hdc, oldcolor);
}
/***********************************************************************
* TOOLBAR_DrawFlatHorizontalSeparator
*
* This function draws horizontal separator for toolbars having CCS_VERT style.
* In this case, the separator is a pixel high line of COLOR_BTNSHADOW,
* followed by a pixel high line of COLOR_BTNHIGHLIGHT. These separators
* are horizontal as opposed to the vertical separators for not dropdown
* type.
*
* FIXME: It is possible that the height of each line is really SM_CYBORDER.
*/
static void
TOOLBAR_DrawFlatHorizontalSeparator (const RECT *lpRect, HDC hdc,
const TOOLBAR_INFO *infoPtr)
{
RECT myrect;
COLORREF oldcolor, newcolor;
myrect.left = lpRect->left;
myrect.right = lpRect->right;
myrect.top = lpRect->top + (lpRect->bottom - lpRect->top - 2)/2;
myrect.bottom = myrect.top + 1;
InflateRect (&myrect, -2, 0);
TRACE("rect=(%s)\n", wine_dbgstr_rect(&myrect));
newcolor = (infoPtr->clrBtnShadow == CLR_DEFAULT) ?
comctl32_color.clrBtnShadow : infoPtr->clrBtnShadow;
oldcolor = SetBkColor (hdc, newcolor);
ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
myrect.top = myrect.bottom;
myrect.bottom = myrect.top + 1;
newcolor = (infoPtr->clrBtnHighlight == CLR_DEFAULT) ?
comctl32_color.clrBtnHighlight : infoPtr->clrBtnHighlight;
SetBkColor (hdc, newcolor);
ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
SetBkColor (hdc, oldcolor);
}
static void
TOOLBAR_DrawArrow (HDC hdc, INT left, INT top, COLORREF clr)
{
INT x, y;
HPEN hPen, hOldPen;
if (!(hPen = CreatePen( PS_SOLID, 1, clr))) return;
hOldPen = SelectObject ( hdc, hPen );
x = left + 2;
y = top;
MoveToEx (hdc, x, y, NULL);
LineTo (hdc, x+5, y++); x++;
MoveToEx (hdc, x, y, NULL);
LineTo (hdc, x+3, y++); x++;
MoveToEx (hdc, x, y, NULL);
LineTo (hdc, x+1, y);
SelectObject( hdc, hOldPen );
DeleteObject( hPen );
}
/*
* Draw the text string for this button.
* note: infoPtr->himlDis *SHOULD* be non-zero when infoPtr->himlDef
* is non-zero, so we can simply check himlDef to see if we have
* an image list
*/
static void
TOOLBAR_DrawString (const TOOLBAR_INFO *infoPtr, RECT *rcText, LPCWSTR lpText,
const NMTBCUSTOMDRAW *tbcd, DWORD dwItemCDFlag)
{
HDC hdc = tbcd->nmcd.hdc;
HFONT hOldFont = 0;
COLORREF clrOld = 0;
COLORREF clrOldBk = 0;
int oldBkMode = 0;
UINT state = tbcd->nmcd.uItemState;
/* draw text */
if (lpText && infoPtr->nMaxTextRows > 0) {
TRACE("string=%s rect=(%s)\n", debugstr_w(lpText),
wine_dbgstr_rect(rcText));
hOldFont = SelectObject (hdc, infoPtr->hFont);
if ((state & CDIS_HOT) && (dwItemCDFlag & TBCDRF_HILITEHOTTRACK )) {
clrOld = SetTextColor (hdc, tbcd->clrTextHighlight);
}
else if (state & CDIS_DISABLED) {
clrOld = SetTextColor (hdc, tbcd->clrBtnHighlight);
OffsetRect (rcText, 1, 1);
DrawTextW (hdc, lpText, -1, rcText, infoPtr->dwDTFlags);
SetTextColor (hdc, comctl32_color.clr3dShadow);
OffsetRect (rcText, -1, -1);
}
else if (state & CDIS_INDETERMINATE) {
clrOld = SetTextColor (hdc, comctl32_color.clr3dShadow);
}
else if ((state & CDIS_MARKED) && !(dwItemCDFlag & TBCDRF_NOMARK)) {
clrOld = SetTextColor (hdc, tbcd->clrTextHighlight);
clrOldBk = SetBkColor (hdc, tbcd->clrMark);
oldBkMode = SetBkMode (hdc, tbcd->nHLStringBkMode);
}
else {
clrOld = SetTextColor (hdc, tbcd->clrText);
}
DrawTextW (hdc, lpText, -1, rcText, infoPtr->dwDTFlags);
SetTextColor (hdc, clrOld);
if ((state & CDIS_MARKED) && !(dwItemCDFlag & TBCDRF_NOMARK))
{
SetBkColor (hdc, clrOldBk);
SetBkMode (hdc, oldBkMode);
}
SelectObject (hdc, hOldFont);
}
}
static void
TOOLBAR_DrawPattern (const RECT *lpRect, const NMTBCUSTOMDRAW *tbcd)
{
HDC hdc = tbcd->nmcd.hdc;
HBRUSH hbr = SelectObject (hdc, tbcd->hbrMonoDither);
COLORREF clrTextOld;
COLORREF clrBkOld;
INT cx = lpRect->right - lpRect->left;
INT cy = lpRect->bottom - lpRect->top;
INT cxEdge = GetSystemMetrics(SM_CXEDGE);
INT cyEdge = GetSystemMetrics(SM_CYEDGE);
clrTextOld = SetTextColor(hdc, tbcd->clrBtnHighlight);
clrBkOld = SetBkColor(hdc, tbcd->clrBtnFace);
PatBlt (hdc, lpRect->left + cxEdge, lpRect->top + cyEdge,
cx - (2 * cxEdge), cy - (2 * cyEdge), PATCOPY);
SetBkColor(hdc, clrBkOld);
SetTextColor(hdc, clrTextOld);
SelectObject (hdc, hbr);
}
static void TOOLBAR_DrawMasked(HIMAGELIST himl, int index, HDC hdc, INT x, INT y, UINT draw_flags)
{
IMAGELISTDRAWPARAMS draw_params = { 0 };
INT cx, cy;
HBITMAP hbmMask, hbmImage;
HDC hdcMask, hdcImage;
ImageList_GetIconSize(himl, &cx, &cy);
draw_params.cbSize = sizeof(draw_params);
draw_params.himl = himl;
draw_params.i = index;
draw_params.hdcDst = hdc;
draw_params.x = x;
draw_params.y = y;
draw_params.cx = cx;
draw_params.cy = cy;
draw_params.rgbBk = CLR_NONE;
draw_params.rgbFg = CLR_NONE;
draw_params.fStyle = draw_flags;
draw_params.fState = ILS_NORMAL;
/* 32bpp image with alpha channel is converted to grayscale */
if (imagelist_has_alpha(himl, index))
{
draw_params.fState = ILS_SATURATE;
ImageList_DrawIndirect(&draw_params);
return;
}
/* Create src image */
hdcImage = CreateCompatibleDC(hdc);
hbmImage = CreateCompatibleBitmap(hdc, cx, cy);
SelectObject(hdcImage, hbmImage);
draw_params.x = 0;
draw_params.y = 0;
draw_params.rgbBk = RGB(0xff, 0xff, 0xff);
draw_params.rgbFg = RGB(0,0,0);
draw_params.hdcDst = hdcImage;
ImageList_DrawIndirect(&draw_params);
/* Create Mask */
hdcMask = CreateCompatibleDC(0);
hbmMask = CreateBitmap(cx, cy, 1, 1, NULL);
SelectObject(hdcMask, hbmMask);
/* Remove the background and all white pixels */
draw_params.fStyle = ILD_MASK;
draw_params.hdcDst = hdcMask;
ImageList_DrawIndirect(&draw_params);
SetBkColor(hdcImage, RGB(0xff, 0xff, 0xff));
BitBlt(hdcMask, 0, 0, cx, cy, hdcImage, 0, 0, NOTSRCERASE);
/* draw the new mask 'etched' to hdc */
SetBkColor(hdc, RGB(255, 255, 255));
SelectObject(hdc, GetSysColorBrush(COLOR_3DHILIGHT));
/* E20746 op code is (Dst ^ (Src & (Pat ^ Dst))) */
BitBlt(hdc, x + 1, y + 1, cx, cy, hdcMask, 0, 0, 0xE20746);
SelectObject(hdc, GetSysColorBrush(COLOR_3DSHADOW));
BitBlt(hdc, x, y, cx, cy, hdcMask, 0, 0, 0xE20746);
/* Cleanup */
DeleteObject(hbmImage);
DeleteDC(hdcImage);
DeleteObject (hbmMask);
DeleteDC(hdcMask);
}
static UINT
TOOLBAR_TranslateState(const TBUTTON_INFO *btnPtr, BOOL captured)
{
UINT retstate = 0;
retstate |= (btnPtr->fsState & TBSTATE_CHECKED) ? CDIS_CHECKED : 0;
retstate |= (btnPtr->fsState & TBSTATE_PRESSED) ? CDIS_SELECTED : 0;
retstate |= (btnPtr->fsState & TBSTATE_ENABLED) ? 0 : CDIS_DISABLED;
retstate |= (btnPtr->fsState & TBSTATE_MARKED ) ? CDIS_MARKED : 0;
retstate |= (btnPtr->bHot & !captured ) ? CDIS_HOT : 0;
retstate |= ((btnPtr->fsState & (TBSTATE_ENABLED|TBSTATE_INDETERMINATE)) == (TBSTATE_ENABLED|TBSTATE_INDETERMINATE)) ? CDIS_INDETERMINATE : 0;
/* NOTE: we don't set CDIS_GRAYED, CDIS_FOCUS, CDIS_DEFAULT */
return retstate;
}
/* draws the image on a toolbar button */
static void
TOOLBAR_DrawImage(const TOOLBAR_INFO *infoPtr, TBUTTON_INFO *btnPtr, INT left, INT top,
const NMTBCUSTOMDRAW *tbcd, DWORD dwItemCDFlag)
{
HIMAGELIST himl = NULL;
BOOL draw_masked = FALSE;
INT index;
INT offset = 0;
UINT draw_flags = ILD_TRANSPARENT;
if (tbcd->nmcd.uItemState & (CDIS_DISABLED | CDIS_INDETERMINATE))
{
himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_DISABLED, &index);
if (!himl)
{
himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_DEFAULT, &index);
draw_masked = TRUE;
}
}
else if (tbcd->nmcd.uItemState & CDIS_CHECKED ||
((tbcd->nmcd.uItemState & CDIS_HOT)
&& ((infoPtr->dwStyle & TBSTYLE_FLAT) || infoPtr->hTheme)))
{
/* if hot, attempt to draw with hot image list, if fails,
use default image list */
himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_HOT, &index);
if (!himl)
himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_DEFAULT, &index);
}
else
himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_DEFAULT, &index);
if (!himl)
return;
if (!(dwItemCDFlag & TBCDRF_NOOFFSET) &&
(tbcd->nmcd.uItemState & (CDIS_SELECTED | CDIS_CHECKED)))
offset = 1;
if (!(dwItemCDFlag & TBCDRF_NOMARK) &&
(tbcd->nmcd.uItemState & CDIS_MARKED))
draw_flags |= ILD_BLEND50;
TRACE("drawing index=%d, himl=%p, left=%d, top=%d, offset=%d\n",
index, himl, left, top, offset);
if (draw_masked)
TOOLBAR_DrawMasked (himl, index, tbcd->nmcd.hdc, left + offset, top + offset, draw_flags);
else
ImageList_Draw (himl, index, tbcd->nmcd.hdc, left + offset, top + offset, draw_flags);
}
/* draws a blank frame for a toolbar button */
static void
TOOLBAR_DrawFrame(const TOOLBAR_INFO *infoPtr, const NMTBCUSTOMDRAW *tbcd, const RECT *rect, DWORD dwItemCDFlag)
{
HDC hdc = tbcd->nmcd.hdc;
RECT rc = *rect;
/* if the state is disabled or indeterminate then the button
* cannot have an interactive look like pressed or hot */
BOOL non_interactive_state = (tbcd->nmcd.uItemState & CDIS_DISABLED) ||
(tbcd->nmcd.uItemState & CDIS_INDETERMINATE);
BOOL pressed_look = !non_interactive_state &&
((tbcd->nmcd.uItemState & CDIS_SELECTED) ||
(tbcd->nmcd.uItemState & CDIS_CHECKED));
/* app don't want us to draw any edges */
if (dwItemCDFlag & TBCDRF_NOEDGES)
return;
if (infoPtr->dwStyle & TBSTYLE_FLAT)
{
if (pressed_look)
DrawEdge (hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
else if ((tbcd->nmcd.uItemState & CDIS_HOT) && !non_interactive_state)
DrawEdge (hdc, &rc, BDR_RAISEDINNER, BF_RECT);
}
else
{
if (pressed_look)
DrawEdge (hdc, &rc, EDGE_SUNKEN, BF_RECT | BF_MIDDLE);
else
DrawEdge (hdc, &rc, EDGE_RAISED,
BF_SOFT | BF_RECT | BF_MIDDLE);
}
}
static void
TOOLBAR_DrawSepDDArrow(const TOOLBAR_INFO *infoPtr, const NMTBCUSTOMDRAW *tbcd, RECT *rcArrow, BOOL bDropDownPressed, DWORD dwItemCDFlag)
{
HDC hdc = tbcd->nmcd.hdc;
int offset = 0;
BOOL pressed = bDropDownPressed ||
(tbcd->nmcd.uItemState & (CDIS_SELECTED | CDIS_CHECKED));
if (infoPtr->dwStyle & TBSTYLE_FLAT)
{
if (pressed)
DrawEdge (hdc, rcArrow, BDR_SUNKENOUTER, BF_RECT);
else if ( (tbcd->nmcd.uItemState & CDIS_HOT) &&
!(tbcd->nmcd.uItemState & CDIS_DISABLED) &&
!(tbcd->nmcd.uItemState & CDIS_INDETERMINATE))
DrawEdge (hdc, rcArrow, BDR_RAISEDINNER, BF_RECT);
}
else
{
if (pressed)
DrawEdge (hdc, rcArrow, EDGE_SUNKEN, BF_RECT | BF_MIDDLE);
else
DrawEdge (hdc, rcArrow, EDGE_RAISED,
BF_SOFT | BF_RECT | BF_MIDDLE);
}
if (pressed)
offset = (dwItemCDFlag & TBCDRF_NOOFFSET) ? 0 : 1;
if (tbcd->nmcd.uItemState & (CDIS_DISABLED | CDIS_INDETERMINATE))
{
TOOLBAR_DrawArrow(hdc, rcArrow->left+1, rcArrow->top+1 + (rcArrow->bottom - rcArrow->top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnHighlight);
TOOLBAR_DrawArrow(hdc, rcArrow->left, rcArrow->top + (rcArrow->bottom - rcArrow->top - ARROW_HEIGHT) / 2, comctl32_color.clr3dShadow);
}
else
TOOLBAR_DrawArrow(hdc, rcArrow->left + offset, rcArrow->top + offset + (rcArrow->bottom - rcArrow->top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnText);
}
/* draws a complete toolbar button */
static void
TOOLBAR_DrawButton (const TOOLBAR_INFO *infoPtr, TBUTTON_INFO *btnPtr, HDC hdc, DWORD dwBaseCustDraw)
{
DWORD dwStyle = infoPtr->dwStyle;
BOOL hasDropDownArrow = button_has_ddarrow( infoPtr, btnPtr );
BOOL drawSepDropDownArrow = hasDropDownArrow &&
(~btnPtr->fsStyle & BTNS_WHOLEDROPDOWN);
RECT rc, rcArrow, rcBitmap, rcText;
LPWSTR lpText = NULL;
NMTBCUSTOMDRAW tbcd;
DWORD ntfret;
INT offset;
INT oldBkMode;
DWORD dwItemCustDraw;
DWORD dwItemCDFlag;
HTHEME theme = infoPtr->hTheme;
rc = btnPtr->rect;
rcArrow = rc;
/* separator - doesn't send NM_CUSTOMDRAW */
if (btnPtr->fsStyle & BTNS_SEP) {
if (theme)
{
DrawThemeBackground (theme, hdc,
(dwStyle & CCS_VERT) ? TP_SEPARATORVERT : TP_SEPARATOR, 0,
&rc, NULL);
}
else
/* with the FLAT style, iBitmap is the width and has already */
/* been taken into consideration in calculating the width */
/* so now we need to draw the vertical separator */
/* empirical tests show that iBitmap can/will be non-zero */
/* when drawing the vertical bar... */
if ((dwStyle & TBSTYLE_FLAT) /* && (btnPtr->iBitmap == 0) */) {
if (dwStyle & CCS_VERT) {
RECT rcsep = rc;
InflateRect(&rcsep, -infoPtr->szPadding.cx, -infoPtr->szPadding.cy);
TOOLBAR_DrawFlatHorizontalSeparator (&rcsep, hdc, infoPtr);
}
else
TOOLBAR_DrawFlatSeparator (&rc, hdc, infoPtr);
}
else if (btnPtr->fsStyle != BTNS_SEP) {
FIXME("Draw some kind of separator: fsStyle=%x\n",
btnPtr->fsStyle);
}
return;
}
/* get a pointer to the text */
lpText = TOOLBAR_GetText(infoPtr, btnPtr);
if (hasDropDownArrow)
{
int right;
if (dwStyle & TBSTYLE_FLAT)
right = max(rc.left, rc.right - DDARROW_WIDTH);
else
right = max(rc.left, rc.right - DDARROW_WIDTH - 2);
if (drawSepDropDownArrow)
rc.right = right;
rcArrow.left = right;
}
/* copy text & bitmap rects after adjusting for drop-down arrow
* so that text & bitmap is centered in the rectangle not containing
* the arrow */
rcText = rc;
rcBitmap = rc;
/* Center the bitmap horizontally and vertically */
if (dwStyle & TBSTYLE_LIST)
{
if (lpText &&
infoPtr->nMaxTextRows > 0 &&
(!(infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) ||
(btnPtr->fsStyle & BTNS_SHOWTEXT)) )
rcBitmap.left += GetSystemMetrics(SM_CXEDGE) + infoPtr->szPadding.cx / 2;
else
rcBitmap.left += GetSystemMetrics(SM_CXEDGE) + infoPtr->iListGap / 2;
}
else
rcBitmap.left += ((rc.right - rc.left) - infoPtr->nBitmapWidth) / 2;
rcBitmap.top += infoPtr->szPadding.cy / 2;
TRACE("iBitmap=%d, start=(%ld,%ld) w=%d, h=%d\n", btnPtr->iBitmap, rcBitmap.left, rcBitmap.top,
infoPtr->nBitmapWidth, infoPtr->nBitmapHeight);
TRACE("Text=%s\n", debugstr_w(lpText));
TRACE("iListGap=%d, padding = { %ld, %ld }\n", infoPtr->iListGap, infoPtr->szPadding.cx, infoPtr->szPadding.cy);
/* calculate text position */
if (lpText)
{
InflateRect(&rcText, -GetSystemMetrics(SM_CXEDGE), 0);
if (dwStyle & TBSTYLE_LIST)
{
rcText.left += infoPtr->nBitmapWidth + infoPtr->iListGap + 2;
}
else
{
if (ImageList_GetImageCount(GETDEFIMAGELIST(infoPtr, 0)) > 0)
rcText.top += infoPtr->szPadding.cy/2 + infoPtr->nBitmapHeight + 1;
else
rcText.top += infoPtr->szPadding.cy/2 + 2;
}
}
/* Initialize fields in all cases, because we use these later
* NOTE: applications can and do alter these to customize their
* toolbars */
ZeroMemory (&tbcd, sizeof(NMTBCUSTOMDRAW));
tbcd.clrText = comctl32_color.clrBtnText;
tbcd.clrTextHighlight = comctl32_color.clrHighlightText;
tbcd.clrBtnFace = comctl32_color.clrBtnFace;
tbcd.clrBtnHighlight = comctl32_color.clrBtnHighlight;
tbcd.clrMark = comctl32_color.clrHighlight;
tbcd.clrHighlightHotTrack = 0;
tbcd.nStringBkMode = TRANSPARENT;
tbcd.nHLStringBkMode = OPAQUE;
tbcd.rcText.left = 0;
tbcd.rcText.top = 0;
tbcd.rcText.right = rcText.right - rc.left;
tbcd.rcText.bottom = rcText.bottom - rc.top;
tbcd.nmcd.uItemState = TOOLBAR_TranslateState(btnPtr, infoPtr->bCaptured);
tbcd.nmcd.hdc = hdc;
tbcd.nmcd.rc = btnPtr->rect;
tbcd.hbrMonoDither = COMCTL32_hPattern55AABrush;
/* FIXME: what are these used for? */
tbcd.hbrLines = 0;
tbcd.hpenLines = 0;
/* Issue Item Prepaint notify */
dwItemCustDraw = 0;
dwItemCDFlag = 0;
if (dwBaseCustDraw & CDRF_NOTIFYITEMDRAW)
{
tbcd.nmcd.dwDrawStage = CDDS_ITEMPREPAINT;
tbcd.nmcd.dwItemSpec = btnPtr->idCommand;
tbcd.nmcd.lItemlParam = btnPtr->dwData;
ntfret = TOOLBAR_SendNotify(&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
/* reset these fields so the user can't alter the behaviour like native */
tbcd.nmcd.hdc = hdc;
tbcd.nmcd.rc = btnPtr->rect;
dwItemCustDraw = ntfret & 0xffff;
dwItemCDFlag = ntfret & 0xffff0000;
if (dwItemCustDraw & CDRF_SKIPDEFAULT)
return;
/* save the only part of the rect that the user can change */
rcText.right = tbcd.rcText.right + rc.left;
rcText.bottom = tbcd.rcText.bottom + rc.top;
}
if (!(dwItemCDFlag & TBCDRF_NOOFFSET) &&
(btnPtr->fsState & (TBSTATE_PRESSED | TBSTATE_CHECKED)))
OffsetRect(&rcText, 1, 1);
if (!theme && !(tbcd.nmcd.uItemState & CDIS_HOT) &&
((tbcd.nmcd.uItemState & CDIS_CHECKED) || (tbcd.nmcd.uItemState & CDIS_INDETERMINATE)))
TOOLBAR_DrawPattern (&rc, &tbcd);
if (((infoPtr->dwStyle & TBSTYLE_FLAT) || theme) && (tbcd.nmcd.uItemState & CDIS_HOT))
{
if ( dwItemCDFlag & TBCDRF_HILITEHOTTRACK )
{
COLORREF oldclr;
oldclr = SetBkColor(hdc, tbcd.clrHighlightHotTrack);
ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, 0);
if (hasDropDownArrow)
ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rcArrow, NULL, 0, 0);
SetBkColor(hdc, oldclr);
}
}
if (theme)
{
if (!(dwItemCDFlag & TBCDRF_NOBACKGROUND))
{
int partId = drawSepDropDownArrow ? TP_SPLITBUTTON : TP_BUTTON;
int stateId = TS_NORMAL;
if (tbcd.nmcd.uItemState & CDIS_DISABLED)
stateId = TS_DISABLED;
else if (tbcd.nmcd.uItemState & CDIS_SELECTED)
stateId = TS_PRESSED;
else if (tbcd.nmcd.uItemState & CDIS_CHECKED)
stateId = (tbcd.nmcd.uItemState & CDIS_HOT) ? TS_HOTCHECKED : TS_CHECKED;
else if ((tbcd.nmcd.uItemState & CDIS_HOT)
|| (drawSepDropDownArrow && btnPtr->bDropDownPressed))
stateId = TS_HOT;
DrawThemeBackground(theme, hdc, partId, stateId, &rc, NULL);
}
}
else
TOOLBAR_DrawFrame(infoPtr, &tbcd, &rc, dwItemCDFlag);
if (drawSepDropDownArrow)
{
if (theme)
{
int stateId = TS_NORMAL;
if (tbcd.nmcd.uItemState & CDIS_DISABLED)
stateId = TS_DISABLED;
else if (btnPtr->bDropDownPressed || (tbcd.nmcd.uItemState & CDIS_SELECTED))
stateId = TS_PRESSED;
else if (tbcd.nmcd.uItemState & CDIS_CHECKED)
stateId = (tbcd.nmcd.uItemState & CDIS_HOT) ? TS_HOTCHECKED : TS_CHECKED;
else if (tbcd.nmcd.uItemState & CDIS_HOT)
stateId = TS_HOT;
DrawThemeBackground (theme, hdc, TP_DROPDOWNBUTTON, stateId, &rcArrow, NULL);
DrawThemeBackground (theme, hdc, TP_SPLITBUTTONDROPDOWN, stateId, &rcArrow, NULL);
}
else
TOOLBAR_DrawSepDDArrow(infoPtr, &tbcd, &rcArrow, btnPtr->bDropDownPressed, dwItemCDFlag);
}
oldBkMode = SetBkMode (hdc, tbcd.nStringBkMode);
if (!(infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) || (btnPtr->fsStyle & BTNS_SHOWTEXT))
TOOLBAR_DrawString (infoPtr, &rcText, lpText, &tbcd, dwItemCDFlag);
SetBkMode (hdc, oldBkMode);
TOOLBAR_DrawImage(infoPtr, btnPtr, rcBitmap.left, rcBitmap.top, &tbcd, dwItemCDFlag);
if (hasDropDownArrow && !drawSepDropDownArrow)
{
if (tbcd.nmcd.uItemState & (CDIS_DISABLED | CDIS_INDETERMINATE))
{
TOOLBAR_DrawArrow(hdc, rcArrow.left+1, rcArrow.top+1 + (rcArrow.bottom - rcArrow.top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnHighlight);
TOOLBAR_DrawArrow(hdc, rcArrow.left, rcArrow.top + (rcArrow.bottom - rcArrow.top - ARROW_HEIGHT) / 2, comctl32_color.clr3dShadow);
}
else if (tbcd.nmcd.uItemState & (CDIS_SELECTED | CDIS_CHECKED))
{
offset = (dwItemCDFlag & TBCDRF_NOOFFSET) ? 0 : 1;
TOOLBAR_DrawArrow(hdc, rcArrow.left + offset, rcArrow.top + offset + (rcArrow.bottom - rcArrow.top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnText);
}
else
TOOLBAR_DrawArrow(hdc, rcArrow.left, rcArrow.top + (rcArrow.bottom - rcArrow.top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnText);
}
if (dwItemCustDraw & CDRF_NOTIFYPOSTPAINT)
{
tbcd.nmcd.dwDrawStage = CDDS_ITEMPOSTPAINT;
TOOLBAR_SendNotify(&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
}
}
static void
TOOLBAR_Refresh (TOOLBAR_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps)
{
TBUTTON_INFO *btnPtr;
INT i;
RECT rcTemp, rcClient;
NMTBCUSTOMDRAW tbcd;
DWORD ntfret;
DWORD dwBaseCustDraw;
/* the app has told us not to redraw the toolbar */
if (!infoPtr->bDoRedraw)
return;
/* if imagelist belongs to the app, it can be changed
by the app after setting it */
if (GETDEFIMAGELIST(infoPtr, 0) != infoPtr->himlInt)
{
infoPtr->nNumBitmaps = 0;
for (i = 0; i < infoPtr->cimlDef; i++)
infoPtr->nNumBitmaps += ImageList_GetImageCount(infoPtr->himlDef[i]->himl);
}
TOOLBAR_DumpToolbar (infoPtr, __LINE__);
/* change the imagelist icon size if we manage the list and it is necessary */
TOOLBAR_CheckImageListIconSize(infoPtr);
/* Send initial notify */
ZeroMemory (&tbcd, sizeof(NMTBCUSTOMDRAW));
tbcd.nmcd.dwDrawStage = CDDS_PREPAINT;
tbcd.nmcd.hdc = hdc;
tbcd.nmcd.rc = ps->rcPaint;
ntfret = TOOLBAR_SendNotify(&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
dwBaseCustDraw = ntfret & 0xffff;
GetClientRect(infoPtr->hwndSelf, &rcClient);
/* redraw necessary buttons */
btnPtr = infoPtr->buttons;
for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++)
{
BOOL bDraw;
if (!RectVisible(hdc, &btnPtr->rect))
continue;
if (infoPtr->dwExStyle & TBSTYLE_EX_HIDECLIPPEDBUTTONS)
{
IntersectRect(&rcTemp, &rcClient, &btnPtr->rect);
bDraw = EqualRect(&rcTemp, &btnPtr->rect);
}
else
bDraw = TRUE;
bDraw &= IntersectRect(&rcTemp, &(ps->rcPaint), &(btnPtr->rect));
bDraw = (btnPtr->fsState & TBSTATE_HIDDEN) ? FALSE : bDraw;
if (bDraw)
TOOLBAR_DrawButton(infoPtr, btnPtr, hdc, dwBaseCustDraw);
}
/* draw insert mark if required */
if (infoPtr->tbim.iButton != -1)
{
RECT rcButton = infoPtr->buttons[infoPtr->tbim.iButton].rect;
RECT rcInsertMark;
rcInsertMark.top = rcButton.top;
rcInsertMark.bottom = rcButton.bottom;
if (infoPtr->tbim.dwFlags & TBIMHT_AFTER)
rcInsertMark.left = rcInsertMark.right = rcButton.right;
else
rcInsertMark.left = rcInsertMark.right = rcButton.left - INSERTMARK_WIDTH;
COMCTL32_DrawInsertMark(hdc, &rcInsertMark, infoPtr->clrInsertMark, FALSE);
}
if (dwBaseCustDraw & CDRF_NOTIFYPOSTPAINT)
{
ZeroMemory (&tbcd, sizeof(NMTBCUSTOMDRAW));
tbcd.nmcd.dwDrawStage = CDDS_POSTPAINT;
tbcd.nmcd.hdc = hdc;
tbcd.nmcd.rc = ps->rcPaint;
TOOLBAR_SendNotify(&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
}
}
/***********************************************************************
* TOOLBAR_MeasureString
*
* This function gets the width and height of a string in pixels. This
* is done first by using GetTextExtentPoint to get the basic width
* and height. The DrawText is called with DT_CALCRECT to get the exact
* width. The reason is because the text may have more than one "&" (or
* prefix characters as M$ likes to call them). The prefix character
* indicates where the underline goes, except for the string "&&" which
* is reduced to a single "&". GetTextExtentPoint does not process these
* only DrawText does. Note that the BTNS_NOPREFIX is handled here.
*/
static void
TOOLBAR_MeasureString(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *btnPtr,
HDC hdc, LPSIZE lpSize)
{
RECT myrect;
lpSize->cx = 0;
lpSize->cy = 0;
if (infoPtr->nMaxTextRows > 0 &&
!(btnPtr->fsState & TBSTATE_HIDDEN) &&
(!(infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) ||
(btnPtr->fsStyle & BTNS_SHOWTEXT)) )
{
LPWSTR lpText = TOOLBAR_GetText(infoPtr, btnPtr);
if(lpText != NULL) {
/* first get size of all the text */
GetTextExtentPoint32W (hdc, lpText, lstrlenW (lpText), lpSize);
/* feed above size into the rectangle for DrawText */
SetRect(&myrect, 0, 0, lpSize->cx, lpSize->cy);
/* Use DrawText to get true size as drawn (less pesky "&") */
DrawTextW (hdc, lpText, -1, &myrect, DT_VCENTER | DT_SINGLELINE |
DT_CALCRECT | ((btnPtr->fsStyle & BTNS_NOPREFIX) ?
DT_NOPREFIX : 0));
/* feed back to caller */
lpSize->cx = myrect.right;
lpSize->cy = myrect.bottom;
}
}
TRACE("string size %ld x %ld.\n", lpSize->cx, lpSize->cy);
}
/***********************************************************************
* TOOLBAR_CalcStrings
*
* This function walks through each string and measures it and returns
* the largest height and width to caller.
*/
static void
TOOLBAR_CalcStrings (const TOOLBAR_INFO *infoPtr, LPSIZE lpSize)
{
TBUTTON_INFO *btnPtr;
INT i;
SIZE sz;
HDC hdc;
HFONT hOldFont;
lpSize->cx = 0;
lpSize->cy = 0;
if (infoPtr->nMaxTextRows == 0)
return;
hdc = GetDC (infoPtr->hwndSelf);
hOldFont = SelectObject (hdc, infoPtr->hFont);
if (infoPtr->nNumButtons == 0 && infoPtr->nNumStrings > 0)
{
TEXTMETRICW tm;
GetTextMetricsW(hdc, &tm);
lpSize->cy = tm.tmHeight;
}
btnPtr = infoPtr->buttons;
for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++) {
if(TOOLBAR_GetText(infoPtr, btnPtr))
{
TOOLBAR_MeasureString(infoPtr, btnPtr, hdc, &sz);
if (sz.cx > lpSize->cx)
lpSize->cx = sz.cx;
if (sz.cy > lpSize->cy)
lpSize->cy = sz.cy;
}
}
SelectObject (hdc, hOldFont);
ReleaseDC (infoPtr->hwndSelf, hdc);
TRACE("max string size %ld x %ld\n", lpSize->cx, lpSize->cy);
}
/***********************************************************************
* TOOLBAR_WrapToolbar
*
* This function walks through the buttons and separators in the
* toolbar, and sets the TBSTATE_WRAP flag only on those items where
* wrapping should occur based on the width of the toolbar window.
* It does *not* calculate button placement itself. That task
* takes place in TOOLBAR_CalcToolbar. If the program wants to manage
* the toolbar wrapping on its own, it can use the TBSTYLE_WRAPABLE
* flag, and set the TBSTATE_WRAP flags manually on the appropriate items.
*
* Note: TBSTYLE_WRAPABLE or TBSTYLE_EX_VERTICAL can be used also to allow
* vertical toolbar lists.
*/
static void
TOOLBAR_WrapToolbar(TOOLBAR_INFO *infoPtr)
{
TBUTTON_INFO *btnPtr;
INT x, cx, i, j, width;
BOOL bButtonWrap;
/* When the toolbar window style is not TBSTYLE_WRAPABLE, */
/* no layout is necessary. Applications may use this style */
/* to perform their own layout on the toolbar. */
if( !(infoPtr->dwStyle & TBSTYLE_WRAPABLE) &&
!(infoPtr->dwExStyle & TBSTYLE_EX_VERTICAL) ) return;
btnPtr = infoPtr->buttons;
x = infoPtr->nIndent;
width = infoPtr->client_rect.right - infoPtr->client_rect.left;
bButtonWrap = FALSE;
TRACE("start ButtonWidth=%d, BitmapWidth=%d, width=%d, nIndent=%d\n",
infoPtr->nButtonWidth, infoPtr->nBitmapWidth, width,
infoPtr->nIndent);
for (i = 0; i < infoPtr->nNumButtons; i++ )
{
btnPtr[i].fsState &= ~TBSTATE_WRAP;
if (btnPtr[i].fsState & TBSTATE_HIDDEN)
continue;
if (btnPtr[i].cx > 0)
cx = btnPtr[i].cx;
/* horizontal separators are treated as buttons for width */
else if ((btnPtr[i].fsStyle & BTNS_SEP) &&
!(infoPtr->dwStyle & CCS_VERT))
cx = (btnPtr[i].iBitmap > 0) ? btnPtr[i].iBitmap : SEPARATOR_WIDTH;
else
cx = infoPtr->nButtonWidth;
if (!btnPtr[i].cx && button_has_ddarrow( infoPtr, btnPtr + i ))
cx += DDARROW_WIDTH;
/* Two or more adjacent separators form a separator group. */
/* The first separator in a group should be wrapped to the */
/* next row if the previous wrapping is on a button. */
if( bButtonWrap &&
(btnPtr[i].fsStyle & BTNS_SEP) &&
(i + 1 < infoPtr->nNumButtons ) &&
(btnPtr[i + 1].fsStyle & BTNS_SEP) )
{
TRACE("wrap point 1 btn %d style %02x\n", i, btnPtr[i].fsStyle);
btnPtr[i].fsState |= TBSTATE_WRAP;
x = infoPtr->nIndent;
i++;
bButtonWrap = FALSE;
continue;
}
/* The layout makes sure the bitmap is visible, but not the button. */
/* Test added to also wrap after a button that starts a row but */
/* is bigger than the area. - GA 8/01 */
if ((x + cx - (infoPtr->nButtonWidth - infoPtr->nBitmapWidth) / 2 > width) ||
((x == infoPtr->nIndent) && (cx > width)))
{
BOOL bFound = FALSE;
/* If the current button is a separator and not hidden, */
/* go to the next until it reaches a non separator. */
/* Wrap the last separator if it is before a button. */
while( ( ((btnPtr[i].fsStyle & BTNS_SEP) &&
!(btnPtr[i].fsStyle & BTNS_DROPDOWN)) ||
(btnPtr[i].fsState & TBSTATE_HIDDEN) ) &&
i < infoPtr->nNumButtons )
{
i++;
bFound = TRUE;
}
if( bFound && i < infoPtr->nNumButtons )
{
i--;
TRACE("wrap point 2 btn %d style %02x, x=%d, cx=%d\n",
i, btnPtr[i].fsStyle, x, cx);
btnPtr[i].fsState |= TBSTATE_WRAP;
x = infoPtr->nIndent;
bButtonWrap = FALSE;
continue;
}
else if ( i >= infoPtr->nNumButtons)
break;
/* If the current button is not a separator, find the last */
/* separator and wrap it. */
for ( j = i - 1; j >= 0 && !(btnPtr[j].fsState & TBSTATE_WRAP); j--)
{
if ((btnPtr[j].fsStyle & BTNS_SEP) &&
!(btnPtr[j].fsState & TBSTATE_HIDDEN))
{
bFound = TRUE;
i = j;
TRACE("wrap point 3 btn %d style %02x, x=%d, cx=%d\n",
i, btnPtr[i].fsStyle, x, cx);
x = infoPtr->nIndent;
btnPtr[j].fsState |= TBSTATE_WRAP;
bButtonWrap = FALSE;
break;
}
}
/* If no separator available for wrapping, wrap one of */
/* non-hidden previous button. */
if (!bFound)
{
for ( j = i - 1;
j >= 0 && !(btnPtr[j].fsState & TBSTATE_WRAP); j--)
{
if (btnPtr[j].fsState & TBSTATE_HIDDEN)
continue;
bFound = TRUE;
i = j;
TRACE("wrap point 4 btn %d style %02x, x=%d, cx=%d\n",
i, btnPtr[i].fsStyle, x, cx);
x = infoPtr->nIndent;
btnPtr[j].fsState |= TBSTATE_WRAP;
bButtonWrap = TRUE;
break;
}
}
/* If all above failed, wrap the current button. */
if (!bFound)
{
TRACE("wrap point 5 btn %d style %02x, x=%d, cx=%d\n",
i, btnPtr[i].fsStyle, x, cx);
btnPtr[i].fsState |= TBSTATE_WRAP;
x = infoPtr->nIndent;
if (btnPtr[i].fsStyle & BTNS_SEP )
bButtonWrap = FALSE;
else
bButtonWrap = TRUE;
}
}
else {
TRACE("wrap point 6 btn %d style %02x, x=%d, cx=%d\n",
i, btnPtr[i].fsStyle, x, cx);
x += cx;
}
}
}
/***********************************************************************
* TOOLBAR_MeasureButton
*
* Calculates the width and height required for a button. Used in
* TOOLBAR_CalcToolbar to set the all-button width and height and also for
* the width of buttons that are autosized.
*
* Note that it would have been rather elegant to use one piece of code for
* both the laying out of the toolbar and for controlling where button parts
* are drawn, but the native control has inconsistencies between the two that
* prevent this from being effectively. These inconsistencies can be seen as
* artefacts where parts of the button appear outside of the bounding button
* rectangle.
*
* There are several cases for the calculation of the button dimensions and
* button part positioning:
*
* List
* ====
*
* With Bitmap:
*
* +--------------------------------------------------------+ ^
* | ^ ^ | |
* | | pad.cy / 2 | centered | |
* | pad.cx/2 + cxedge +--------------+ +------------+ | | DEFPAD_CY +
* |<----------------->| nBitmapWidth | | Text | | | max(nBitmapHeight, szText.cy)
* | |<------------>| | | | |
* | +--------------+ +------------+ | |
* |<-------------------------------------->| | |
* | cxedge + iListGap + nBitmapWidth + 2 |<-----------> | |
* | szText.cx | |
* +--------------------------------------------------------+ -
* <-------------------------------------------------------->
* 2*cxedge + nBitmapWidth + iListGap + szText.cx + pad.cx
*
* Without Bitmap (I_IMAGENONE):
*
* +-----------------------------------+ ^
* | ^ | |
* | | centered | | LISTPAD_CY +
* | +------------+ | | szText.cy
* | | Text | | |
* | | | | |
* | +------------+ | |
* |<----------------->| | |
* | cxedge |<-----------> | |
* | szText.cx | |
* +-----------------------------------+ -
* <----------------------------------->
* szText.cx + pad.cx
*
* Without text:
*
* +--------------------------------------+ ^
* | ^ | |
* | | padding.cy/2 | | DEFPAD_CY +
* | +------------+ | | nBitmapHeight
* | | Bitmap | | |
* | | | | |
* | +------------+ | |
* |<------------------->| | |
* | cxedge + iListGap/2 |<-----------> | |
* | nBitmapWidth | |
* +--------------------------------------+ -
* <-------------------------------------->
* 2*cxedge + nBitmapWidth + iListGap
*
* Non-List
* ========
*
* With bitmap:
*
* +-----------------------------------+ ^
* | ^ | |
* | | pad.cy / 2 | | nBitmapHeight +
* | - | | szText.cy +
* | +------------+ | | DEFPAD_CY + 1
* | centered | Bitmap | | |
* |<----------------->| | | |
* | +------------+ | |
* | ^ | |
* | 1 | | |
* | - | |
* | centered +---------------+ | |
* |<--------------->| Text | | |
* | +---------------+ | |
* +-----------------------------------+ -
* <----------------------------------->
* pad.cx + max(nBitmapWidth, szText.cx)
*
* Without bitmaps (NULL imagelist or ImageList_GetImageCount() = 0):
*
* +---------------------------------------+ ^
* | ^ | |
* | | 2 + pad.cy / 2 | |
* | - | | szText.cy +
* | centered +-----------------+ | | pad.cy + 2
* |<--------------->| Text | | |
* | +-----------------+ | |
* | | |
* +---------------------------------------+ -
* <--------------------------------------->
* 2*cxedge + pad.cx + szText.cx
*
* Without text:
* As for with bitmaps, but with szText.cx zero.
*/
static inline SIZE TOOLBAR_MeasureButton(const TOOLBAR_INFO *infoPtr, SIZE sizeString,
BOOL bHasBitmap, BOOL bValidImageList)
{
SIZE sizeButton;
if (infoPtr->dwStyle & TBSTYLE_LIST)
{
/* set button height from bitmap / text height... */
sizeButton.cy = max((bHasBitmap ? infoPtr->nBitmapHeight : 0),
sizeString.cy);
/* ... add on the necessary padding */
if (bValidImageList)
{
if (bHasBitmap)
sizeButton.cy += DEFPAD_CY;
else
sizeButton.cy += LISTPAD_CY;
}
else
sizeButton.cy += infoPtr->szPadding.cy;
/* calculate button width */
sizeButton.cx = 2*GetSystemMetrics(SM_CXEDGE) +
infoPtr->nBitmapWidth + infoPtr->iListGap;
if (sizeString.cx > 0)
sizeButton.cx += sizeString.cx + infoPtr->szPadding.cx;
}
else
{
if (bHasBitmap)
{
sizeButton.cy = infoPtr->nBitmapHeight + DEFPAD_CY;
if (sizeString.cy > 0)
sizeButton.cy += 1 + sizeString.cy;
sizeButton.cx = infoPtr->szPadding.cx +
max(sizeString.cx, infoPtr->nBitmapWidth);
}
else
{
sizeButton.cy = sizeString.cy + infoPtr->szPadding.cy +
NONLIST_NOTEXT_OFFSET;
sizeButton.cx = infoPtr->szPadding.cx +
max(2*GetSystemMetrics(SM_CXEDGE) + sizeString.cx, infoPtr->nBitmapWidth);
}
}
return sizeButton;
}
/***********************************************************************
* TOOLBAR_CalcToolbar
*
* This function calculates button and separator placement. It first
* calculates the button sizes, gets the toolbar window width and then
* calls TOOLBAR_WrapToolbar to determine which buttons we need to wrap
* on. It assigns a new location to each item and sends this location to
* the tooltip window if appropriate. Finally, it updates the rcBound
* rect and calculates the new required toolbar window height.
*/
static void
TOOLBAR_CalcToolbar (TOOLBAR_INFO *infoPtr)
{
SIZE sizeString, sizeButton;
BOOL validImageList = FALSE;
TOOLBAR_CalcStrings (infoPtr, &sizeString);
TOOLBAR_DumpToolbar (infoPtr, __LINE__);
if (TOOLBAR_IsValidImageList(infoPtr, 0))
validImageList = TRUE;
sizeButton = TOOLBAR_MeasureButton(infoPtr, sizeString, TRUE, validImageList);
infoPtr->nButtonWidth = sizeButton.cx;
infoPtr->nButtonHeight = sizeButton.cy;
infoPtr->iTopMargin = default_top_margin(infoPtr);
if ( infoPtr->cxMin >= 0 && infoPtr->nButtonWidth < infoPtr->cxMin )
infoPtr->nButtonWidth = infoPtr->cxMin;
if ( infoPtr->cxMax > 0 && infoPtr->nButtonWidth > infoPtr->cxMax )
infoPtr->nButtonWidth = infoPtr->cxMax;
TOOLBAR_LayoutToolbar(infoPtr);
}
static void
TOOLBAR_LayoutToolbar(TOOLBAR_INFO *infoPtr)
{
TBUTTON_INFO *btnPtr;
SIZE sizeButton;
INT i, nRows, nSepRows;
INT x, y, cx, cy;
BOOL bWrap;
BOOL validImageList = TOOLBAR_IsValidImageList(infoPtr, 0);
TOOLBAR_WrapToolbar(infoPtr);
x = infoPtr->nIndent;
y = infoPtr->iTopMargin;
cx = infoPtr->nButtonWidth;
cy = infoPtr->nButtonHeight;
nRows = nSepRows = 0;
infoPtr->rcBound.top = y;
infoPtr->rcBound.left = x;
infoPtr->rcBound.bottom = y + cy;
infoPtr->rcBound.right = x;
btnPtr = infoPtr->buttons;
TRACE("cy=%d\n", cy);
for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++ )
{
bWrap = FALSE;
if (btnPtr->fsState & TBSTATE_HIDDEN)
{
SetRectEmpty (&btnPtr->rect);
TOOLBAR_TooltipSetRect(infoPtr, btnPtr);
continue;
}
cy = infoPtr->nButtonHeight;
if (btnPtr->fsStyle & BTNS_SEP) {
if (infoPtr->dwStyle & CCS_VERT) {
cy = (btnPtr->iBitmap > 0) ? btnPtr->iBitmap : SEPARATOR_WIDTH;
cx = (btnPtr->cx > 0) ? btnPtr->cx : infoPtr->nButtonWidth;
}
else
cx = (btnPtr->cx > 0) ? btnPtr->cx :
(btnPtr->iBitmap > 0) ? btnPtr->iBitmap : SEPARATOR_WIDTH;
}
else
{
if (btnPtr->cx)
cx = btnPtr->cx;
else if (btnPtr->fsStyle & BTNS_AUTOSIZE)
{
SIZE sz;
HDC hdc;
HFONT hOldFont;
hdc = GetDC (infoPtr->hwndSelf);
hOldFont = SelectObject (hdc, infoPtr->hFont);
TOOLBAR_MeasureString(infoPtr, btnPtr, hdc, &sz);
SelectObject (hdc, hOldFont);
ReleaseDC (infoPtr->hwndSelf, hdc);
sizeButton = TOOLBAR_MeasureButton(infoPtr, sz,
TOOLBAR_IsValidBitmapIndex(infoPtr, infoPtr->buttons[i].iBitmap),
validImageList);
cx = sizeButton.cx;
}
else
cx = infoPtr->nButtonWidth;
/* if size has been set manually then don't add on extra space
* for the drop down arrow */
if (!btnPtr->cx && button_has_ddarrow( infoPtr, btnPtr ))
cx += DDARROW_WIDTH;
}
if (btnPtr->fsState & TBSTATE_WRAP)
bWrap = TRUE;
SetRect (&btnPtr->rect, x, y, x + cx, y + cy);
if (infoPtr->rcBound.left > x)
infoPtr->rcBound.left = x;
if (infoPtr->rcBound.right < x + cx)
infoPtr->rcBound.right = x + cx;
if (infoPtr->rcBound.bottom < y + cy)
infoPtr->rcBound.bottom = y + cy;
TOOLBAR_TooltipSetRect(infoPtr, btnPtr);
/* btnPtr->nRow is zero based. The space between the rows is */
/* also considered as a row. */
btnPtr->nRow = nRows + nSepRows;
TRACE("button %d style=%x, bWrap=%d, nRows=%d, nSepRows=%d, btnrow=%d, (%d,%d)-(%d,%d)\n",
i, btnPtr->fsStyle, bWrap, nRows, nSepRows, btnPtr->nRow,
x, y, x+cx, y+cy);
if( bWrap )
{
if ( !(btnPtr->fsStyle & BTNS_SEP) )
y += cy;
else
{
if ( !(infoPtr->dwStyle & CCS_VERT))
y += cy + ( (btnPtr->cx > 0 ) ?
btnPtr->cx : SEPARATOR_WIDTH) * 2 /3;
else
y += cy;
/* nSepRows is used to calculate the extra height following */
/* the last row. */
nSepRows++;
}
x = infoPtr->nIndent;
/* Increment row number unless this is the last button */
/* and it has Wrap set. */
if (i != infoPtr->nNumButtons-1)
nRows++;
}
else
x += cx;
}
/* infoPtr->nRows is the number of rows on the toolbar */
infoPtr->nRows = nRows + nSepRows + 1;
TRACE("toolbar button width %d\n", infoPtr->nButtonWidth);
}
static INT
TOOLBAR_InternalHitTest (const TOOLBAR_INFO *infoPtr, const POINT *lpPt, BOOL *button)
{
TBUTTON_INFO *btnPtr;
INT i;
if (button)
*button = FALSE;
btnPtr = infoPtr->buttons;
for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++) {
if (btnPtr->fsState & TBSTATE_HIDDEN)
continue;
if (btnPtr->fsStyle & BTNS_SEP) {
if (PtInRect (&btnPtr->rect, *lpPt)) {
TRACE(" ON SEPARATOR %d\n", i);
return -i;
}
}
else {
if (PtInRect (&btnPtr->rect, *lpPt)) {
TRACE(" ON BUTTON %d\n", i);
if (button)
*button = TRUE;
return i;
}
}
}
TRACE(" NOWHERE\n");
return TOOLBAR_NOWHERE;
}
/* worker for TB_ADDBUTTONS and TB_INSERTBUTTON */
static BOOL
TOOLBAR_InternalInsertButtonsT(TOOLBAR_INFO *infoPtr, INT iIndex, UINT nAddButtons, const TBBUTTON *lpTbb, BOOL fUnicode)
{
INT nOldButtons, nNewButtons, iButton;
BOOL fHasString = FALSE;
if (iIndex < 0) /* iIndex can be negative, what means adding at the end */
iIndex = infoPtr->nNumButtons;
nOldButtons = infoPtr->nNumButtons;
nNewButtons = nOldButtons + nAddButtons;
infoPtr->buttons = ReAlloc(infoPtr->buttons, sizeof(TBUTTON_INFO)*nNewButtons);
memmove(&infoPtr->buttons[iIndex + nAddButtons], &infoPtr->buttons[iIndex],
(nOldButtons - iIndex) * sizeof(TBUTTON_INFO));
infoPtr->nNumButtons += nAddButtons;
/* insert new buttons data */
for (iButton = 0; iButton < nAddButtons; iButton++) {
TBUTTON_INFO *btnPtr = &infoPtr->buttons[iIndex + iButton];
INT_PTR str;
TOOLBAR_DumpTBButton(lpTbb + iButton, fUnicode);
ZeroMemory(btnPtr, sizeof(*btnPtr));
btnPtr->iBitmap = lpTbb[iButton].iBitmap;
btnPtr->idCommand = lpTbb[iButton].idCommand;
btnPtr->fsState = lpTbb[iButton].fsState;
btnPtr->fsStyle = lpTbb[iButton].fsStyle;
btnPtr->dwData = lpTbb[iButton].dwData;
if (btnPtr->fsStyle & BTNS_SEP)
str = -1;
else
str = lpTbb[iButton].iString;
set_string_index( btnPtr, str, fUnicode );
fHasString |= TOOLBAR_ButtonHasString( btnPtr );
TOOLBAR_TooltipAddTool(infoPtr, btnPtr);
}
if (infoPtr->nNumStrings > 0 || fHasString)
TOOLBAR_CalcToolbar(infoPtr);
else
TOOLBAR_LayoutToolbar(infoPtr);
TOOLBAR_AutoSize(infoPtr);
TOOLBAR_DumpToolbar(infoPtr, __LINE__);
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return TRUE;
}
static INT
TOOLBAR_GetButtonIndex (const TOOLBAR_INFO *infoPtr, INT idCommand, BOOL CommandIsIndex)
{
TBUTTON_INFO *btnPtr;
INT i;
if (CommandIsIndex) {
TRACE("command is really index command=%d\n", idCommand);
if (idCommand >= infoPtr->nNumButtons) return -1;
return idCommand;
}
btnPtr = infoPtr->buttons;
for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++) {
if (btnPtr->idCommand == idCommand) {
TRACE("command=%d index=%d\n", idCommand, i);
return i;
}
}
TRACE("no index found for command=%d\n", idCommand);
return -1;
}
static INT
TOOLBAR_GetCheckedGroupButtonIndex (const TOOLBAR_INFO *infoPtr, INT nIndex)
{
TBUTTON_INFO *btnPtr;
INT nRunIndex;
if ((nIndex < 0) || (nIndex > infoPtr->nNumButtons))
return -1;
/* check index button */
btnPtr = &infoPtr->buttons[nIndex];
if ((btnPtr->fsStyle & BTNS_CHECKGROUP) == BTNS_CHECKGROUP) {
if (btnPtr->fsState & TBSTATE_CHECKED)
return nIndex;
}
/* check previous buttons */
nRunIndex = nIndex - 1;
while (nRunIndex >= 0) {
btnPtr = &infoPtr->buttons[nRunIndex];
if ((btnPtr->fsStyle & BTNS_GROUP) == BTNS_GROUP) {
if (btnPtr->fsState & TBSTATE_CHECKED)
return nRunIndex;
}
else
break;
nRunIndex--;
}
/* check next buttons */
nRunIndex = nIndex + 1;
while (nRunIndex < infoPtr->nNumButtons) {
btnPtr = &infoPtr->buttons[nRunIndex];
if ((btnPtr->fsStyle & BTNS_GROUP) == BTNS_GROUP) {
if (btnPtr->fsState & TBSTATE_CHECKED)
return nRunIndex;
}
else
break;
nRunIndex++;
}
return -1;
}
static VOID
TOOLBAR_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
MSG msg;
msg.hwnd = hwndMsg;
msg.message = uMsg;
msg.wParam = wParam;
msg.lParam = lParam;
msg.time = GetMessageTime ();
msg.pt.x = (short)LOWORD(GetMessagePos ());
msg.pt.y = (short)HIWORD(GetMessagePos ());
SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
}
static void
TOOLBAR_TooltipAddTool(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *button)
{
if (infoPtr->hwndToolTip && !(button->fsStyle & BTNS_SEP)) {
TTTOOLINFOW ti;
ZeroMemory(&ti, sizeof(TTTOOLINFOW));
ti.cbSize = sizeof (TTTOOLINFOW);
ti.hwnd = infoPtr->hwndSelf;
ti.uId = button->idCommand;
ti.hinst = 0;
ti.lpszText = LPSTR_TEXTCALLBACKW;
/* ti.lParam = random value from the stack? */
SendMessageW(infoPtr->hwndToolTip, TTM_ADDTOOLW,
0, (LPARAM)&ti);
}
}
static void
TOOLBAR_TooltipDelTool(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *button)
{
if ((infoPtr->hwndToolTip) && !(button->fsStyle & BTNS_SEP)) {
TTTOOLINFOW ti;
ZeroMemory(&ti, sizeof(ti));
ti.cbSize = sizeof(ti);
ti.hwnd = infoPtr->hwndSelf;
ti.uId = button->idCommand;
SendMessageW(infoPtr->hwndToolTip, TTM_DELTOOLW, 0, (LPARAM)&ti);
}
}
static void TOOLBAR_TooltipSetRect(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *button)
{
/* Set the toolTip only for non-hidden, non-separator button */
if (infoPtr->hwndToolTip && !(button->fsStyle & BTNS_SEP))
{
TTTOOLINFOW ti;
ZeroMemory(&ti, sizeof(ti));
ti.cbSize = sizeof(ti);
ti.hwnd = infoPtr->hwndSelf;
ti.uId = button->idCommand;
ti.rect = button->rect;
SendMessageW(infoPtr->hwndToolTip, TTM_NEWTOOLRECTW, 0, (LPARAM)&ti);
}
}
/* Creates the tooltip control */
static void
TOOLBAR_TooltipCreateControl(TOOLBAR_INFO *infoPtr)
{
int i;
NMTOOLTIPSCREATED nmttc;
infoPtr->hwndToolTip = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, WS_POPUP,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
infoPtr->hwndSelf, 0, 0, 0);
if (!infoPtr->hwndToolTip)
return;
/* Send NM_TOOLTIPSCREATED notification */
nmttc.hwndToolTips = infoPtr->hwndToolTip;
TOOLBAR_SendNotify(&nmttc.hdr, infoPtr, NM_TOOLTIPSCREATED);
for (i = 0; i < infoPtr->nNumButtons; i++)
{
TOOLBAR_TooltipAddTool(infoPtr, &infoPtr->buttons[i]);
TOOLBAR_TooltipSetRect(infoPtr, &infoPtr->buttons[i]);
}
}
/* keeps available button list box sorted by button id */
static void TOOLBAR_Cust_InsertAvailButton(HWND hwnd, PCUSTOMBUTTON btnInfoNew)
{
int i;
int count;
PCUSTOMBUTTON btnInfo;
HWND hwndAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
TRACE("button %s, idCommand %d\n", debugstr_w(btnInfoNew->text), btnInfoNew->btn.idCommand);
count = SendMessageW(hwndAvail, LB_GETCOUNT, 0, 0);
/* position 0 is always separator */
for (i = 1; i < count; i++)
{
btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndAvail, LB_GETITEMDATA, i, 0);
if (btnInfoNew->btn.idCommand < btnInfo->btn.idCommand)
{
i = SendMessageW(hwndAvail, LB_INSERTSTRING, i, 0);
SendMessageW(hwndAvail, LB_SETITEMDATA, i, (LPARAM)btnInfoNew);
return;
}
}
/* id higher than all others add to end */
i = SendMessageW(hwndAvail, LB_ADDSTRING, 0, 0);
SendMessageW(hwndAvail, LB_SETITEMDATA, i, (LPARAM)btnInfoNew);
}
static void TOOLBAR_Cust_MoveButton(const CUSTDLG_INFO *custInfo, HWND hwnd, INT nIndexFrom, INT nIndexTo)
{
NMTOOLBARW nmtb;
TRACE("index from %d, index to %d\n", nIndexFrom, nIndexTo);
if (nIndexFrom == nIndexTo)
return;
/* MSDN states that iItem is the index of the button, rather than the
* command ID as used by every other NMTOOLBAR notification */
nmtb.iItem = nIndexFrom;
if (TOOLBAR_SendNotify(&nmtb.hdr, custInfo->tbInfo, TBN_QUERYINSERT))
{
PCUSTOMBUTTON btnInfo;
NMHDR hdr;
HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);
int count = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndList, LB_GETITEMDATA, nIndexFrom, 0);
SendMessageW(hwndList, LB_DELETESTRING, nIndexFrom, 0);
SendMessageW(hwndList, LB_INSERTSTRING, nIndexTo, 0);
SendMessageW(hwndList, LB_SETITEMDATA, nIndexTo, (LPARAM)btnInfo);
SendMessageW(hwndList, LB_SETCURSEL, nIndexTo, 0);
if (nIndexTo <= 0)
EnableWindow(GetDlgItem(hwnd,IDC_MOVEUP_BTN), FALSE);
else
EnableWindow(GetDlgItem(hwnd,IDC_MOVEUP_BTN), TRUE);
/* last item is always separator, so -2 instead of -1 */
if (nIndexTo >= (count - 2))
EnableWindow(GetDlgItem(hwnd,IDC_MOVEDN_BTN), FALSE);
else
EnableWindow(GetDlgItem(hwnd,IDC_MOVEDN_BTN), TRUE);
SendMessageW(custInfo->tbHwnd, TB_DELETEBUTTON, nIndexFrom, 0);
SendMessageW(custInfo->tbHwnd, TB_INSERTBUTTONW, nIndexTo, (LPARAM)&(btnInfo->btn));
TOOLBAR_SendNotify(&hdr, custInfo->tbInfo, TBN_TOOLBARCHANGE);
}
}
static void TOOLBAR_Cust_AddButton(const CUSTDLG_INFO *custInfo, HWND hwnd, INT nIndexAvail, INT nIndexTo)
{
NMTOOLBARW nmtb;
TRACE("Add: nIndexAvail %d, nIndexTo %d\n", nIndexAvail, nIndexTo);
/* MSDN states that iItem is the index of the button, rather than the
* command ID as used by every other NMTOOLBAR notification */
nmtb.iItem = nIndexAvail;
if (TOOLBAR_SendNotify(&nmtb.hdr, custInfo->tbInfo, TBN_QUERYINSERT))
{
PCUSTOMBUTTON btnInfo;
NMHDR hdr;
HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);
HWND hwndAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
int count = SendMessageW(hwndAvail, LB_GETCOUNT, 0, 0);
btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndAvail, LB_GETITEMDATA, nIndexAvail, 0);
if (nIndexAvail != 0) /* index == 0 indicates separator */
{
/* remove from 'available buttons' list */
SendMessageW(hwndAvail, LB_DELETESTRING, nIndexAvail, 0);
if (nIndexAvail == count-1)
SendMessageW(hwndAvail, LB_SETCURSEL, nIndexAvail-1 , 0);
else
SendMessageW(hwndAvail, LB_SETCURSEL, nIndexAvail , 0);
}
else
{
PCUSTOMBUTTON btnNew;
/* duplicate 'separator' button */
btnNew = Alloc(sizeof(CUSTOMBUTTON));
*btnNew = *btnInfo;
btnInfo = btnNew;
}
/* insert into 'toolbar button' list */
SendMessageW(hwndList, LB_INSERTSTRING, nIndexTo, 0);
SendMessageW(hwndList, LB_SETITEMDATA, nIndexTo, (LPARAM)btnInfo);
SendMessageW(custInfo->tbHwnd, TB_INSERTBUTTONW, nIndexTo, (LPARAM)&(btnInfo->btn));
TOOLBAR_SendNotify(&hdr, custInfo->tbInfo, TBN_TOOLBARCHANGE);
}
}
static void TOOLBAR_Cust_RemoveButton(const CUSTDLG_INFO *custInfo, HWND hwnd, INT index)
{
PCUSTOMBUTTON btnInfo;
HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);
TRACE("Remove: index %d\n", index);
btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndList, LB_GETITEMDATA, index, 0);
/* send TBN_QUERYDELETE notification */
if (TOOLBAR_IsButtonRemovable(custInfo->tbInfo, index, btnInfo))
{
NMHDR hdr;
SendMessageW(hwndList, LB_DELETESTRING, index, 0);
SendMessageW(hwndList, LB_SETCURSEL, index , 0);
SendMessageW(custInfo->tbHwnd, TB_DELETEBUTTON, index, 0);
/* insert into 'available button' list */
if (!(btnInfo->btn.fsStyle & BTNS_SEP))
TOOLBAR_Cust_InsertAvailButton(hwnd, btnInfo);
else
Free(btnInfo);
TOOLBAR_SendNotify(&hdr, custInfo->tbInfo, TBN_TOOLBARCHANGE);
}
}
/* drag list notification function for toolbar buttons list box */
static LRESULT TOOLBAR_Cust_ToolbarDragListNotification(const CUSTDLG_INFO *custInfo, HWND hwnd,
const DRAGLISTINFO *pDLI)
{
HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);
switch (pDLI->uNotification)
{
case DL_BEGINDRAG:
{
INT nCurrentItem = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
/* no dragging for last item (separator) */
if (nCurrentItem >= (nCount - 1)) return FALSE;
return TRUE;
}
case DL_DRAGGING:
{
INT nCurrentItem = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
/* no dragging past last item (separator) */
if ((nCurrentItem >= 0) && (nCurrentItem < (nCount - 1)))
{
DrawInsert(hwnd, hwndList, nCurrentItem);
/* FIXME: native uses "move button" cursor */
return DL_COPYCURSOR;
}
/* not over toolbar buttons list */
if (nCurrentItem < 0)
{
POINT ptWindow = pDLI->ptCursor;
HWND hwndListAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
MapWindowPoints(NULL, hwnd, &ptWindow, 1);
/* over available buttons list? */
if (ChildWindowFromPoint(hwnd, ptWindow) == hwndListAvail)
/* FIXME: native uses "move button" cursor */
return DL_COPYCURSOR;
}
/* clear drag arrow */
DrawInsert(hwnd, hwndList, -1);
return DL_STOPCURSOR;
}
case DL_DROPPED:
{
INT nIndexTo = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
INT nIndexFrom = SendMessageW(hwndList, LB_GETCURSEL, 0, 0);
INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
if ((nIndexTo >= 0) && (nIndexTo < (nCount - 1)))
{
/* clear drag arrow */
DrawInsert(hwnd, hwndList, -1);
/* move item */
TOOLBAR_Cust_MoveButton(custInfo, hwnd, nIndexFrom, nIndexTo);
}
/* not over toolbar buttons list */
if (nIndexTo < 0)
{
POINT ptWindow = pDLI->ptCursor;
HWND hwndListAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
MapWindowPoints(NULL, hwnd, &ptWindow, 1);
/* over available buttons list? */
if (ChildWindowFromPoint(hwnd, ptWindow) == hwndListAvail)
TOOLBAR_Cust_RemoveButton(custInfo, hwnd, nIndexFrom);
}
break;
}
case DL_CANCELDRAG:
/* Clear drag arrow */
DrawInsert(hwnd, hwndList, -1);
break;
}
return 0;
}
/* drag list notification function for available buttons list box */
static LRESULT TOOLBAR_Cust_AvailDragListNotification(const CUSTDLG_INFO *custInfo, HWND hwnd,
const DRAGLISTINFO *pDLI)
{
HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);
switch (pDLI->uNotification)
{
case DL_BEGINDRAG:
return TRUE;
case DL_DRAGGING:
{
INT nCurrentItem = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
/* no dragging past last item (separator) */
if ((nCurrentItem >= 0) && (nCurrentItem < nCount))
{
DrawInsert(hwnd, hwndList, nCurrentItem);
/* FIXME: native uses "move button" cursor */
return DL_COPYCURSOR;
}
/* not over toolbar buttons list */
if (nCurrentItem < 0)
{
POINT ptWindow = pDLI->ptCursor;
HWND hwndListAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
MapWindowPoints(NULL, hwnd, &ptWindow, 1);
/* over available buttons list? */
if (ChildWindowFromPoint(hwnd, ptWindow) == hwndListAvail)
/* FIXME: native uses "move button" cursor */
return DL_COPYCURSOR;
}
/* clear drag arrow */
DrawInsert(hwnd, hwndList, -1);
return DL_STOPCURSOR;
}
case DL_DROPPED:
{
INT nIndexTo = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
INT nIndexFrom = SendDlgItemMessageW(hwnd, IDC_AVAILBTN_LBOX, LB_GETCURSEL, 0, 0);
if ((nIndexTo >= 0) && (nIndexTo < nCount))
{
/* clear drag arrow */
DrawInsert(hwnd, hwndList, -1);
/* add item */
TOOLBAR_Cust_AddButton(custInfo, hwnd, nIndexFrom, nIndexTo);
}
}
case DL_CANCELDRAG:
/* Clear drag arrow */
DrawInsert(hwnd, hwndList, -1);
break;
}
return 0;
}
extern UINT uDragListMessage;
/***********************************************************************
* TOOLBAR_CustomizeDialogProc
* This function implements the toolbar customization dialog.
*/
static INT_PTR CALLBACK
TOOLBAR_CustomizeDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PCUSTDLG_INFO custInfo = (PCUSTDLG_INFO)GetWindowLongPtrW (hwnd, DWLP_USER);
PCUSTOMBUTTON btnInfo;
NMTOOLBARA nmtb;
TOOLBAR_INFO *infoPtr = custInfo ? custInfo->tbInfo : NULL;
switch (uMsg)
{
case WM_INITDIALOG:
custInfo = (PCUSTDLG_INFO)lParam;
SetWindowLongPtrW (hwnd, DWLP_USER, (LONG_PTR)custInfo);
if (custInfo)
{
WCHAR Buffer[256];
int i = 0;
int index;
NMTBINITCUSTOMIZE nmtbic;
infoPtr = custInfo->tbInfo;
/* send TBN_QUERYINSERT notification */
nmtb.iItem = custInfo->tbInfo->nNumButtons;
if (!TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_QUERYINSERT))
return FALSE;
nmtbic.hwndDialog = hwnd;
/* Send TBN_INITCUSTOMIZE notification */
if (TOOLBAR_SendNotify (&nmtbic.hdr, infoPtr, TBN_INITCUSTOMIZE) ==
TBNRF_HIDEHELP)
{
TRACE("TBNRF_HIDEHELP requested\n");
ShowWindow(GetDlgItem(hwnd, IDC_HELP_BTN), SW_HIDE);
}
/* add items to 'toolbar buttons' list and check if removable */
for (i = 0; i < custInfo->tbInfo->nNumButtons; i++)
{
btnInfo = Alloc(sizeof(CUSTOMBUTTON));
memset (&btnInfo->btn, 0, sizeof(TBBUTTON));
btnInfo->btn.fsStyle = BTNS_SEP;
btnInfo->bVirtual = FALSE;
LoadStringW (COMCTL32_hModule, IDS_SEPARATOR, btnInfo->text, 64);
/* send TBN_QUERYDELETE notification */
btnInfo->bRemovable = TOOLBAR_IsButtonRemovable(infoPtr, i, btnInfo);
index = (int)SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_ADDSTRING, 0, 0);
SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_SETITEMDATA, index, (LPARAM)btnInfo);
}
SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_SETITEMHEIGHT, 0, infoPtr->nBitmapHeight + 8);
/* insert separator button into 'available buttons' list */
btnInfo = Alloc(sizeof(CUSTOMBUTTON));
memset (&btnInfo->btn, 0, sizeof(TBBUTTON));
btnInfo->btn.fsStyle = BTNS_SEP;
btnInfo->bVirtual = FALSE;
btnInfo->bRemovable = TRUE;
LoadStringW (COMCTL32_hModule, IDS_SEPARATOR, btnInfo->text, 64);
index = (int)SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_ADDSTRING, 0, (LPARAM)btnInfo);
SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_SETITEMDATA, index, (LPARAM)btnInfo);
/* insert all buttons into dsa */
for (i = 0;; i++)
{
/* send TBN_GETBUTTONINFO notification */
NMTOOLBARW nmtb;
nmtb.iItem = i;
nmtb.pszText = Buffer;
nmtb.cchText = 256;
/* Clear previous button's text */
ZeroMemory(nmtb.pszText, nmtb.cchText * sizeof(WCHAR));
if (!TOOLBAR_GetButtonInfo(infoPtr, &nmtb))
break;
TRACE("WM_INITDIALOG style: %x iItem(%d) idCommand(%d) iString(%Id) %s\n",
nmtb.tbButton.fsStyle, i,
nmtb.tbButton.idCommand,
nmtb.tbButton.iString,
nmtb.tbButton.iString >= 0 ? debugstr_w(infoPtr->strings[nmtb.tbButton.iString])
: "");
/* insert button into the appropriate list */
index = TOOLBAR_GetButtonIndex (custInfo->tbInfo, nmtb.tbButton.idCommand, FALSE);
if (index == -1)
{
btnInfo = Alloc(sizeof(CUSTOMBUTTON));
btnInfo->bVirtual = FALSE;
btnInfo->bRemovable = TRUE;
}
else
{
btnInfo = (PCUSTOMBUTTON)SendDlgItemMessageW (hwnd,
IDC_TOOLBARBTN_LBOX, LB_GETITEMDATA, index, 0);
}
btnInfo->btn = nmtb.tbButton;
if (!(nmtb.tbButton.fsStyle & BTNS_SEP))
{
if (*nmtb.pszText)
lstrcpyW(btnInfo->text, nmtb.pszText);
else if (nmtb.tbButton.iString >= 0 &&
nmtb.tbButton.iString < infoPtr->nNumStrings)
{
lstrcpyW(btnInfo->text,
infoPtr->strings[nmtb.tbButton.iString]);
}
}
if (index == -1)
TOOLBAR_Cust_InsertAvailButton(hwnd, btnInfo);
}
SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_SETITEMHEIGHT, 0, infoPtr->nBitmapHeight + 8);
/* select first item in the 'available' list */
SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_SETCURSEL, 0, 0);
/* append 'virtual' separator button to the 'toolbar buttons' list */
btnInfo = Alloc(sizeof(CUSTOMBUTTON));
memset (&btnInfo->btn, 0, sizeof(TBBUTTON));
btnInfo->btn.fsStyle = BTNS_SEP;
btnInfo->bVirtual = TRUE;
btnInfo->bRemovable = FALSE;
LoadStringW (COMCTL32_hModule, IDS_SEPARATOR, btnInfo->text, 64);
index = (int)SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_ADDSTRING, 0, (LPARAM)btnInfo);
SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_SETITEMDATA, index, (LPARAM)btnInfo);
/* select last item in the 'toolbar' list */
SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_SETCURSEL, index, 0);
SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_SETTOPINDEX, index, 0);
MakeDragList(GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX));
MakeDragList(GetDlgItem(hwnd, IDC_AVAILBTN_LBOX));
/* set focus and disable buttons */
PostMessageW (hwnd, WM_USER, 0, 0);
}
return TRUE;
case WM_USER:
EnableWindow (GetDlgItem (hwnd,IDC_MOVEUP_BTN), FALSE);
EnableWindow (GetDlgItem (hwnd,IDC_MOVEDN_BTN), FALSE);
EnableWindow (GetDlgItem (hwnd,IDC_REMOVE_BTN), FALSE);
SetFocus (GetDlgItem (hwnd, IDC_TOOLBARBTN_LBOX));
return TRUE;
case WM_CLOSE:
EndDialog(hwnd, FALSE);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_TOOLBARBTN_LBOX:
if (HIWORD(wParam) == LBN_SELCHANGE)
{
PCUSTOMBUTTON btnInfo;
NMTOOLBARA nmtb;
int count;
int index;
count = SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETCOUNT, 0, 0);
index = SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETCURSEL, 0, 0);
/* send TBN_QUERYINSERT notification */
nmtb.iItem = index;
TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_QUERYINSERT);
/* get list box item */
btnInfo = (PCUSTOMBUTTON)SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETITEMDATA, index, 0);
if (index == (count - 1))
{
/* last item (virtual separator) */
EnableWindow (GetDlgItem (hwnd,IDC_MOVEUP_BTN), FALSE);
EnableWindow (GetDlgItem (hwnd,IDC_MOVEDN_BTN), FALSE);
}
else if (index == (count - 2))
{
/* second last item (last non-virtual item) */
EnableWindow (GetDlgItem (hwnd,IDC_MOVEUP_BTN), TRUE);
EnableWindow (GetDlgItem (hwnd,IDC_MOVEDN_BTN), FALSE);
}
else if (index == 0)
{
/* first item */
EnableWindow (GetDlgItem (hwnd,IDC_MOVEUP_BTN), FALSE);
EnableWindow (GetDlgItem (hwnd,IDC_MOVEDN_BTN), TRUE);
}
else
{
EnableWindow (GetDlgItem (hwnd,IDC_MOVEUP_BTN), TRUE);
EnableWindow (GetDlgItem (hwnd,IDC_MOVEDN_BTN), TRUE);
}
EnableWindow (GetDlgItem (hwnd,IDC_REMOVE_BTN), btnInfo->bRemovable);
}
break;
case IDC_MOVEUP_BTN:
{
int index = SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETCURSEL, 0, 0);
TOOLBAR_Cust_MoveButton(custInfo, hwnd, index, index-1);
}
break;
case IDC_MOVEDN_BTN: /* move down */
{
int index = SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETCURSEL, 0, 0);
TOOLBAR_Cust_MoveButton(custInfo, hwnd, index, index+1);
}
break;
case IDC_REMOVE_BTN: /* remove button */
{
int index = SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETCURSEL, 0, 0);
if (LB_ERR == index)
break;
TOOLBAR_Cust_RemoveButton(custInfo, hwnd, index);
}
break;
case IDC_HELP_BTN:
TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_CUSTHELP);
break;
case IDC_RESET_BTN:
TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_RESET);
break;
case IDOK: /* Add button */
{
int index;
int indexto;
index = SendDlgItemMessageW(hwnd, IDC_AVAILBTN_LBOX, LB_GETCURSEL, 0, 0);
indexto = SendDlgItemMessageW(hwnd, IDC_TOOLBARBTN_LBOX, LB_GETCURSEL, 0, 0);
TOOLBAR_Cust_AddButton(custInfo, hwnd, index, indexto);
}
break;
case IDCANCEL:
EndDialog(hwnd, FALSE);
break;
}
return TRUE;
case WM_DESTROY:
{
int count;
int i;
/* delete items from 'toolbar buttons' listbox*/
count = SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETCOUNT, 0, 0);
for (i = 0; i < count; i++)
{
btnInfo = (PCUSTOMBUTTON)SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETITEMDATA, i, 0);
Free(btnInfo);
SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_SETITEMDATA, 0, 0);
}
SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_RESETCONTENT, 0, 0);
/* delete items from 'available buttons' listbox*/
count = SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_GETCOUNT, 0, 0);
for (i = 0; i < count; i++)
{
btnInfo = (PCUSTOMBUTTON)SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_GETITEMDATA, i, 0);
Free(btnInfo);
SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_SETITEMDATA, i, 0);
}
SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_RESETCONTENT, 0, 0);
}
return TRUE;
case WM_DRAWITEM:
if (wParam == IDC_AVAILBTN_LBOX || wParam == IDC_TOOLBARBTN_LBOX)
{
LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
RECT rcButton;
RECT rcText;
HPEN hPen, hOldPen;
HBRUSH hOldBrush;
COLORREF oldText = 0;
COLORREF oldBk = 0;
/* get item data */
btnInfo = (PCUSTOMBUTTON)SendDlgItemMessageW (hwnd, wParam, LB_GETITEMDATA, lpdis->itemID, 0);
if (btnInfo == NULL)
{
FIXME("btnInfo invalid\n");
return TRUE;
}
/* set colors and select objects */
oldBk = SetBkColor (lpdis->hDC, (lpdis->itemState & ODS_FOCUS)?comctl32_color.clrHighlight:comctl32_color.clrWindow);
if (btnInfo->bVirtual)
oldText = SetTextColor (lpdis->hDC, comctl32_color.clrGrayText);
else
oldText = SetTextColor (lpdis->hDC, (lpdis->itemState & ODS_FOCUS)?comctl32_color.clrHighlightText:comctl32_color.clrWindowText);
hPen = CreatePen( PS_SOLID, 1,
(lpdis->itemState & ODS_SELECTED)?comctl32_color.clrHighlight:comctl32_color.clrWindow);
hOldPen = SelectObject (lpdis->hDC, hPen );
hOldBrush = SelectObject (lpdis->hDC, GetSysColorBrush ((lpdis->itemState & ODS_FOCUS)?COLOR_HIGHLIGHT:COLOR_WINDOW));
/* fill background rectangle */
Rectangle (lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top,
lpdis->rcItem.right, lpdis->rcItem.bottom);
/* calculate button and text rectangles */
rcButton = lpdis->rcItem;
InflateRect (&rcButton, -1, -1);
rcText = rcButton;
rcButton.right = rcButton.left + custInfo->tbInfo->nBitmapWidth + 6;
rcText.left = rcButton.right + 2;
/* draw focus rectangle */
if (lpdis->itemState & ODS_FOCUS)
DrawFocusRect (lpdis->hDC, &lpdis->rcItem);
/* draw button */
if (!(infoPtr->dwStyle & TBSTYLE_FLAT))
DrawEdge (lpdis->hDC, &rcButton, EDGE_RAISED, BF_RECT|BF_MIDDLE|BF_SOFT);
/* draw image and text */
if ((btnInfo->btn.fsStyle & BTNS_SEP) == 0) {
HIMAGELIST himl = GETDEFIMAGELIST(infoPtr, GETHIMLID(infoPtr,
btnInfo->btn.iBitmap));
ImageList_Draw (himl, GETIBITMAP(infoPtr, btnInfo->btn.iBitmap),
lpdis->hDC, rcButton.left+3, rcButton.top+3, ILD_NORMAL);
}
DrawTextW (lpdis->hDC, btnInfo->text, -1, &rcText,
DT_LEFT | DT_VCENTER | DT_SINGLELINE);
/* delete objects and reset colors */
SelectObject (lpdis->hDC, hOldBrush);
SelectObject (lpdis->hDC, hOldPen);
SetBkColor (lpdis->hDC, oldBk);
SetTextColor (lpdis->hDC, oldText);
DeleteObject( hPen );
return TRUE;
}
return FALSE;
case WM_MEASUREITEM:
if (wParam == IDC_AVAILBTN_LBOX || wParam == IDC_TOOLBARBTN_LBOX)
{
MEASUREITEMSTRUCT *lpmis = (MEASUREITEMSTRUCT*)lParam;
lpmis->itemHeight = 15 + 8; /* default height */
return TRUE;
}
return FALSE;
default:
if (uDragListMessage && (uMsg == uDragListMessage))
{
if (wParam == IDC_TOOLBARBTN_LBOX)
{
LRESULT res = TOOLBAR_Cust_ToolbarDragListNotification(
custInfo, hwnd, (DRAGLISTINFO *)lParam);
SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, res);
return TRUE;
}
else if (wParam == IDC_AVAILBTN_LBOX)
{
LRESULT res = TOOLBAR_Cust_AvailDragListNotification(
custInfo, hwnd, (DRAGLISTINFO *)lParam);
SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, res);
return TRUE;
}
}
return FALSE;
}
}
static BOOL
TOOLBAR_AddBitmapToImageList(TOOLBAR_INFO *infoPtr, HIMAGELIST himlDef, const TBITMAP_INFO *bitmap)
{
HBITMAP hbmLoad;
INT nCountBefore = ImageList_GetImageCount(himlDef);
INT nCountAfter;
INT cxIcon, cyIcon;
INT nAdded;
INT nIndex;
TRACE("adding hInst=%p nID=%d nButtons=%d\n", bitmap->hInst, bitmap->nID, bitmap->nButtons);
/* Add bitmaps to the default image list */
if (bitmap->hInst == NULL) /* a handle was passed */
hbmLoad = CopyImage(ULongToHandle(bitmap->nID), IMAGE_BITMAP, 0, 0, 0);
else if (bitmap->hInst == COMCTL32_hModule)
hbmLoad = LoadImageW( bitmap->hInst, MAKEINTRESOURCEW(bitmap->nID),
IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );
else
hbmLoad = CreateMappedBitmap(bitmap->hInst, bitmap->nID, 0, NULL, 0);
/* enlarge the bitmap if needed */
ImageList_GetIconSize(himlDef, &cxIcon, &cyIcon);
if (bitmap->hInst != COMCTL32_hModule)
COMCTL32_EnsureBitmapSize(&hbmLoad, cxIcon*(INT)bitmap->nButtons, cyIcon, comctl32_color.clrBtnFace);
nIndex = ImageList_AddMasked(himlDef, hbmLoad, comctl32_color.clrBtnFace);
DeleteObject(hbmLoad);
if (nIndex == -1)
return FALSE;
nCountAfter = ImageList_GetImageCount(himlDef);
nAdded = nCountAfter - nCountBefore;
if (bitmap->nButtons == 0) /* wParam == 0 is special and means add only one image */
{
ImageList_SetImageCount(himlDef, nCountBefore + 1);
} else if (nAdded > (INT)bitmap->nButtons) {
TRACE("Added more images than wParam: Previous image number %i added %i while wParam %i. Images in list %i\n",
nCountBefore, nAdded, bitmap->nButtons, nCountAfter);
}
infoPtr->nNumBitmaps += nAdded;
return TRUE;
}
static void
TOOLBAR_CheckImageListIconSize(TOOLBAR_INFO *infoPtr)
{
HIMAGELIST himlDef;
HIMAGELIST himlNew;
INT cx, cy;
INT i;
himlDef = GETDEFIMAGELIST(infoPtr, 0);
if (himlDef == NULL || himlDef != infoPtr->himlInt)
return;
if (!ImageList_GetIconSize(himlDef, &cx, &cy))
return;
if (cx == infoPtr->nBitmapWidth && cy == infoPtr->nBitmapHeight)
return;
TRACE("Update icon size: %dx%d -> %dx%d\n",
cx, cy, infoPtr->nBitmapWidth, infoPtr->nBitmapHeight);
himlNew = ImageList_Create(infoPtr->nBitmapWidth, infoPtr->nBitmapHeight,
ILC_COLOR32|ILC_MASK, 8, 2);
for (i = 0; i < infoPtr->nNumBitmapInfos; i++)
TOOLBAR_AddBitmapToImageList(infoPtr, himlNew, &infoPtr->bitmaps[i]);
TOOLBAR_InsertImageList(&infoPtr->himlDef, &infoPtr->cimlDef, himlNew, 0);
infoPtr->himlInt = himlNew;
infoPtr->nNumBitmaps -= ImageList_GetImageCount(himlDef);
ImageList_Destroy(himlDef);
}
/***********************************************************************
* TOOLBAR_AddBitmap: Add the bitmaps to the default image list.
*
*/
static LRESULT
TOOLBAR_AddBitmap (TOOLBAR_INFO *infoPtr, INT count, const TBADDBITMAP *lpAddBmp)
{
TBITMAP_INFO info;
INT iSumButtons, i;
HIMAGELIST himlDef;
TRACE("hwnd=%p count=%d lpAddBmp=%p\n", infoPtr->hwndSelf, count, lpAddBmp);
if (!lpAddBmp)
return -1;
if (lpAddBmp->hInst == HINST_COMMCTRL)
{
info.hInst = COMCTL32_hModule;
switch (lpAddBmp->nID)
{
case IDB_STD_SMALL_COLOR:
case 2:
info.nButtons = 15;
info.nID = IDB_STD_SMALL;
break;
case IDB_STD_LARGE_COLOR:
case 3:
info.nButtons = 15;
info.nID = IDB_STD_LARGE;
break;
case IDB_VIEW_SMALL_COLOR:
case 6:
info.nButtons = 12;
info.nID = IDB_VIEW_SMALL;
break;
case IDB_VIEW_LARGE_COLOR:
case 7:
info.nButtons = 12;
info.nID = IDB_VIEW_LARGE;
break;
case IDB_HIST_SMALL_COLOR:
info.nButtons = 5;
info.nID = IDB_HIST_SMALL;
break;
case IDB_HIST_LARGE_COLOR:
info.nButtons = 5;
info.nID = IDB_HIST_LARGE;
break;
default:
WARN("unknown bitmap id, %Id\n", lpAddBmp->nID);
return -1;
}
TRACE ("adding %d internal bitmaps\n", info.nButtons);
/* Windows resize all the buttons to the size of a newly added standard image */
if (lpAddBmp->nID & 1)
{
/* large icons: 24x24. Will make the button 31x30 */
SendMessageW (infoPtr->hwndSelf, TB_SETBITMAPSIZE, 0, MAKELPARAM(24, 24));
}
else
{
/* small icons: 16x16. Will make the buttons 23x22 */
SendMessageW (infoPtr->hwndSelf, TB_SETBITMAPSIZE, 0, MAKELPARAM(16, 16));
}
TOOLBAR_CalcToolbar (infoPtr);
}
else
{
info.nButtons = count;
info.hInst = lpAddBmp->hInst;
info.nID = lpAddBmp->nID;
TRACE("adding %d bitmaps\n", info.nButtons);
}
/* check if the bitmap is already loaded and compute iSumButtons */
iSumButtons = 0;
for (i = 0; i < infoPtr->nNumBitmapInfos; i++)
{
if (infoPtr->bitmaps[i].hInst == info.hInst &&
infoPtr->bitmaps[i].nID == info.nID)
return iSumButtons;
iSumButtons += infoPtr->bitmaps[i].nButtons;
}
if (!infoPtr->cimlDef) {
/* create new default image list */
TRACE ("creating default image list\n");
himlDef = ImageList_Create (infoPtr->nBitmapWidth, infoPtr->nBitmapHeight,
ILC_COLOR32 | ILC_MASK, info.nButtons, 2);
TOOLBAR_InsertImageList(&infoPtr->himlDef, &infoPtr->cimlDef, himlDef, 0);
infoPtr->himlInt = himlDef;
}
else {
himlDef = GETDEFIMAGELIST(infoPtr, 0);
}
if (!himlDef) {
WARN("No default image list available\n");
return -1;
}
if (!TOOLBAR_AddBitmapToImageList(infoPtr, himlDef, &info))
return -1;
TRACE("Number of bitmap infos: %d\n", infoPtr->nNumBitmapInfos);
infoPtr->bitmaps = ReAlloc(infoPtr->bitmaps, (infoPtr->nNumBitmapInfos + 1) * sizeof(TBITMAP_INFO));
infoPtr->bitmaps[infoPtr->nNumBitmapInfos] = info;
infoPtr->nNumBitmapInfos++;
TRACE("Number of bitmap infos: %d\n", infoPtr->nNumBitmapInfos);
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return iSumButtons;
}
static LRESULT
TOOLBAR_AddButtonsT(TOOLBAR_INFO *infoPtr, INT nAddButtons, const TBBUTTON* lpTbb, BOOL fUnicode)
{
TRACE("adding %d buttons (unicode=%d)\n", nAddButtons, fUnicode);
return TOOLBAR_InternalInsertButtonsT(infoPtr, -1, nAddButtons, lpTbb, fUnicode);
}
static LRESULT
TOOLBAR_AddStringW (TOOLBAR_INFO *infoPtr, HINSTANCE hInstance, LPARAM lParam)
{
#define MAX_RESOURCE_STRING_LENGTH 512
BOOL fFirstString = (infoPtr->nNumStrings == 0);
INT nIndex = infoPtr->nNumStrings;
TRACE("%p, %Ix\n", hInstance, lParam);
if (IS_INTRESOURCE(lParam)) {
WCHAR szString[MAX_RESOURCE_STRING_LENGTH];
WCHAR delimiter;
WCHAR *next_delim;
HRSRC hrsrc;
WCHAR *p;
INT len;
TRACE("adding string from resource\n");
if (!hInstance) return -1;
hrsrc = FindResourceW( hInstance, MAKEINTRESOURCEW((LOWORD(lParam) >> 4) + 1),
(LPWSTR)RT_STRING );
if (!hrsrc)
{
TRACE("string not found in resources\n");
return -1;
}
len = LoadStringW (hInstance, (UINT)lParam,
szString, MAX_RESOURCE_STRING_LENGTH);
TRACE("len=%d %s\n", len, debugstr_w(szString));
if (len == 0 || len == 1)
return nIndex;
TRACE("delimiter: 0x%x\n", *szString);
delimiter = *szString;
p = szString + 1;
while ((next_delim = wcschr(p, delimiter)) != NULL) {
*next_delim = 0;
if (next_delim + 1 >= szString + len)
{
/* this may happen if delimiter == '\0' or if the last char is a
* delimiter (then it is ignored like the native does) */
break;
}
infoPtr->strings = ReAlloc(infoPtr->strings, sizeof(LPWSTR)*(infoPtr->nNumStrings+1));
Str_SetPtrW(&infoPtr->strings[infoPtr->nNumStrings], p);
infoPtr->nNumStrings++;
p = next_delim + 1;
}
}
else {
LPWSTR p = (LPWSTR)lParam;
INT len;
if (p == NULL)
return -1;
TRACE("adding string(s) from array\n");
while (*p) {
len = lstrlenW (p);
TRACE("len=%d %s\n", len, debugstr_w(p));
infoPtr->strings = ReAlloc(infoPtr->strings, sizeof(LPWSTR)*(infoPtr->nNumStrings+1));
Str_SetPtrW (&infoPtr->strings[infoPtr->nNumStrings], p);
infoPtr->nNumStrings++;
p += (len+1);
}
}
if (fFirstString)
TOOLBAR_CalcToolbar(infoPtr);
return nIndex;
}
static LRESULT
TOOLBAR_AddStringA (TOOLBAR_INFO *infoPtr, HINSTANCE hInstance, LPARAM lParam)
{
BOOL fFirstString = (infoPtr->nNumStrings == 0);
LPSTR p;
INT nIndex;
INT len;
TRACE("%p, %Ix\n", hInstance, lParam);
if (IS_INTRESOURCE(lParam)) /* load from resources */
return TOOLBAR_AddStringW(infoPtr, hInstance, lParam);
p = (LPSTR)lParam;
if (p == NULL)
return -1;
TRACE("adding string(s) from array\n");
nIndex = infoPtr->nNumStrings;
while (*p) {
len = strlen (p);
TRACE("len=%d \"%s\"\n", len, p);
infoPtr->strings = ReAlloc(infoPtr->strings, sizeof(LPWSTR)*(infoPtr->nNumStrings+1));
Str_SetPtrAtoW(&infoPtr->strings[infoPtr->nNumStrings], p);
infoPtr->nNumStrings++;
p += (len+1);
}
if (fFirstString)
TOOLBAR_CalcToolbar(infoPtr);
return nIndex;
}
static LRESULT
TOOLBAR_AutoSize (TOOLBAR_INFO *infoPtr)
{
TRACE("auto sizing, style=%#lx\n", infoPtr->dwStyle);
TRACE("nRows: %d, infoPtr->nButtonHeight: %d\n", infoPtr->nRows, infoPtr->nButtonHeight);
if (!(infoPtr->dwStyle & CCS_NORESIZE))
{
RECT window_rect, client_rect, parent_rect, border;
UINT uPosFlags = SWP_NOZORDER | SWP_NOACTIVATE;
HWND parent;
INT x, y, cx, cy;
parent = GetParent (infoPtr->hwndSelf);
if (!parent || !infoPtr->bDoRedraw)
return 0;
GetWindowRect(infoPtr->hwndSelf, &window_rect);
GetClientRect(infoPtr->hwndSelf, &client_rect);
border = window_rect;
MapWindowPoints(0, infoPtr->hwndSelf, (POINT *)&border, 2);
border.right -= border.left + client_rect.right - client_rect.left;
border.bottom -= border.top + client_rect.bottom - client_rect.top;
GetClientRect(parent, &parent_rect);
x = parent_rect.left;
y = parent_rect.top;
cy = TOP_BORDER + infoPtr->nRows * infoPtr->nButtonHeight + BOTTOM_BORDER;
cx = parent_rect.right - parent_rect.left;
if ((infoPtr->dwStyle & CCS_BOTTOM) == CCS_NOMOVEY)
{
MapWindowPoints( 0, parent, (POINT *)&window_rect, 2 );
y = window_rect.top;
}
if ((infoPtr->dwStyle & CCS_BOTTOM) == CCS_BOTTOM)
y = parent_rect.bottom - ( window_rect.bottom - window_rect.top);
if (infoPtr->dwStyle & CCS_NOPARENTALIGN)
uPosFlags |= SWP_NOMOVE;
if (!(infoPtr->dwStyle & CCS_NOMOVEY) && !(infoPtr->dwStyle & CCS_NODIVIDER))
y += GetSystemMetrics(SM_CYEDGE);
x += border.left;
y += border.top;
cx += border.right;
cy += border.bottom;
SetWindowPos(infoPtr->hwndSelf, NULL, x, y, cx, cy, uPosFlags);
}
if ((infoPtr->dwStyle & TBSTYLE_WRAPABLE) || (infoPtr->dwExStyle & TBSTYLE_EX_VERTICAL))
{
TOOLBAR_LayoutToolbar(infoPtr);
InvalidateRect( infoPtr->hwndSelf, NULL, TRUE );
}
return 0;
}
static inline LRESULT
TOOLBAR_ButtonCount (const TOOLBAR_INFO *infoPtr)
{
return infoPtr->nNumButtons;
}
static inline LRESULT
TOOLBAR_ButtonStructSize (TOOLBAR_INFO *infoPtr, DWORD Size)
{
infoPtr->dwStructSize = Size;
return 0;
}
static LRESULT
TOOLBAR_ChangeBitmap (TOOLBAR_INFO *infoPtr, INT Id, INT Index)
{
TBUTTON_INFO *btnPtr;
INT nIndex;
TRACE("button %d, iBitmap now %d\n", Id, Index);
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return FALSE;
btnPtr = &infoPtr->buttons[nIndex];
btnPtr->iBitmap = Index;
/* we HAVE to erase the background, the new bitmap could be */
/* transparent */
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
return TRUE;
}
static LRESULT
TOOLBAR_CheckButton (TOOLBAR_INFO *infoPtr, INT Id, LPARAM lParam)
{
TBUTTON_INFO *btnPtr;
INT nIndex;
INT nOldIndex = -1;
BOOL bChecked = FALSE;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
TRACE("hwnd %p, btn index %d, lParam %Ix\n", infoPtr->hwndSelf, nIndex, lParam);
if (nIndex == -1)
return FALSE;
btnPtr = &infoPtr->buttons[nIndex];
bChecked = (btnPtr->fsState & TBSTATE_CHECKED) != 0;
if (!LOWORD(lParam))
btnPtr->fsState &= ~TBSTATE_CHECKED;
else {
if (btnPtr->fsStyle & BTNS_GROUP) {
nOldIndex =
TOOLBAR_GetCheckedGroupButtonIndex (infoPtr, nIndex);
if (nOldIndex == nIndex)
return 0;
if (nOldIndex != -1)
infoPtr->buttons[nOldIndex].fsState &= ~TBSTATE_CHECKED;
}
btnPtr->fsState |= TBSTATE_CHECKED;
}
if( bChecked != LOWORD(lParam) )
{
if (nOldIndex != -1)
InvalidateRect(infoPtr->hwndSelf, &infoPtr->buttons[nOldIndex].rect, TRUE);
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
}
/* FIXME: Send a WM_NOTIFY?? */
return TRUE;
}
static LRESULT
TOOLBAR_CommandToIndex (const TOOLBAR_INFO *infoPtr, INT Id)
{
return TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
}
static LRESULT
TOOLBAR_Customize (TOOLBAR_INFO *infoPtr)
{
CUSTDLG_INFO custInfo;
LRESULT ret;
NMHDR nmhdr;
custInfo.tbInfo = infoPtr;
custInfo.tbHwnd = infoPtr->hwndSelf;
/* send TBN_BEGINADJUST notification */
TOOLBAR_SendNotify (&nmhdr, infoPtr, TBN_BEGINADJUST);
ret = DialogBoxParamW (COMCTL32_hModule, MAKEINTRESOURCEW(IDD_TBCUSTOMIZE),
infoPtr->hwndSelf, TOOLBAR_CustomizeDialogProc, (LPARAM)&custInfo);
/* send TBN_ENDADJUST notification */
TOOLBAR_SendNotify (&nmhdr, infoPtr, TBN_ENDADJUST);
return ret;
}
static LRESULT
TOOLBAR_DeleteButton (TOOLBAR_INFO *infoPtr, INT nIndex)
{
NMTOOLBARW nmtb;
TBUTTON_INFO *btnPtr = &infoPtr->buttons[nIndex];
if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
return FALSE;
memset(&nmtb, 0, sizeof(nmtb));
nmtb.iItem = btnPtr->idCommand;
nmtb.tbButton.iBitmap = btnPtr->iBitmap;
nmtb.tbButton.idCommand = btnPtr->idCommand;
nmtb.tbButton.fsState = btnPtr->fsState;
nmtb.tbButton.fsStyle = btnPtr->fsStyle;
nmtb.tbButton.dwData = btnPtr->dwData;
nmtb.tbButton.iString = btnPtr->iString;
TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_DELETINGBUTTON);
TOOLBAR_TooltipDelTool(infoPtr, &infoPtr->buttons[nIndex]);
infoPtr->nHotItem = -1;
if (infoPtr->nNumButtons == 1) {
TRACE(" simple delete\n");
free_string( infoPtr->buttons );
Free (infoPtr->buttons);
infoPtr->buttons = NULL;
infoPtr->nNumButtons = 0;
}
else {
TBUTTON_INFO *oldButtons = infoPtr->buttons;
TRACE("complex delete [nIndex=%d]\n", nIndex);
infoPtr->nNumButtons--;
infoPtr->buttons = Alloc (sizeof (TBUTTON_INFO) * infoPtr->nNumButtons);
if (nIndex > 0) {
memcpy (&infoPtr->buttons[0], &oldButtons[0],
nIndex * sizeof(TBUTTON_INFO));
}
if (nIndex < infoPtr->nNumButtons) {
memcpy (&infoPtr->buttons[nIndex], &oldButtons[nIndex+1],
(infoPtr->nNumButtons - nIndex) * sizeof(TBUTTON_INFO));
}
free_string( oldButtons + nIndex );
Free (oldButtons);
}
TOOLBAR_LayoutToolbar(infoPtr);
InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
return TRUE;
}
static LRESULT
TOOLBAR_EnableButton (TOOLBAR_INFO *infoPtr, INT Id, LPARAM lParam)
{
TBUTTON_INFO *btnPtr;
INT nIndex;
DWORD bState;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
TRACE("hwnd %p, btn id %d, lParam %Ix\n", infoPtr->hwndSelf, Id, lParam);
if (nIndex == -1)
return FALSE;
btnPtr = &infoPtr->buttons[nIndex];
bState = btnPtr->fsState & TBSTATE_ENABLED;
/* update the toolbar button state */
if(!LOWORD(lParam)) {
btnPtr->fsState &= ~(TBSTATE_ENABLED | TBSTATE_PRESSED);
} else {
btnPtr->fsState |= TBSTATE_ENABLED;
}
/* redraw the button only if the state of the button changed */
if(bState != (btnPtr->fsState & TBSTATE_ENABLED))
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
return TRUE;
}
static inline LRESULT
TOOLBAR_GetAnchorHighlight (const TOOLBAR_INFO *infoPtr)
{
return infoPtr->bAnchor;
}
static LRESULT
TOOLBAR_GetBitmap (const TOOLBAR_INFO *infoPtr, INT Id)
{
INT nIndex;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return -1;
return infoPtr->buttons[nIndex].iBitmap;
}
static inline LRESULT
TOOLBAR_GetBitmapFlags (void)
{
return (GetDeviceCaps (0, LOGPIXELSX) >= 120) ? TBBF_LARGE : 0;
}
static LRESULT
TOOLBAR_GetButton (const TOOLBAR_INFO *infoPtr, INT nIndex, TBBUTTON *lpTbb)
{
TBUTTON_INFO *btnPtr;
if (lpTbb == NULL)
return FALSE;
if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
return FALSE;
btnPtr = &infoPtr->buttons[nIndex];
lpTbb->iBitmap = btnPtr->iBitmap;
lpTbb->idCommand = btnPtr->idCommand;
lpTbb->fsState = btnPtr->fsState;
lpTbb->fsStyle = btnPtr->fsStyle;
lpTbb->bReserved[0] = 0;
lpTbb->bReserved[1] = 0;
lpTbb->dwData = btnPtr->dwData;
lpTbb->iString = btnPtr->iString;
return TRUE;
}
static LRESULT
TOOLBAR_GetButtonInfoT(const TOOLBAR_INFO *infoPtr, INT Id, LPTBBUTTONINFOW lpTbInfo, BOOL bUnicode)
{
/* TBBUTTONINFOW and TBBUTTONINFOA have the same layout*/
TBUTTON_INFO *btnPtr;
INT nIndex;
if (lpTbInfo == NULL)
return -1;
/* MSDN documents an iImageLabel field added in Vista but it is not present in
* the headers and tests shows that even with comctl 6 Vista accepts only the
* original TBBUTTONINFO size
*/
if (lpTbInfo->cbSize != sizeof(TBBUTTONINFOW))
{
WARN("Invalid button size\n");
return -1;
}
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, lpTbInfo->dwMask & TBIF_BYINDEX);
if (nIndex == -1)
return -1;
btnPtr = &infoPtr->buttons[nIndex];
if (lpTbInfo->dwMask & TBIF_COMMAND)
lpTbInfo->idCommand = btnPtr->idCommand;
if (lpTbInfo->dwMask & TBIF_IMAGE)
lpTbInfo->iImage = btnPtr->iBitmap;
if (lpTbInfo->dwMask & TBIF_LPARAM)
lpTbInfo->lParam = btnPtr->dwData;
if (lpTbInfo->dwMask & TBIF_SIZE)
/* tests show that for separators TBIF_SIZE returns not calculated width,
but cx property, that differs from 0 only if application have
specifically set it */
lpTbInfo->cx = (btnPtr->fsStyle & BTNS_SEP)
? btnPtr->cx : (WORD)(btnPtr->rect.right - btnPtr->rect.left);
if (lpTbInfo->dwMask & TBIF_STATE)
lpTbInfo->fsState = btnPtr->fsState;
if (lpTbInfo->dwMask & TBIF_STYLE)
lpTbInfo->fsStyle = btnPtr->fsStyle;
if (lpTbInfo->dwMask & TBIF_TEXT) {
/* TB_GETBUTTONINFO doesn't retrieve text from the string list, so we
can't use TOOLBAR_GetText here */
if (!IS_INTRESOURCE(btnPtr->iString) && (btnPtr->iString != -1)) {
LPWSTR lpText = (LPWSTR)btnPtr->iString;
if (bUnicode)
Str_GetPtrW(lpText, lpTbInfo->pszText, lpTbInfo->cchText);
else
Str_GetPtrWtoA(lpText, (LPSTR)lpTbInfo->pszText, lpTbInfo->cchText);
} else if (!bUnicode || lpTbInfo->pszText)
lpTbInfo->pszText[0] = '\0';
}
return nIndex;
}
static inline LRESULT
TOOLBAR_GetButtonSize (const TOOLBAR_INFO *infoPtr)
{
return MAKELONG((WORD)infoPtr->nButtonWidth,
(WORD)infoPtr->nButtonHeight);
}
static LRESULT
TOOLBAR_GetButtonText (const TOOLBAR_INFO *infoPtr, INT Id, LPWSTR lpStr, BOOL isW)
{
INT nIndex;
LPWSTR lpText;
LRESULT ret = 0;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return -1;
lpText = TOOLBAR_GetText(infoPtr,&infoPtr->buttons[nIndex]);
if (isW)
{
if (lpText)
{
ret = lstrlenW (lpText);
if (lpStr) lstrcpyW (lpStr, lpText);
}
}
else
ret = WideCharToMultiByte( CP_ACP, 0, lpText, -1,
(LPSTR)lpStr, lpStr ? 0x7fffffff : 0, NULL, NULL ) - 1;
return ret;
}
static LRESULT
TOOLBAR_GetDisabledImageList (const TOOLBAR_INFO *infoPtr, WPARAM wParam)
{
TRACE("hwnd %p, wParam %Id\n", infoPtr->hwndSelf, wParam);
/* UNDOCUMENTED: wParam is actually the ID of the image list to return */
return (LRESULT)GETDISIMAGELIST(infoPtr, wParam);
}
static inline LRESULT
TOOLBAR_GetExtendedStyle (const TOOLBAR_INFO *infoPtr)
{
TRACE("\n");
return infoPtr->dwExStyle;
}
static LRESULT
TOOLBAR_GetHotImageList (const TOOLBAR_INFO *infoPtr, WPARAM wParam)
{
TRACE("hwnd %p, wParam %Id\n", infoPtr->hwndSelf, wParam);
/* UNDOCUMENTED: wParam is actually the ID of the image list to return */
return (LRESULT)GETHOTIMAGELIST(infoPtr, wParam);
}
static LRESULT
TOOLBAR_GetHotItem (const TOOLBAR_INFO *infoPtr)
{
if (!((infoPtr->dwStyle & TBSTYLE_FLAT) || infoPtr->hTheme))
return -1;
if (infoPtr->nHotItem < 0)
return -1;
return (LRESULT)infoPtr->nHotItem;
}
static LRESULT
TOOLBAR_GetDefImageList (const TOOLBAR_INFO *infoPtr, WPARAM wParam)
{
TRACE("hwnd %p, wParam %Id\n", infoPtr->hwndSelf, wParam);
/* UNDOCUMENTED: wParam is actually the ID of the image list to return */
return (LRESULT) GETDEFIMAGELIST(infoPtr, wParam);
}
static LRESULT
TOOLBAR_GetInsertMark (const TOOLBAR_INFO *infoPtr, TBINSERTMARK *lptbim)
{
TRACE("hwnd = %p, lptbim = %p\n", infoPtr->hwndSelf, lptbim);
*lptbim = infoPtr->tbim;
return 0;
}
static inline LRESULT
TOOLBAR_GetInsertMarkColor (const TOOLBAR_INFO *infoPtr)
{
TRACE("hwnd = %p\n", infoPtr->hwndSelf);
return (LRESULT)infoPtr->clrInsertMark;
}
static LRESULT
TOOLBAR_GetItemRect (const TOOLBAR_INFO *infoPtr, INT nIndex, LPRECT lpRect)
{
TBUTTON_INFO *btnPtr;
btnPtr = &infoPtr->buttons[nIndex];
if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
return FALSE;
if (lpRect == NULL)
return FALSE;
if (btnPtr->fsState & TBSTATE_HIDDEN)
return FALSE;
lpRect->left = btnPtr->rect.left;
lpRect->right = btnPtr->rect.right;
lpRect->bottom = btnPtr->rect.bottom;
lpRect->top = btnPtr->rect.top;
return TRUE;
}
static LRESULT
TOOLBAR_GetMaxSize (const TOOLBAR_INFO *infoPtr, LPSIZE lpSize)
{
if (lpSize == NULL)
return FALSE;
lpSize->cx = infoPtr->rcBound.right - infoPtr->rcBound.left;
lpSize->cy = infoPtr->rcBound.bottom - infoPtr->rcBound.top;
TRACE("maximum size %ld x %ld\n", infoPtr->rcBound.right - infoPtr->rcBound.left,
infoPtr->rcBound.bottom - infoPtr->rcBound.top);
return TRUE;
}
/* << TOOLBAR_GetObject >> */
static inline LRESULT
TOOLBAR_GetPadding (const TOOLBAR_INFO *infoPtr)
{
return MAKELONG(infoPtr->szPadding.cx, infoPtr->szPadding.cy);
}
static LRESULT
TOOLBAR_GetRect (const TOOLBAR_INFO *infoPtr, INT Id, LPRECT lpRect)
{
TBUTTON_INFO *btnPtr;
INT nIndex;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
btnPtr = &infoPtr->buttons[nIndex];
if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
return FALSE;
if (lpRect == NULL)
return FALSE;
lpRect->left = btnPtr->rect.left;
lpRect->right = btnPtr->rect.right;
lpRect->bottom = btnPtr->rect.bottom;
lpRect->top = btnPtr->rect.top;
return TRUE;
}
static inline LRESULT
TOOLBAR_GetRows (const TOOLBAR_INFO *infoPtr)
{
return infoPtr->nRows;
}
static LRESULT
TOOLBAR_GetState (const TOOLBAR_INFO *infoPtr, INT Id)
{
INT nIndex;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return -1;
return infoPtr->buttons[nIndex].fsState;
}
static inline LRESULT
TOOLBAR_GetStyle (const TOOLBAR_INFO *infoPtr)
{
return infoPtr->dwStyle;
}
static inline LRESULT
TOOLBAR_GetTextRows (const TOOLBAR_INFO *infoPtr)
{
return infoPtr->nMaxTextRows;
}
static LRESULT
TOOLBAR_GetToolTips (TOOLBAR_INFO *infoPtr)
{
if ((infoPtr->dwStyle & TBSTYLE_TOOLTIPS) && (infoPtr->hwndToolTip == NULL))
TOOLBAR_TooltipCreateControl(infoPtr);
return (LRESULT)infoPtr->hwndToolTip;
}
static LRESULT
TOOLBAR_GetUnicodeFormat (const TOOLBAR_INFO *infoPtr)
{
TRACE("%s hwnd=%p\n",
infoPtr->bUnicode ? "TRUE" : "FALSE", infoPtr->hwndSelf);
return infoPtr->bUnicode;
}
static inline LRESULT
TOOLBAR_GetVersion (const TOOLBAR_INFO *infoPtr)
{
return infoPtr->iVersion;
}
static LRESULT
TOOLBAR_HideButton (TOOLBAR_INFO *infoPtr, INT Id, BOOL fHide)
{
TBUTTON_INFO *btnPtr;
BYTE oldState;
INT nIndex;
TRACE("\n");
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return FALSE;
btnPtr = &infoPtr->buttons[nIndex];
oldState = btnPtr->fsState;
if (fHide)
btnPtr->fsState |= TBSTATE_HIDDEN;
else
btnPtr->fsState &= ~TBSTATE_HIDDEN;
if (oldState != btnPtr->fsState) {
TOOLBAR_LayoutToolbar (infoPtr);
InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
}
return TRUE;
}
static inline LRESULT
TOOLBAR_HitTest (const TOOLBAR_INFO *infoPtr, const POINT* lpPt)
{
return TOOLBAR_InternalHitTest (infoPtr, lpPt, NULL);
}
static LRESULT
TOOLBAR_Indeterminate (const TOOLBAR_INFO *infoPtr, INT Id, BOOL fIndeterminate)
{
TBUTTON_INFO *btnPtr;
INT nIndex;
DWORD oldState;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return FALSE;
btnPtr = &infoPtr->buttons[nIndex];
oldState = btnPtr->fsState;
if (fIndeterminate)
btnPtr->fsState |= TBSTATE_INDETERMINATE;
else
btnPtr->fsState &= ~TBSTATE_INDETERMINATE;
if(oldState != btnPtr->fsState)
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
return TRUE;
}
static LRESULT
TOOLBAR_InsertButtonT(TOOLBAR_INFO *infoPtr, INT nIndex, const TBBUTTON *lpTbb, BOOL fUnicode)
{
if (lpTbb == NULL)
return FALSE;
if (nIndex == -1) {
/* EPP: this seems to be an undocumented call (from my IE4)
* I assume in that case that:
* - index of insertion is at the end of existing buttons
* I only see this happen with nIndex == -1, but it could have a special
* meaning (like -nIndex (or ~nIndex) to get the real position of insertion).
*/
nIndex = infoPtr->nNumButtons;
} else if (nIndex < 0)
return FALSE;
TRACE("inserting button index=%d\n", nIndex);
if (nIndex > infoPtr->nNumButtons) {
nIndex = infoPtr->nNumButtons;
TRACE("adjust index=%d\n", nIndex);
}
return TOOLBAR_InternalInsertButtonsT(infoPtr, nIndex, 1, lpTbb, fUnicode);
}
/* << TOOLBAR_InsertMarkHitTest >> */
static LRESULT
TOOLBAR_IsButtonChecked (const TOOLBAR_INFO *infoPtr, INT Id)
{
INT nIndex;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return -1;
return (infoPtr->buttons[nIndex].fsState & TBSTATE_CHECKED);
}
static LRESULT
TOOLBAR_IsButtonEnabled (const TOOLBAR_INFO *infoPtr, INT Id)
{
INT nIndex;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return -1;
return (infoPtr->buttons[nIndex].fsState & TBSTATE_ENABLED);
}
static LRESULT
TOOLBAR_IsButtonHidden (const TOOLBAR_INFO *infoPtr, INT Id)
{
INT nIndex;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return -1;
return (infoPtr->buttons[nIndex].fsState & TBSTATE_HIDDEN);
}
static LRESULT
TOOLBAR_IsButtonHighlighted (const TOOLBAR_INFO *infoPtr, INT Id)
{
INT nIndex;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return -1;
return (infoPtr->buttons[nIndex].fsState & TBSTATE_MARKED);
}
static LRESULT
TOOLBAR_IsButtonIndeterminate (const TOOLBAR_INFO *infoPtr, INT Id)
{
INT nIndex;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return -1;
return (infoPtr->buttons[nIndex].fsState & TBSTATE_INDETERMINATE);
}
static LRESULT
TOOLBAR_IsButtonPressed (const TOOLBAR_INFO *infoPtr, INT Id)
{
INT nIndex;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return -1;
return (infoPtr->buttons[nIndex].fsState & TBSTATE_PRESSED);
}
static LRESULT
TOOLBAR_LoadImages (TOOLBAR_INFO *infoPtr, WPARAM wParam, HINSTANCE hInstance)
{
TBADDBITMAP tbab;
tbab.hInst = hInstance;
tbab.nID = wParam;
TRACE("hwnd = %p, hInst = %p, nID = %Iu\n", infoPtr->hwndSelf, tbab.hInst, tbab.nID);
return TOOLBAR_AddBitmap(infoPtr, 0, &tbab);
}
static LRESULT
TOOLBAR_MapAccelerator (const TOOLBAR_INFO *infoPtr, WCHAR wAccel, UINT *pIDButton)
{
WCHAR wszAccel[] = {'&',wAccel,0};
int i;
TRACE("hwnd = %p, wAccel = %x(%s), pIDButton = %p\n",
infoPtr->hwndSelf, wAccel, debugstr_wn(&wAccel,1), pIDButton);
for (i = 0; i < infoPtr->nNumButtons; i++)
{
TBUTTON_INFO *btnPtr = infoPtr->buttons+i;
if (!(btnPtr->fsStyle & BTNS_NOPREFIX) &&
!(btnPtr->fsState & TBSTATE_HIDDEN))
{
int iLen = lstrlenW(wszAccel);
LPCWSTR lpszStr = TOOLBAR_GetText(infoPtr, btnPtr);
if (!lpszStr)
continue;
while (*lpszStr)
{
if ((lpszStr[0] == '&') && (lpszStr[1] == '&'))
{
lpszStr += 2;
continue;
}
if (!wcsnicmp(lpszStr, wszAccel, iLen))
{
*pIDButton = btnPtr->idCommand;
return TRUE;
}
lpszStr++;
}
}
}
return FALSE;
}
static LRESULT
TOOLBAR_MarkButton (const TOOLBAR_INFO *infoPtr, INT Id, BOOL fMark)
{
INT nIndex;
DWORD oldState;
TBUTTON_INFO *btnPtr;
TRACE("hwnd = %p, Id = %d, fMark = 0%d\n", infoPtr->hwndSelf, Id, fMark);
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return FALSE;
btnPtr = &infoPtr->buttons[nIndex];
oldState = btnPtr->fsState;
if (fMark)
btnPtr->fsState |= TBSTATE_MARKED;
else
btnPtr->fsState &= ~TBSTATE_MARKED;
if(oldState != btnPtr->fsState)
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
return TRUE;
}
/* fixes up an index of a button affected by a move */
static inline void TOOLBAR_MoveFixupIndex(INT* pIndex, INT nIndex, INT nMoveIndex, BOOL bMoveUp)
{
if (bMoveUp)
{
if (*pIndex > nIndex && *pIndex <= nMoveIndex)
(*pIndex)--;
else if (*pIndex == nIndex)
*pIndex = nMoveIndex;
}
else
{
if (*pIndex >= nMoveIndex && *pIndex < nIndex)
(*pIndex)++;
else if (*pIndex == nIndex)
*pIndex = nMoveIndex;
}
}
static LRESULT
TOOLBAR_MoveButton (TOOLBAR_INFO *infoPtr, INT Id, INT nMoveIndex)
{
INT nIndex;
INT nCount;
TBUTTON_INFO button;
TRACE("hwnd=%p, Id=%d, nMoveIndex=%d\n", infoPtr->hwndSelf, Id, nMoveIndex);
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, TRUE);
if ((nIndex == -1) || (nMoveIndex < 0))
return FALSE;
if (nMoveIndex > infoPtr->nNumButtons - 1)
nMoveIndex = infoPtr->nNumButtons - 1;
button = infoPtr->buttons[nIndex];
/* move button right */
if (nIndex < nMoveIndex)
{
nCount = nMoveIndex - nIndex;
memmove(&infoPtr->buttons[nIndex], &infoPtr->buttons[nIndex+1], nCount*sizeof(TBUTTON_INFO));
infoPtr->buttons[nMoveIndex] = button;
TOOLBAR_MoveFixupIndex(&infoPtr->nButtonDown, nIndex, nMoveIndex, TRUE);
TOOLBAR_MoveFixupIndex(&infoPtr->nButtonDrag, nIndex, nMoveIndex, TRUE);
TOOLBAR_MoveFixupIndex(&infoPtr->nOldHit, nIndex, nMoveIndex, TRUE);
TOOLBAR_MoveFixupIndex(&infoPtr->nHotItem, nIndex, nMoveIndex, TRUE);
}
else if (nIndex > nMoveIndex) /* move button left */
{
nCount = nIndex - nMoveIndex;
memmove(&infoPtr->buttons[nMoveIndex+1], &infoPtr->buttons[nMoveIndex], nCount*sizeof(TBUTTON_INFO));
infoPtr->buttons[nMoveIndex] = button;
TOOLBAR_MoveFixupIndex(&infoPtr->nButtonDown, nIndex, nMoveIndex, FALSE);
TOOLBAR_MoveFixupIndex(&infoPtr->nButtonDrag, nIndex, nMoveIndex, FALSE);
TOOLBAR_MoveFixupIndex(&infoPtr->nOldHit, nIndex, nMoveIndex, FALSE);
TOOLBAR_MoveFixupIndex(&infoPtr->nHotItem, nIndex, nMoveIndex, FALSE);
}
TOOLBAR_LayoutToolbar(infoPtr);
TOOLBAR_AutoSize(infoPtr);
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return TRUE;
}
static LRESULT
TOOLBAR_PressButton (const TOOLBAR_INFO *infoPtr, INT Id, BOOL fPress)
{
TBUTTON_INFO *btnPtr;
INT nIndex;
DWORD oldState;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return FALSE;
btnPtr = &infoPtr->buttons[nIndex];
oldState = btnPtr->fsState;
if (fPress)
btnPtr->fsState |= TBSTATE_PRESSED;
else
btnPtr->fsState &= ~TBSTATE_PRESSED;
if(oldState != btnPtr->fsState)
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
return TRUE;
}
/* FIXME: there might still be some confusion her between number of buttons
* and number of bitmaps */
static LRESULT
TOOLBAR_ReplaceBitmap (TOOLBAR_INFO *infoPtr, const TBREPLACEBITMAP *lpReplace)
{
HBITMAP hBitmap;
int i = 0, nOldButtons = 0, pos = 0;
int nOldBitmaps, nNewBitmaps = 0;
HIMAGELIST himlDef = 0;
TRACE("hInstOld %p nIDOld %Ix hInstNew %p nIDNew %Ix nButtons %x\n",
lpReplace->hInstOld, lpReplace->nIDOld, lpReplace->hInstNew, lpReplace->nIDNew,
lpReplace->nButtons);
if (lpReplace->hInstOld == HINST_COMMCTRL)
{
FIXME("changing standard bitmaps not implemented\n");
return FALSE;
}
else if (lpReplace->hInstOld != 0 && lpReplace->hInstOld != lpReplace->hInstNew)
FIXME("resources not in the current module not implemented\n");
TRACE("To be replaced hInstOld %p nIDOld %Ix\n", lpReplace->hInstOld, lpReplace->nIDOld);
for (i = 0; i < infoPtr->nNumBitmapInfos; i++) {
TBITMAP_INFO *tbi = &infoPtr->bitmaps[i];
TRACE("tbimapinfo %d hInstOld %p nIDOld %x\n", i, tbi->hInst, tbi->nID);
if (tbi->hInst == lpReplace->hInstOld && tbi->nID == lpReplace->nIDOld)
{
TRACE("Found: nButtons %d hInst %p nID %x\n", tbi->nButtons, tbi->hInst, tbi->nID);
nOldButtons = tbi->nButtons;
tbi->nButtons = lpReplace->nButtons;
tbi->hInst = lpReplace->hInstNew;
tbi->nID = lpReplace->nIDNew;
TRACE("tbimapinfo changed %d hInstOld %p nIDOld %x\n", i, tbi->hInst, tbi->nID);
break;
}
pos += tbi->nButtons;
}
if (nOldButtons == 0)
{
WARN("No hinst/bitmap found! hInst %p nID %Ix\n", lpReplace->hInstOld, lpReplace->nIDOld);
return FALSE;
}
/* copy the bitmap before adding it as ImageList_AddMasked modifies the
* bitmap
*/
if (lpReplace->hInstNew)
hBitmap = LoadBitmapW(lpReplace->hInstNew,(LPWSTR)lpReplace->nIDNew);
else
hBitmap = CopyImage((HBITMAP)lpReplace->nIDNew, IMAGE_BITMAP, 0, 0, 0);
himlDef = GETDEFIMAGELIST(infoPtr, 0); /* fixme: correct? */
nOldBitmaps = ImageList_GetImageCount(himlDef);
/* ImageList_Replace(GETDEFIMAGELIST(), pos, hBitmap, NULL); */
for (i = pos + nOldBitmaps - 1; i >= pos; i--)
ImageList_Remove(himlDef, i);
if (hBitmap)
{
ImageList_AddMasked (himlDef, hBitmap, comctl32_color.clrBtnFace);
nNewBitmaps = ImageList_GetImageCount(himlDef);
DeleteObject(hBitmap);
}
infoPtr->nNumBitmaps = infoPtr->nNumBitmaps - nOldBitmaps + nNewBitmaps;
TRACE(" pos %d %d old bitmaps replaced by %d new ones.\n",
pos, nOldBitmaps, nNewBitmaps);
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return TRUE;
}
/* helper for TOOLBAR_SaveRestoreW */
static BOOL
TOOLBAR_Save(TOOLBAR_INFO *infoPtr, const TBSAVEPARAMSW *params)
{
NMTBSAVE save;
INT ret, i;
BOOL alloced = FALSE;
HKEY key;
TRACE( "save to %s %s\n", debugstr_w(params->pszSubKey), debugstr_w(params->pszValueName) );
memset( &save, 0, sizeof(save) );
save.cbData = infoPtr->nNumButtons * sizeof(DWORD);
save.iItem = -1;
save.cButtons = infoPtr->nNumButtons;
save.tbButton.idCommand = -1;
TOOLBAR_SendNotify( &save.hdr, infoPtr, TBN_SAVE );
if (!save.pData)
{
save.pData = Alloc( save.cbData );
if (!save.pData) return FALSE;
alloced = TRUE;
}
if (!save.pCurrent) save.pCurrent = save.pData;
for (i = 0; i < infoPtr->nNumButtons; i++)
{
save.iItem = i;
save.tbButton.iBitmap = infoPtr->buttons[i].iBitmap;
save.tbButton.idCommand = infoPtr->buttons[i].idCommand;
save.tbButton.fsState = infoPtr->buttons[i].fsState;
save.tbButton.fsStyle = infoPtr->buttons[i].fsStyle;
memset( save.tbButton.bReserved, 0, sizeof(save.tbButton.bReserved) );
save.tbButton.dwData = infoPtr->buttons[i].dwData;
save.tbButton.iString = infoPtr->buttons[i].iString;
*save.pCurrent++ = save.tbButton.idCommand;
TOOLBAR_SendNotify( &save.hdr, infoPtr, TBN_SAVE );
}
ret = RegCreateKeyW( params->hkr, params->pszSubKey, &key );
if (ret == ERROR_SUCCESS)
{
ret = RegSetValueExW( key, params->pszValueName, 0, REG_BINARY, (BYTE *)save.pData, save.cbData );
RegCloseKey( key );
}
if (alloced) Free( save.pData );
return !ret;
}
/* helper for TOOLBAR_Restore */
static void
TOOLBAR_DeleteAllButtons(TOOLBAR_INFO *infoPtr)
{
INT i;
for (i = 0; i < infoPtr->nNumButtons; i++)
{
free_string( infoPtr->buttons + i );
TOOLBAR_TooltipDelTool(infoPtr, &infoPtr->buttons[i]);
}
Free(infoPtr->buttons);
infoPtr->buttons = NULL;
infoPtr->nNumButtons = 0;
}
/* helper for TOOLBAR_SaveRestoreW */
static BOOL
TOOLBAR_Restore(TOOLBAR_INFO *infoPtr, const TBSAVEPARAMSW *lpSave)
{
LONG res;
HKEY hkey = NULL;
BOOL ret = FALSE;
DWORD dwType;
DWORD dwSize = 0;
NMTBRESTORE nmtbr;
NMHDR hdr;
/* restore toolbar information */
TRACE("restore from %s %s\n", debugstr_w(lpSave->pszSubKey),
debugstr_w(lpSave->pszValueName));
memset(&nmtbr, 0, sizeof(nmtbr));
res = RegOpenKeyExW(lpSave->hkr, lpSave->pszSubKey, 0,
KEY_QUERY_VALUE, &hkey);
if (!res)
res = RegQueryValueExW(hkey, lpSave->pszValueName, NULL, &dwType,
NULL, &dwSize);
if (!res && dwType != REG_BINARY)
res = ERROR_FILE_NOT_FOUND;
if (!res)
{
nmtbr.pData = Alloc(dwSize);
nmtbr.cbData = dwSize;
if (!nmtbr.pData) res = ERROR_OUTOFMEMORY;
}
if (!res)
res = RegQueryValueExW(hkey, lpSave->pszValueName, NULL, &dwType,
(LPBYTE)nmtbr.pData, &dwSize);
if (!res)
{
nmtbr.pCurrent = nmtbr.pData;
nmtbr.iItem = -1;
nmtbr.cbBytesPerRecord = sizeof(DWORD);
nmtbr.cButtons = nmtbr.cbData / nmtbr.cbBytesPerRecord;
if (!TOOLBAR_SendNotify(&nmtbr.hdr, infoPtr, TBN_RESTORE))
{
INT i, count = nmtbr.cButtons;
/* remove all existing buttons as this function is designed to
* restore the toolbar to a previously saved state */
TOOLBAR_DeleteAllButtons(infoPtr);
for (i = 0; i < count; i++)
{
nmtbr.iItem = i;
nmtbr.tbButton.iBitmap = -1;
nmtbr.tbButton.fsState = 0;
nmtbr.tbButton.fsStyle = 0;
nmtbr.tbButton.dwData = 0;
nmtbr.tbButton.iString = 0;
if (*nmtbr.pCurrent & 0x80000000)
{
/* separator */
nmtbr.tbButton.iBitmap = SEPARATOR_WIDTH;
nmtbr.tbButton.idCommand = 0;
nmtbr.tbButton.fsStyle = BTNS_SEP;
if (*nmtbr.pCurrent != (DWORD)-1)
nmtbr.tbButton.fsState = TBSTATE_HIDDEN;
}
else
nmtbr.tbButton.idCommand = (int)*nmtbr.pCurrent;
nmtbr.pCurrent++;
TOOLBAR_SendNotify(&nmtbr.hdr, infoPtr, TBN_RESTORE);
/* All returned ptrs and -1 are ignored */
if (!IS_INTRESOURCE(nmtbr.tbButton.iString))
nmtbr.tbButton.iString = 0;
TOOLBAR_InsertButtonT(infoPtr, -1, &nmtbr.tbButton, TRUE);
}
TOOLBAR_SendNotify( &hdr, infoPtr, TBN_BEGINADJUST );
for (i = 0; ; i++)
{
NMTOOLBARW tb;
TBBUTTONINFOW bi;
WCHAR buf[128];
UINT code = infoPtr->bUnicode ? TBN_GETBUTTONINFOW : TBN_GETBUTTONINFOA;
INT idx;
memset( &tb, 0, sizeof(tb) );
tb.iItem = i;
tb.cchText = ARRAY_SIZE(buf);
tb.pszText = buf;
/* Use the same struct for both A and W versions since the layout is the same. */
if (!TOOLBAR_SendNotify( &tb.hdr, infoPtr, code ))
break;
idx = TOOLBAR_GetButtonIndex( infoPtr, tb.tbButton.idCommand, FALSE );
if (idx == -1) continue;
/* tb.pszText is ignored - the string comes from tb.tbButton.iString, which may
be an index or a ptr. Either way it is simply copied. There is no api to change
the string index, so we set it manually. The other properties can be set with SetButtonInfo. */
free_string( infoPtr->buttons + idx );
infoPtr->buttons[idx].iString = tb.tbButton.iString;
memset( &bi, 0, sizeof(bi) );
bi.cbSize = sizeof(bi);
bi.dwMask = TBIF_IMAGE | TBIF_STATE | TBIF_STYLE | TBIF_LPARAM;
bi.iImage = tb.tbButton.iBitmap;
bi.fsState = tb.tbButton.fsState;
bi.fsStyle = tb.tbButton.fsStyle;
bi.lParam = tb.tbButton.dwData;
TOOLBAR_SetButtonInfo( infoPtr, tb.tbButton.idCommand, &bi, TRUE );
}
TOOLBAR_SendNotify( &hdr, infoPtr, TBN_ENDADJUST );
/* remove all uninitialised buttons
* note: loop backwards to avoid having to fixup i on a
* delete */
for (i = infoPtr->nNumButtons - 1; i >= 0; i--)
if (infoPtr->buttons[i].iBitmap == -1)
TOOLBAR_DeleteButton(infoPtr, i);
/* only indicate success if at least one button survived */
if (infoPtr->nNumButtons > 0) ret = TRUE;
}
}
Free (nmtbr.pData);
RegCloseKey(hkey);
return ret;
}
static LRESULT
TOOLBAR_SaveRestoreW (TOOLBAR_INFO *infoPtr, WPARAM wParam, const TBSAVEPARAMSW *lpSave)
{
if (lpSave == NULL) return 0;
if (wParam)
return TOOLBAR_Save(infoPtr, lpSave);
else
return TOOLBAR_Restore(infoPtr, lpSave);
}
static LRESULT
TOOLBAR_SaveRestoreA (TOOLBAR_INFO *infoPtr, WPARAM wParam, const TBSAVEPARAMSA *lpSave)
{
LPWSTR pszValueName = 0, pszSubKey = 0;
TBSAVEPARAMSW SaveW;
LRESULT result = 0;
int len;
if (lpSave == NULL) return 0;
len = MultiByteToWideChar(CP_ACP, 0, lpSave->pszSubKey, -1, NULL, 0);
pszSubKey = Alloc(len * sizeof(WCHAR));
if (!pszSubKey) goto exit;
MultiByteToWideChar(CP_ACP, 0, lpSave->pszSubKey, -1, pszSubKey, len);
len = MultiByteToWideChar(CP_ACP, 0, lpSave->pszValueName, -1, NULL, 0);
pszValueName = Alloc(len * sizeof(WCHAR));
if (!pszValueName) goto exit;
MultiByteToWideChar(CP_ACP, 0, lpSave->pszValueName, -1, pszValueName, len);
SaveW.pszValueName = pszValueName;
SaveW.pszSubKey = pszSubKey;
SaveW.hkr = lpSave->hkr;
result = TOOLBAR_SaveRestoreW(infoPtr, wParam, &SaveW);
exit:
Free (pszValueName);
Free (pszSubKey);
return result;
}
static LRESULT
TOOLBAR_SetAnchorHighlight (TOOLBAR_INFO *infoPtr, BOOL bAnchor)
{
BOOL bOldAnchor = infoPtr->bAnchor;
TRACE("hwnd=%p, bAnchor = %s\n", infoPtr->hwndSelf, bAnchor ? "TRUE" : "FALSE");
infoPtr->bAnchor = bAnchor;
/* Native does not remove the hot effect from an already hot button */
return (LRESULT)bOldAnchor;
}
static LRESULT
TOOLBAR_SetBitmapSize (TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
HIMAGELIST himlDef = GETDEFIMAGELIST(infoPtr, 0);
short width = (short)LOWORD(lParam);
short height = (short)HIWORD(lParam);
TRACE("hwnd %p, wParam %Id, size %d x %d\n", infoPtr->hwndSelf, wParam, width, height);
if (wParam != 0)
FIXME("wParam is %Id. Perhaps image list index?\n", wParam);
/* 0 width or height is changed to 1 */
if (width == 0)
width = 1;
if (height == 0)
height = 1;
if (infoPtr->nNumButtons > 0)
TRACE("%d buttons, undoc change to bitmap size : %d-%d -> %d-%d\n",
infoPtr->nNumButtons,
infoPtr->nBitmapWidth, infoPtr->nBitmapHeight, width, height);
if (width < -1 || height < -1)
{
/* Windows destroys the imagelist and seems to actually use negative
* values to compute button sizes */
FIXME("Negative bitmap sizes not supported (%d, %d)\n", width, height);
return FALSE;
}
/* width or height of -1 means no change */
if (width != -1)
infoPtr->nBitmapWidth = width;
if (height != -1)
infoPtr->nBitmapHeight = height;
if ((himlDef == infoPtr->himlInt) &&
(ImageList_GetImageCount(infoPtr->himlInt) == 0))
{
ImageList_SetIconSize(infoPtr->himlInt, infoPtr->nBitmapWidth,
infoPtr->nBitmapHeight);
}
TOOLBAR_CalcToolbar(infoPtr);
InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
return TRUE;
}
static LRESULT
TOOLBAR_SetButtonInfo (TOOLBAR_INFO *infoPtr, INT Id,
const TBBUTTONINFOW *lptbbi, BOOL isW)
{
TBUTTON_INFO *btnPtr;
INT nIndex;
RECT oldBtnRect;
if (lptbbi == NULL)
return FALSE;
if (lptbbi->cbSize < sizeof(TBBUTTONINFOW))
return FALSE;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, lptbbi->dwMask & TBIF_BYINDEX);
if (nIndex == -1)
return FALSE;
btnPtr = &infoPtr->buttons[nIndex];
if (lptbbi->dwMask & TBIF_COMMAND)
btnPtr->idCommand = lptbbi->idCommand;
if (lptbbi->dwMask & TBIF_IMAGE)
btnPtr->iBitmap = lptbbi->iImage;
if (lptbbi->dwMask & TBIF_LPARAM)
btnPtr->dwData = lptbbi->lParam;
if (lptbbi->dwMask & TBIF_SIZE)
btnPtr->cx = lptbbi->cx;
if (lptbbi->dwMask & TBIF_STATE)
btnPtr->fsState = lptbbi->fsState;
if (lptbbi->dwMask & TBIF_STYLE)
btnPtr->fsStyle = lptbbi->fsStyle;
if (lptbbi->dwMask & TBIF_TEXT)
set_stringT( btnPtr, lptbbi->pszText, isW );
/* save the button rect to see if we need to redraw the whole toolbar */
oldBtnRect = btnPtr->rect;
TOOLBAR_LayoutToolbar(infoPtr);
if (!EqualRect(&oldBtnRect, &btnPtr->rect))
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
else
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
return TRUE;
}
static LRESULT
TOOLBAR_SetButtonSize (TOOLBAR_INFO *infoPtr, LPARAM lParam)
{
INT cx = (short)LOWORD(lParam), cy = (short)HIWORD(lParam);
int top = default_top_margin(infoPtr);
if ((cx < 0) || (cy < 0))
{
ERR("invalid parameter %#Ix\n", lParam);
return FALSE;
}
TRACE("%p, cx = %d, cy = %d\n", infoPtr->hwndSelf, cx, cy);
/* The documentation claims you can only change the button size before
* any button has been added. But this is wrong.
* WINZIP32.EXE (ver 8) calls this on one of its buttons after adding
* it to the toolbar, and it checks that the return value is nonzero - mjm
* Further testing shows that we must actually perform the change too.
*/
/*
* The documentation also does not mention that if 0 is supplied for
* either size, the system changes it to the default of 24 wide and
* 22 high. Demonstrated in ControlSpy Toolbar. GLA 3/02
*/
if (cx == 0) cx = 24;
if (cy == 0) cy = 22;
cx = max(cx, infoPtr->szPadding.cx + infoPtr->nBitmapWidth);
cy = max(cy, infoPtr->szPadding.cy + infoPtr->nBitmapHeight);
if (cx != infoPtr->nButtonWidth || cy != infoPtr->nButtonHeight ||
top != infoPtr->iTopMargin)
{
infoPtr->nButtonWidth = cx;
infoPtr->nButtonHeight = cy;
infoPtr->iTopMargin = top;
TOOLBAR_LayoutToolbar( infoPtr );
InvalidateRect( infoPtr->hwndSelf, NULL, TRUE );
}
return TRUE;
}
static LRESULT
TOOLBAR_SetButtonWidth (TOOLBAR_INFO *infoPtr, LPARAM lParam)
{
/* if setting to current values, ignore */
if ((infoPtr->cxMin == (short)LOWORD(lParam)) &&
(infoPtr->cxMax == (short)HIWORD(lParam))) {
TRACE("matches current width, min=%d, max=%d, no recalc\n",
infoPtr->cxMin, infoPtr->cxMax);
return TRUE;
}
/* save new values */
infoPtr->cxMin = (short)LOWORD(lParam);
infoPtr->cxMax = (short)HIWORD(lParam);
/* otherwise we need to recalc the toolbar and in some cases
recalc the bounding rectangle (does DrawText w/ DT_CALCRECT
which doesn't actually draw - GA). */
TRACE("number of buttons %d, cx=%d, cy=%d, recalcing\n",
infoPtr->nNumButtons, infoPtr->cxMin, infoPtr->cxMax);
TOOLBAR_CalcToolbar (infoPtr);
InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
return TRUE;
}
static LRESULT
TOOLBAR_SetCmdId (TOOLBAR_INFO *infoPtr, INT nIndex, INT nId)
{
if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
return FALSE;
infoPtr->buttons[nIndex].idCommand = nId;
if (infoPtr->hwndToolTip) {
FIXME("change tool tip\n");
}
return TRUE;
}
static LRESULT
TOOLBAR_SetDisabledImageList (TOOLBAR_INFO *infoPtr, WPARAM wParam, HIMAGELIST himl)
{
HIMAGELIST himlTemp;
INT id = 0;
if (infoPtr->iVersion >= 5)
id = wParam;
himlTemp = TOOLBAR_InsertImageList(&infoPtr->himlDis,
&infoPtr->cimlDis, himl, id);
/* FIXME: redraw ? */
return (LRESULT)himlTemp;
}
static LRESULT
TOOLBAR_SetDrawTextFlags (TOOLBAR_INFO *infoPtr, DWORD mask, DWORD flags)
{
DWORD old_flags;
TRACE("hwnd = %p, mask = %#lx, flags %#lx\n", infoPtr->hwndSelf, mask, flags);
old_flags = infoPtr->dwDTFlags;
infoPtr->dwDTFlags = (old_flags & ~mask) | (flags & mask);
return (LRESULT)old_flags;
}
/* This function differs a bit from what MSDN says it does:
* 1. lParam contains extended style flags to OR with current style
* (MSDN isn't clear on the OR bit)
* 2. wParam appears to contain extended style flags to be reset
* (MSDN says that this parameter is reserved)
*/
static LRESULT
TOOLBAR_SetExtendedStyle (TOOLBAR_INFO *infoPtr, DWORD mask, DWORD style)
{
DWORD old_style = infoPtr->dwExStyle;
TRACE("mask %#lx, style %#lx\n", mask, style);
if (mask)
infoPtr->dwExStyle = (old_style & ~mask) | (style & mask);
else
infoPtr->dwExStyle = style;
if (infoPtr->dwExStyle & ~TBSTYLE_EX_ALL)
FIXME("Unknown Toolbar Extended Style %#lx. Please report.\n", (infoPtr->dwExStyle & ~TBSTYLE_EX_ALL));
if ((old_style ^ infoPtr->dwExStyle) & TBSTYLE_EX_MIXEDBUTTONS)
TOOLBAR_CalcToolbar(infoPtr);
else
TOOLBAR_LayoutToolbar(infoPtr);
TOOLBAR_AutoSize(infoPtr);
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return old_style;
}
static LRESULT
TOOLBAR_SetHotImageList (TOOLBAR_INFO *infoPtr, WPARAM wParam, HIMAGELIST himl)
{
HIMAGELIST himlTemp;
INT id = 0;
if (infoPtr->iVersion >= 5)
id = wParam;
TRACE("hwnd = %p, himl = %p, id = %d\n", infoPtr->hwndSelf, himl, id);
himlTemp = TOOLBAR_InsertImageList(&infoPtr->himlHot,
&infoPtr->cimlHot, himl, id);
/* FIXME: redraw ? */
return (LRESULT)himlTemp;
}
/* Makes previous hot button no longer hot, makes the specified
* button hot and sends appropriate notifications. dwReason is one or
* more HICF_ flags. Specify nHit < 0 to make no buttons hot.
* NOTE 1: this function does not validate nHit
* NOTE 2: the name of this function is completely made up and
* not based on any documentation from Microsoft. */
static void
TOOLBAR_SetHotItemEx (TOOLBAR_INFO *infoPtr, INT nHit, DWORD dwReason)
{
if (infoPtr->nHotItem != nHit)
{
NMTBHOTITEM nmhotitem;
TBUTTON_INFO *btnPtr = NULL, *oldBtnPtr = NULL;
nmhotitem.dwFlags = dwReason;
if(infoPtr->nHotItem >= 0)
{
oldBtnPtr = &infoPtr->buttons[infoPtr->nHotItem];
nmhotitem.idOld = oldBtnPtr->idCommand;
}
else
{
nmhotitem.dwFlags |= HICF_ENTERING;
nmhotitem.idOld = 0;
}
if (nHit >= 0)
{
btnPtr = &infoPtr->buttons[nHit];
nmhotitem.idNew = btnPtr->idCommand;
}
else
{
nmhotitem.dwFlags |= HICF_LEAVING;
nmhotitem.idNew = 0;
}
/* now change the hot and invalidate the old and new buttons - if the
* parent agrees */
if (!TOOLBAR_SendNotify(&nmhotitem.hdr, infoPtr, TBN_HOTITEMCHANGE))
{
if (oldBtnPtr) {
oldBtnPtr->bHot = FALSE;
InvalidateRect(infoPtr->hwndSelf, &oldBtnPtr->rect, TRUE);
}
/* setting disabled buttons as hot fails even if the notify contains the button id */
if (btnPtr && (btnPtr->fsState & TBSTATE_ENABLED)) {
btnPtr->bHot = TRUE;
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
infoPtr->nHotItem = nHit;
}
else
infoPtr->nHotItem = -1;
}
}
}
static LRESULT
TOOLBAR_SetHotItem (TOOLBAR_INFO *infoPtr, INT nHotItem)
{
INT nOldHotItem = infoPtr->nHotItem;
TRACE("hwnd = %p, nHotItem = %d\n", infoPtr->hwndSelf, nHotItem);
if (nHotItem >= infoPtr->nNumButtons)
return infoPtr->nHotItem;
if (nHotItem < 0)
nHotItem = -1;
/* NOTE: an application can still remove the hot item even if anchor
* highlighting is enabled */
TOOLBAR_SetHotItemEx(infoPtr, nHotItem, HICF_OTHER);
if (nOldHotItem < 0)
return -1;
return (LRESULT)nOldHotItem;
}
static LRESULT
TOOLBAR_SetImageList (TOOLBAR_INFO *infoPtr, WPARAM wParam, HIMAGELIST himl)
{
HIMAGELIST himlTemp;
INT oldButtonWidth = infoPtr->nButtonWidth;
INT oldBitmapWidth = infoPtr->nBitmapWidth;
INT oldBitmapHeight = infoPtr->nBitmapHeight;
INT i, id = 0;
if (infoPtr->iVersion >= 5)
id = wParam;
himlTemp = TOOLBAR_InsertImageList(&infoPtr->himlDef,
&infoPtr->cimlDef, himl, id);
infoPtr->nNumBitmaps = 0;
for (i = 0; i < infoPtr->cimlDef; i++)
infoPtr->nNumBitmaps += ImageList_GetImageCount(infoPtr->himlDef[i]->himl);
if (!ImageList_GetIconSize(himl, &infoPtr->nBitmapWidth,
&infoPtr->nBitmapHeight))
{
infoPtr->nBitmapWidth = 1;
infoPtr->nBitmapHeight = 1;
}
if ((oldBitmapWidth != infoPtr->nBitmapWidth) || (oldBitmapHeight != infoPtr->nBitmapHeight))
{
TOOLBAR_CalcToolbar(infoPtr);
if (infoPtr->nButtonWidth < oldButtonWidth)
TOOLBAR_SetButtonSize(infoPtr, MAKELONG(oldButtonWidth, infoPtr->nButtonHeight));
}
TRACE("hwnd %p, new himl=%p, id = %d, count=%d, bitmap w=%d, h=%d\n",
infoPtr->hwndSelf, infoPtr->himlDef, id, infoPtr->nNumBitmaps,
infoPtr->nBitmapWidth, infoPtr->nBitmapHeight);
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return (LRESULT)himlTemp;
}
static LRESULT
TOOLBAR_SetIndent (TOOLBAR_INFO *infoPtr, INT nIndent)
{
infoPtr->nIndent = nIndent;
TRACE("\n");
/* process only on indent changing */
if(infoPtr->nIndent != nIndent)
{
infoPtr->nIndent = nIndent;
TOOLBAR_CalcToolbar (infoPtr);
InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
}
return TRUE;
}
static LRESULT
TOOLBAR_SetInsertMark (TOOLBAR_INFO *infoPtr, const TBINSERTMARK *lptbim)
{
TRACE("hwnd = %p, lptbim = { %d, %#lx}\n", infoPtr->hwndSelf, lptbim->iButton, lptbim->dwFlags);
if ((lptbim->dwFlags & ~TBIMHT_AFTER) != 0)
{
FIXME("Unrecognized flag(s): %#lx\n", (lptbim->dwFlags & ~TBIMHT_AFTER));
return 0;
}
if ((lptbim->iButton == -1) ||
((lptbim->iButton < infoPtr->nNumButtons) &&
(lptbim->iButton >= 0)))
{
infoPtr->tbim = *lptbim;
/* FIXME: don't need to update entire toolbar */
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
}
else
ERR("Invalid button index %d\n", lptbim->iButton);
return 0;
}
static LRESULT
TOOLBAR_SetInsertMarkColor (TOOLBAR_INFO *infoPtr, COLORREF clr)
{
infoPtr->clrInsertMark = clr;
/* FIXME: don't need to update entire toolbar */
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return 0;
}
static LRESULT
TOOLBAR_SetMaxTextRows (TOOLBAR_INFO *infoPtr, INT nMaxRows)
{
infoPtr->nMaxTextRows = nMaxRows;
TOOLBAR_CalcToolbar(infoPtr);
return TRUE;
}
/* MSDN gives slightly wrong info on padding.
* 1. It is not only used on buttons with the BTNS_AUTOSIZE style
* 2. It is not used to create a blank area between the edge of the button
* and the text or image if TBSTYLE_LIST is set. It is used to control
* the gap between the image and text.
* 3. It is not applied to both sides. If TBSTYLE_LIST is set it is used
* to control the bottom and right borders [with the border being
* szPadding.cx - (GetSystemMetrics(SM_CXEDGE)+1)], otherwise the padding
* is shared evenly on both sides of the button.
* See blueprints in comments above TOOLBAR_MeasureButton for more info.
*/
static LRESULT
TOOLBAR_SetPadding (TOOLBAR_INFO *infoPtr, LPARAM lParam)
{
DWORD oldPad;
oldPad = MAKELONG(infoPtr->szPadding.cx, infoPtr->szPadding.cy);
infoPtr->szPadding.cx = min(LOWORD((DWORD)lParam), GetSystemMetrics(SM_CXEDGE));
infoPtr->szPadding.cy = min(HIWORD((DWORD)lParam), GetSystemMetrics(SM_CYEDGE));
TRACE("cx %ld, cy %ld\n", infoPtr->szPadding.cx, infoPtr->szPadding.cy);
return (LRESULT) oldPad;
}
static LRESULT
TOOLBAR_SetParent (TOOLBAR_INFO *infoPtr, HWND hParent)
{
HWND hwndOldNotify;
TRACE("\n");
hwndOldNotify = infoPtr->hwndNotify;
infoPtr->hwndNotify = hParent;
return (LRESULT)hwndOldNotify;
}
static LRESULT
TOOLBAR_SetRows (TOOLBAR_INFO *infoPtr, WPARAM wParam, LPRECT lprc)
{
int rows = LOWORD(wParam);
BOOL bLarger = HIWORD(wParam);
TRACE("\n");
TRACE("Setting rows to %d (%d)\n", rows, bLarger);
if(infoPtr->nRows != rows)
{
TBUTTON_INFO *btnPtr = infoPtr->buttons;
int curColumn = 0; /* Current column */
int curRow = 0; /* Current row */
int hidden = 0; /* Number of hidden buttons */
int seps = 0; /* Number of separators */
int idealWrap = 0; /* Ideal wrap point */
int i;
BOOL wrap;
/*
Calculate new size and wrap points - Under windows, setrows will
change the dimensions if needed to show the number of requested
rows (if CCS_NORESIZE is set), or will take up the whole window
(if no CCS_NORESIZE).
Basic algorithm - If N buttons, and y rows requested, each row
contains N/y buttons.
FIXME: Handling of separators not obvious from testing results
FIXME: Take width of window into account?
*/
/* Loop through the buttons one by one counting key items */
for (i = 0; i < infoPtr->nNumButtons; i++ )
{
btnPtr[i].fsState &= ~TBSTATE_WRAP;
if (btnPtr[i].fsState & TBSTATE_HIDDEN)
hidden++;
else if (btnPtr[i].fsStyle & BTNS_SEP)
seps++;
}
/* FIXME: Separators make this quite complex */
if (seps) FIXME("Separators unhandled\n");
/* Round up so more per line, i.e., less rows */
idealWrap = (infoPtr->nNumButtons - hidden + (rows-1)) / (rows ? rows : 1);
/* Calculate ideal wrap point if we are allowed to grow, but cannot
achieve the requested number of rows. */
if (bLarger && idealWrap > 1)
{
int resRows = (infoPtr->nNumButtons + (idealWrap-1)) / idealWrap;
int moreRows = (infoPtr->nNumButtons + (idealWrap-2)) / (idealWrap-1);
if (resRows < rows && moreRows > rows)
{
idealWrap--;
TRACE("Changing idealWrap due to bLarger (now %d)\n", idealWrap);
}
}
curColumn = curRow = 0;
wrap = FALSE;
TRACE("Trying to wrap at %d (%d,%d,%d)\n", idealWrap,
infoPtr->nNumButtons, hidden, rows);
for (i = 0; i < infoPtr->nNumButtons; i++ )
{
if (btnPtr[i].fsState & TBSTATE_HIDDEN)
continue;
/* Step on, wrap if necessary or flag next to wrap */
if (!wrap) {
curColumn++;
} else {
wrap = FALSE;
curColumn = 1;
curRow++;
}
if (curColumn > (idealWrap-1)) {
wrap = TRUE;
btnPtr[i].fsState |= TBSTATE_WRAP;
}
}
TRACE("Result - %d rows\n", curRow + 1);
/* recalculate toolbar */
TOOLBAR_CalcToolbar (infoPtr);
/* Resize if necessary (Only if NORESIZE is set - odd, but basically
if NORESIZE is NOT set, then the toolbar will always be resized to
take up the whole window. With it set, sizing needs to be manual. */
if (infoPtr->dwStyle & CCS_NORESIZE) {
SetWindowPos(infoPtr->hwndSelf, NULL, 0, 0,
infoPtr->rcBound.right - infoPtr->rcBound.left,
infoPtr->rcBound.bottom - infoPtr->rcBound.top,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
}
/* repaint toolbar */
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
}
/* return bounding rectangle */
if (lprc) {
lprc->left = infoPtr->rcBound.left;
lprc->right = infoPtr->rcBound.right;
lprc->top = infoPtr->rcBound.top;
lprc->bottom = infoPtr->rcBound.bottom;
}
return 0;
}
static LRESULT
TOOLBAR_SetState (TOOLBAR_INFO *infoPtr, INT Id, LPARAM lParam)
{
TBUTTON_INFO *btnPtr;
INT nIndex;
nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
if (nIndex == -1)
return FALSE;
btnPtr = &infoPtr->buttons[nIndex];
/* if hidden state has changed the invalidate entire window and recalc */
if ((btnPtr->fsState & TBSTATE_HIDDEN) != (LOWORD(lParam) & TBSTATE_HIDDEN)) {
btnPtr->fsState = LOWORD(lParam);
TOOLBAR_CalcToolbar (infoPtr);
InvalidateRect(infoPtr->hwndSelf, 0, TRUE);
return TRUE;
}
/* process state changing if current state doesn't match new state */
if(btnPtr->fsState != LOWORD(lParam))
{
btnPtr->fsState = LOWORD(lParam);
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
}
return TRUE;
}
static inline void unwrap(TOOLBAR_INFO *info)
{
int i;
for (i = 0; i < info->nNumButtons; i++)
info->buttons[i].fsState &= ~TBSTATE_WRAP;
}
static LRESULT
TOOLBAR_SetStyle (TOOLBAR_INFO *infoPtr, DWORD style)
{
DWORD dwOldStyle = infoPtr->dwStyle;
TRACE("new style %#lx\n", style);
if (style & TBSTYLE_LIST)
infoPtr->dwDTFlags = DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS;
else
infoPtr->dwDTFlags = DT_CENTER | DT_END_ELLIPSIS;
infoPtr->dwStyle = style;
TOOLBAR_CheckStyle(infoPtr);
if ((dwOldStyle ^ style) & TBSTYLE_WRAPABLE)
{
if (dwOldStyle & TBSTYLE_WRAPABLE)
unwrap(infoPtr);
TOOLBAR_CalcToolbar(infoPtr);
}
else if ((dwOldStyle ^ style) & CCS_VERT)
TOOLBAR_LayoutToolbar(infoPtr);
/* only resize if one of the CCS_* styles was changed */
if ((dwOldStyle ^ style) & COMMON_STYLES)
{
TOOLBAR_AutoSize(infoPtr);
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
}
return 0;
}
static inline LRESULT
TOOLBAR_SetToolTips (TOOLBAR_INFO *infoPtr, HWND hwndTooltip)
{
TRACE("hwnd=%p, hwndTooltip=%p\n", infoPtr->hwndSelf, hwndTooltip);
infoPtr->hwndToolTip = hwndTooltip;
return 0;
}
static LRESULT
TOOLBAR_SetUnicodeFormat (TOOLBAR_INFO *infoPtr, WPARAM wParam)
{
BOOL bTemp;
TRACE("%s hwnd=%p\n",
((BOOL)wParam) ? "TRUE" : "FALSE", infoPtr->hwndSelf);
bTemp = infoPtr->bUnicode;
infoPtr->bUnicode = (BOOL)wParam;
return bTemp;
}
static LRESULT
TOOLBAR_GetColorScheme (const TOOLBAR_INFO *infoPtr, LPCOLORSCHEME lParam)
{
lParam->clrBtnHighlight = (infoPtr->clrBtnHighlight == CLR_DEFAULT) ?
comctl32_color.clrBtnHighlight :
infoPtr->clrBtnHighlight;
lParam->clrBtnShadow = (infoPtr->clrBtnShadow == CLR_DEFAULT) ?
comctl32_color.clrBtnShadow : infoPtr->clrBtnShadow;
return 1;
}
static LRESULT
TOOLBAR_SetColorScheme (TOOLBAR_INFO *infoPtr, const COLORSCHEME *lParam)
{
TRACE("new colors Hl=%#lx Shd=%#lx, old colors Hl=%#lx Shd=%#lx\n",
lParam->clrBtnHighlight, lParam->clrBtnShadow,
infoPtr->clrBtnHighlight, infoPtr->clrBtnShadow);
infoPtr->clrBtnHighlight = lParam->clrBtnHighlight;
infoPtr->clrBtnShadow = lParam->clrBtnShadow;
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return 0;
}
static LRESULT
TOOLBAR_SetVersion (TOOLBAR_INFO *infoPtr, INT iVersion)
{
INT iOldVersion = infoPtr->iVersion;
infoPtr->iVersion = iVersion;
if (infoPtr->iVersion >= 5)
TOOLBAR_SetUnicodeFormat(infoPtr, TRUE);
return iOldVersion;
}
static LRESULT
TOOLBAR_GetStringA (const TOOLBAR_INFO *infoPtr, WPARAM wParam, LPSTR str)
{
WORD iString = HIWORD(wParam);
WORD buffersize = LOWORD(wParam);
LRESULT ret = -1;
TRACE("hwnd=%p, iString=%d, buffersize=%d, string=%p\n", infoPtr->hwndSelf, iString, buffersize, str);
if (iString < infoPtr->nNumStrings)
{
ret = WideCharToMultiByte(CP_ACP, 0, infoPtr->strings[iString], -1, str, buffersize, NULL, NULL);
ret--;
TRACE("returning %s\n", debugstr_a(str));
}
else
WARN("String index %d out of range (largest is %d)\n", iString, infoPtr->nNumStrings - 1);
return ret;
}
static LRESULT
TOOLBAR_GetStringW (const TOOLBAR_INFO *infoPtr, WPARAM wParam, LPWSTR str)
{
WORD iString = HIWORD(wParam);
WORD len = LOWORD(wParam)/sizeof(WCHAR) - 1;
LRESULT ret = -1;
TRACE("hwnd=%p, iString=%d, buffersize=%d, string=%p\n", infoPtr->hwndSelf, iString, LOWORD(wParam), str);
if (iString < infoPtr->nNumStrings)
{
len = min(len, lstrlenW(infoPtr->strings[iString]));
ret = (len+1)*sizeof(WCHAR);
if (str)
{
memcpy(str, infoPtr->strings[iString], ret);
str[len] = '\0';
}
ret = len;
TRACE("returning %s\n", debugstr_w(str));
}
else
WARN("String index %d out of range (largest is %d)\n", iString, infoPtr->nNumStrings - 1);
return ret;
}
static LRESULT TOOLBAR_SetBoundingSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
SIZE * pSize = (SIZE*)lParam;
FIXME("hwnd=%p, wParam=%Ix, size.cx=%ld, size.cy=%ld stub\n", hwnd, wParam, pSize->cx, pSize->cy);
return 0;
}
/* This is an extended version of the TB_SETHOTITEM message. It allows the
* caller to specify a reason why the hot item changed (rather than just the
* HICF_OTHER that TB_SETHOTITEM sends). */
static LRESULT
TOOLBAR_SetHotItem2 (TOOLBAR_INFO *infoPtr, INT nHotItem, LPARAM lParam)
{
INT nOldHotItem = infoPtr->nHotItem;
TRACE("old item %d, new item %d, flags %Ix\n", nOldHotItem, nHotItem, lParam);
if (nHotItem < 0 || nHotItem > infoPtr->nNumButtons)
nHotItem = -1;
/* NOTE: an application can still remove the hot item even if anchor
* highlighting is enabled */
TOOLBAR_SetHotItemEx(infoPtr, nHotItem, lParam);
return (nOldHotItem < 0) ? -1 : (LRESULT)nOldHotItem;
}
/* Sets the toolbar global iListGap parameter which controls the amount of
* spacing between the image and the text of buttons for TBSTYLE_LIST
* toolbars. */
static LRESULT TOOLBAR_SetListGap(TOOLBAR_INFO *infoPtr, INT iListGap)
{
TRACE("hwnd=%p iListGap=%d\n", infoPtr->hwndSelf, iListGap);
infoPtr->iListGap = iListGap;
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return 0;
}
/* Returns the number of maximum number of image lists associated with the
* various states. */
static LRESULT TOOLBAR_GetImageListCount(const TOOLBAR_INFO *infoPtr)
{
TRACE("hwnd=%p\n", infoPtr->hwndSelf);
return max(infoPtr->cimlDef, max(infoPtr->cimlHot, infoPtr->cimlDis));
}
static LRESULT
TOOLBAR_GetIdealSize (const TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
LPSIZE lpsize = (LPSIZE)lParam;
if (lpsize == NULL)
return FALSE;
/*
* Testing shows the following:
* wParam = 0 adjust cx value
* = 1 set cy value to max size.
* lParam pointer to SIZE structure
*
*/
TRACE("wParam %Id, lParam %Ix -> %lx, %lx\n", wParam, lParam, lpsize->cx, lpsize->cy);
switch(wParam) {
case 0:
if (lpsize->cx == -1) {
lpsize->cx = infoPtr->rcBound.right - infoPtr->rcBound.left;
}
else if(HIWORD(lpsize->cx)) {
RECT rc;
HWND hwndParent = GetParent(infoPtr->hwndSelf);
GetWindowRect(infoPtr->hwndSelf, &rc);
MapWindowPoints(0, hwndParent, (LPPOINT)&rc, 2);
TRACE("mapped to (%s)\n", wine_dbgstr_rect(&rc));
lpsize->cx = max(rc.right-rc.left,
infoPtr->rcBound.right - infoPtr->rcBound.left);
}
else {
lpsize->cx = infoPtr->rcBound.right - infoPtr->rcBound.left;
}
break;
case 1:
lpsize->cy = infoPtr->rcBound.bottom - infoPtr->rcBound.top;
break;
default:
FIXME("Unknown wParam %Id\n", wParam);
return 0;
}
TRACE("set to -> %lu, %lu\n", lpsize->cx, lpsize->cy);
return 1;
}
static LRESULT TOOLBAR_Unkwn464(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
FIXME("hwnd %p, wParam %Ix, lParam %Ix\n", hwnd, wParam, lParam);
InvalidateRect(hwnd, NULL, TRUE);
return 1;
}
static LRESULT
TOOLBAR_Create (HWND hwnd, const CREATESTRUCTW *lpcs)
{
TOOLBAR_INFO *infoPtr = (TOOLBAR_INFO *)GetWindowLongPtrW(hwnd, 0);
LOGFONTW logFont;
TRACE("hwnd = %p, style = %#lx\n", hwnd, lpcs->style);
infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
GetClientRect(hwnd, &infoPtr->client_rect);
infoPtr->bUnicode = infoPtr->hwndNotify &&
(NFR_UNICODE == SendMessageW(hwnd, WM_NOTIFYFORMAT, (WPARAM)hwnd, NF_REQUERY));
infoPtr->hwndToolTip = NULL; /* if needed the tooltip control will be created after a WM_MOUSEMOVE */
SystemParametersInfoW (SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
infoPtr->hFont = infoPtr->hDefaultFont = CreateFontIndirectW (&logFont);
infoPtr->hTheme = OpenThemeDataForDpi (NULL, themeClass, GetDpiForWindow (hwnd));
TOOLBAR_CheckStyle (infoPtr);
return 0;
}
static LRESULT
TOOLBAR_Destroy (TOOLBAR_INFO *infoPtr)
{
INT i;
/* delete tooltip control */
if (infoPtr->hwndToolTip)
DestroyWindow (infoPtr->hwndToolTip);
/* delete temporary buffer for tooltip text */
Free (infoPtr->pszTooltipText);
Free (infoPtr->bitmaps); /* bitmaps list */
/* delete button data */
for (i = 0; i < infoPtr->nNumButtons; i++)
free_string( infoPtr->buttons + i );
Free (infoPtr->buttons);
/* delete strings */
if (infoPtr->strings) {
for (i = 0; i < infoPtr->nNumStrings; i++)
Free (infoPtr->strings[i]);
Free (infoPtr->strings);
}
/* destroy internal image list */
if (infoPtr->himlInt)
ImageList_Destroy (infoPtr->himlInt);
TOOLBAR_DeleteImageList(&infoPtr->himlDef, &infoPtr->cimlDef);
TOOLBAR_DeleteImageList(&infoPtr->himlDis, &infoPtr->cimlDis);
TOOLBAR_DeleteImageList(&infoPtr->himlHot, &infoPtr->cimlHot);
/* delete default font */
DeleteObject (infoPtr->hDefaultFont);
CloseThemeData (infoPtr->hTheme);
/* free toolbar info data */
SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
Free (infoPtr);
return 0;
}
static LRESULT
TOOLBAR_EraseBackground (TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
NMTBCUSTOMDRAW tbcd;
INT ret = FALSE;
DWORD ntfret;
DWORD dwEraseCustDraw = 0;
/* the app has told us not to redraw the toolbar */
if (!infoPtr->bDoRedraw)
return FALSE;
if (infoPtr->dwStyle & TBSTYLE_CUSTOMERASE) {
ZeroMemory (&tbcd, sizeof(NMTBCUSTOMDRAW));
tbcd.nmcd.dwDrawStage = CDDS_PREERASE;
tbcd.nmcd.hdc = (HDC)wParam;
ntfret = TOOLBAR_SendNotify (&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
dwEraseCustDraw = ntfret & 0xffff;
/* FIXME: in general the return flags *can* be or'ed together */
switch (dwEraseCustDraw)
{
case CDRF_DODEFAULT:
break;
case CDRF_SKIPDEFAULT:
return TRUE;
default:
FIXME("[%p] response %ld not handled to NM_CUSTOMDRAW (CDDS_PREERASE)\n",
infoPtr->hwndSelf, ntfret);
}
}
/* If the toolbar is "transparent" then pass the WM_ERASEBKGND up
* to my parent for processing.
*/
if (infoPtr->hTheme || (infoPtr->dwStyle & TBSTYLE_TRANSPARENT)) {
POINT pt, ptorig;
HDC hdc = (HDC)wParam;
HWND parent;
pt.x = 0;
pt.y = 0;
parent = GetParent(infoPtr->hwndSelf);
MapWindowPoints(infoPtr->hwndSelf, parent, &pt, 1);
OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
ret = SendMessageW (parent, WM_ERASEBKGND, wParam, lParam);
SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);
}
if (!ret)
ret = DefWindowProcW (infoPtr->hwndSelf, WM_ERASEBKGND, wParam, lParam);
if (dwEraseCustDraw & CDRF_NOTIFYPOSTERASE) {
ZeroMemory (&tbcd, sizeof(NMTBCUSTOMDRAW));
tbcd.nmcd.dwDrawStage = CDDS_POSTERASE;
tbcd.nmcd.hdc = (HDC)wParam;
ntfret = TOOLBAR_SendNotify (&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
dwEraseCustDraw = ntfret & 0xffff;
switch (dwEraseCustDraw)
{
case CDRF_DODEFAULT:
break;
case CDRF_SKIPDEFAULT:
return TRUE;
default:
FIXME("[%p] response %ld not handled to NM_CUSTOMDRAW (CDDS_POSTERASE)\n",
infoPtr->hwndSelf, ntfret);
}
}
return ret;
}
static inline LRESULT
TOOLBAR_GetFont (const TOOLBAR_INFO *infoPtr)
{
return (LRESULT)infoPtr->hFont;
}
static void
TOOLBAR_SetRelativeHotItem(TOOLBAR_INFO *infoPtr, INT iDirection, DWORD dwReason)
{
INT i;
INT nNewHotItem = infoPtr->nHotItem;
for (i = 0; i < infoPtr->nNumButtons; i++)
{
/* did we wrap? */
if ((nNewHotItem + iDirection < 0) ||
(nNewHotItem + iDirection >= infoPtr->nNumButtons))
{
NMTBWRAPHOTITEM nmtbwhi;
nmtbwhi.idNew = infoPtr->buttons[nNewHotItem].idCommand;
nmtbwhi.iDirection = iDirection;
nmtbwhi.dwReason = dwReason;
if (TOOLBAR_SendNotify(&nmtbwhi.hdr, infoPtr, TBN_WRAPHOTITEM))
return;
}
nNewHotItem += iDirection;
nNewHotItem = (nNewHotItem + infoPtr->nNumButtons) % infoPtr->nNumButtons;
if ((infoPtr->buttons[nNewHotItem].fsState & TBSTATE_ENABLED) &&
!(infoPtr->buttons[nNewHotItem].fsStyle & BTNS_SEP))
{
TOOLBAR_SetHotItemEx(infoPtr, nNewHotItem, dwReason);
break;
}
}
}
static LRESULT
TOOLBAR_KeyDown (TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
NMKEY nmkey;
nmkey.nVKey = (UINT)wParam;
nmkey.uFlags = HIWORD(lParam);
if (TOOLBAR_SendNotify(&nmkey.hdr, infoPtr, NM_KEYDOWN))
return DefWindowProcW(infoPtr->hwndSelf, WM_KEYDOWN, wParam, lParam);
switch ((UINT)wParam)
{
case VK_LEFT:
case VK_UP:
TOOLBAR_SetRelativeHotItem(infoPtr, -1, HICF_ARROWKEYS);
break;
case VK_RIGHT:
case VK_DOWN:
TOOLBAR_SetRelativeHotItem(infoPtr, 1, HICF_ARROWKEYS);
break;
case VK_SPACE:
case VK_RETURN:
if ((infoPtr->nHotItem >= 0) &&
(infoPtr->buttons[infoPtr->nHotItem].fsState & TBSTATE_ENABLED))
{
SendMessageW (infoPtr->hwndNotify, WM_COMMAND,
MAKEWPARAM(infoPtr->buttons[infoPtr->nHotItem].idCommand, BN_CLICKED),
(LPARAM)infoPtr->hwndSelf);
}
break;
}
return 0;
}
static LRESULT
TOOLBAR_LButtonDblClk (TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
POINT pt;
BOOL button;
pt.x = (short)LOWORD(lParam);
pt.y = (short)HIWORD(lParam);
TOOLBAR_InternalHitTest (infoPtr, &pt, &button);
if (button)
TOOLBAR_LButtonDown (infoPtr, wParam, lParam);
else if (infoPtr->dwStyle & CCS_ADJUSTABLE)
TOOLBAR_Customize (infoPtr);
return 0;
}
static LRESULT
TOOLBAR_LButtonDown (TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
TBUTTON_INFO *btnPtr;
POINT pt;
INT nHit;
NMTOOLBARA nmtb;
NMMOUSE nmmouse;
BOOL bDragKeyPressed;
BOOL button;
TRACE("\n");
if (infoPtr->dwStyle & TBSTYLE_ALTDRAG)
bDragKeyPressed = (GetKeyState(VK_MENU) < 0);
else
bDragKeyPressed = (wParam & MK_SHIFT);
if (infoPtr->hwndToolTip)
TOOLBAR_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwndSelf,
WM_LBUTTONDOWN, wParam, lParam);
pt.x = (short)LOWORD(lParam);
pt.y = (short)HIWORD(lParam);
nHit = TOOLBAR_InternalHitTest (infoPtr, &pt, &button);
if (button)
{
btnPtr = &infoPtr->buttons[nHit];
if (bDragKeyPressed && (infoPtr->dwStyle & CCS_ADJUSTABLE))
{
infoPtr->nButtonDrag = nHit;
SetCapture (infoPtr->hwndSelf);
/* If drag cursor has not been loaded, load it.
* Note: it doesn't need to be freed */
if (!hCursorDrag)
hCursorDrag = LoadCursorW(COMCTL32_hModule, (LPCWSTR)IDC_MOVEBUTTON);
SetCursor(hCursorDrag);
}
else
{
RECT arrowRect;
infoPtr->nOldHit = nHit;
arrowRect = btnPtr->rect;
arrowRect.left = max(btnPtr->rect.left, btnPtr->rect.right - DDARROW_WIDTH);
/* for EX_DRAWDDARROWS style, click must be in the drop-down arrow rect */
if ((btnPtr->fsState & TBSTATE_ENABLED) &&
((btnPtr->fsStyle & BTNS_WHOLEDROPDOWN) ||
((btnPtr->fsStyle & BTNS_DROPDOWN) &&
((TOOLBAR_HasDropDownArrows(infoPtr->dwExStyle) && PtInRect(&arrowRect, pt)) ||
(!TOOLBAR_HasDropDownArrows(infoPtr->dwExStyle))))))
{
LRESULT res;
/* draw in pressed state */
if (btnPtr->fsStyle & BTNS_WHOLEDROPDOWN)
btnPtr->fsState |= TBSTATE_PRESSED;
else
btnPtr->bDropDownPressed = TRUE;
RedrawWindow(infoPtr->hwndSelf, &btnPtr->rect, 0, RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW);
memset(&nmtb, 0, sizeof(nmtb));
nmtb.iItem = btnPtr->idCommand;
nmtb.rcButton = btnPtr->rect;
res = TOOLBAR_SendNotify ((NMHDR *) &nmtb, infoPtr, TBN_DROPDOWN);
TRACE("TBN_DROPDOWN responded with %Id\n", res);
if (res != TBDDRET_TREATPRESSED)
{
MSG msg;
/* redraw button in unpressed state */
if (btnPtr->fsStyle & BTNS_WHOLEDROPDOWN)
btnPtr->fsState &= ~TBSTATE_PRESSED;
else
btnPtr->bDropDownPressed = FALSE;
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
/* find and set hot item */
GetCursorPos(&pt);
ScreenToClient(infoPtr->hwndSelf, &pt);
nHit = TOOLBAR_InternalHitTest(infoPtr, &pt, &button);
if (!infoPtr->bAnchor || button)
TOOLBAR_SetHotItemEx(infoPtr, nHit, HICF_MOUSE | HICF_LMOUSE);
/* remove any left mouse button down or double-click messages
* so that we can get a toggle effect on the button */
while (PeekMessageW(&msg, infoPtr->hwndSelf, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE) ||
PeekMessageW(&msg, infoPtr->hwndSelf, WM_LBUTTONDBLCLK, WM_LBUTTONDBLCLK, PM_REMOVE))
;
return 0;
}
/* otherwise drop through and process as pushed */
}
infoPtr->bCaptured = TRUE;
infoPtr->nButtonDown = nHit;
infoPtr->bDragOutSent = FALSE;
btnPtr->fsState |= TBSTATE_PRESSED;
TOOLBAR_SetHotItemEx(infoPtr, nHit, HICF_MOUSE | HICF_LMOUSE);
if (btnPtr->fsState & TBSTATE_ENABLED)
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
UpdateWindow(infoPtr->hwndSelf);
SetCapture (infoPtr->hwndSelf);
}
memset(&nmtb, 0, sizeof(nmtb));
nmtb.iItem = btnPtr->idCommand;
TOOLBAR_SendNotify((NMHDR *)&nmtb, infoPtr, TBN_BEGINDRAG);
}
nmmouse.dwHitInfo = nHit;
/* !!! Undocumented - sends NM_LDOWN with the NMMOUSE structure. */
if (!button)
nmmouse.dwItemSpec = -1;
else
{
nmmouse.dwItemSpec = infoPtr->buttons[nmmouse.dwHitInfo].idCommand;
nmmouse.dwItemData = infoPtr->buttons[nmmouse.dwHitInfo].dwData;
}
ClientToScreen(infoPtr->hwndSelf, &pt);
nmmouse.pt = pt;
if (!TOOLBAR_SendNotify(&nmmouse.hdr, infoPtr, NM_LDOWN))
return DefWindowProcW(infoPtr->hwndSelf, WM_LBUTTONDOWN, wParam, lParam);
return 0;
}
static LRESULT
TOOLBAR_LButtonUp (TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
TBUTTON_INFO *btnPtr;
POINT pt;
INT nHit;
INT nOldIndex = -1;
NMHDR hdr;
NMMOUSE nmmouse;
NMTOOLBARA nmtb;
BOOL button;
if (infoPtr->hwndToolTip)
TOOLBAR_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwndSelf,
WM_LBUTTONUP, wParam, lParam);
pt.x = (short)LOWORD(lParam);
pt.y = (short)HIWORD(lParam);
nHit = TOOLBAR_InternalHitTest (infoPtr, &pt, &button);
if (!infoPtr->bAnchor || button)
TOOLBAR_SetHotItemEx(infoPtr, button ? nHit : TOOLBAR_NOWHERE, HICF_MOUSE | HICF_LMOUSE);
if (infoPtr->nButtonDrag >= 0) {
RECT rcClient;
NMHDR hdr;
btnPtr = &infoPtr->buttons[infoPtr->nButtonDrag];
ReleaseCapture();
/* reset cursor */
SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_ARROW));
GetClientRect(infoPtr->hwndSelf, &rcClient);
if (PtInRect(&rcClient, pt))
{
INT nButton = -1;
if (nHit >= 0)
nButton = nHit;
else if (nHit < -1)
nButton = -nHit;
else if ((nHit == -1) && PtInRect(&infoPtr->buttons[-nHit].rect, pt))
nButton = -nHit;
if (nButton == infoPtr->nButtonDrag)
{
/* if the button is moved slightly left and we have a
* separator there then remove it */
if (pt.x < (btnPtr->rect.left + (btnPtr->rect.right - btnPtr->rect.left)/2))
{
if ((nButton > 0) && (infoPtr->buttons[nButton-1].fsStyle & BTNS_SEP))
TOOLBAR_DeleteButton(infoPtr, nButton - 1);
}
else /* else insert a separator before the dragged button */
{
TBBUTTON tbb;
memset(&tbb, 0, sizeof(tbb));
tbb.fsStyle = BTNS_SEP;
tbb.iString = -1;
TOOLBAR_InsertButtonT(infoPtr, nButton, &tbb, TRUE);
}
}
else
{
if (nButton == -1)
{
if ((infoPtr->nNumButtons > 0) && (pt.x < infoPtr->buttons[0].rect.left))
TOOLBAR_MoveButton(infoPtr, infoPtr->nButtonDrag, 0);
else
TOOLBAR_MoveButton(infoPtr, infoPtr->nButtonDrag, infoPtr->nNumButtons);
}
else
TOOLBAR_MoveButton(infoPtr, infoPtr->nButtonDrag, nButton);
}
}
else
{
TRACE("button %d dragged out of toolbar\n", infoPtr->nButtonDrag);
TOOLBAR_DeleteButton(infoPtr, infoPtr->nButtonDrag);
}
/* button under cursor changed so need to re-set hot item */
TOOLBAR_SetHotItemEx(infoPtr, button ? nHit : TOOLBAR_NOWHERE, HICF_MOUSE | HICF_LMOUSE);
infoPtr->nButtonDrag = -1;
TOOLBAR_SendNotify(&hdr, infoPtr, TBN_TOOLBARCHANGE);
}
else if (infoPtr->nButtonDown >= 0)
{
BOOL was_clicked = nHit == infoPtr->nButtonDown;
btnPtr = &infoPtr->buttons[infoPtr->nButtonDown];
btnPtr->fsState &= ~TBSTATE_PRESSED;
if (btnPtr->fsStyle & BTNS_CHECK) {
if (btnPtr->fsStyle & BTNS_GROUP) {
nOldIndex = TOOLBAR_GetCheckedGroupButtonIndex (infoPtr,
nHit);
if ((nOldIndex != nHit) &&
(nOldIndex != -1))
infoPtr->buttons[nOldIndex].fsState &= ~TBSTATE_CHECKED;
btnPtr->fsState |= TBSTATE_CHECKED;
}
else {
if (btnPtr->fsState & TBSTATE_CHECKED)
btnPtr->fsState &= ~TBSTATE_CHECKED;
else
btnPtr->fsState |= TBSTATE_CHECKED;
}
}
if (nOldIndex != -1)
InvalidateRect(infoPtr->hwndSelf, &infoPtr->buttons[nOldIndex].rect, TRUE);
/*
* now we can ReleaseCapture, which triggers CAPTURECHANGED msg,
* that resets bCaptured and btn TBSTATE_PRESSED flags,
* and obliterates nButtonDown and nOldHit (see TOOLBAR_CaptureChanged)
*/
if ((infoPtr->bCaptured) && (infoPtr->nButtonDown >= 0))
ReleaseCapture ();
infoPtr->nButtonDown = -1;
/* Issue NM_RELEASEDCAPTURE to parent to let him know it is released */
TOOLBAR_SendNotify (&hdr, infoPtr,
NM_RELEASEDCAPTURE);
memset(&nmtb, 0, sizeof(nmtb));
nmtb.iItem = btnPtr->idCommand;
TOOLBAR_SendNotify ((NMHDR *) &nmtb, infoPtr,
TBN_ENDDRAG);
if (was_clicked && btnPtr->fsState & TBSTATE_ENABLED)
{
SendMessageW (infoPtr->hwndNotify, WM_COMMAND,
MAKEWPARAM(infoPtr->buttons[nHit].idCommand, BN_CLICKED), (LPARAM)infoPtr->hwndSelf);
/* In case we have just been destroyed... */
if(!IsWindow(infoPtr->hwndSelf))
return 0;
}
}
/* !!! Undocumented - toolbar at 4.71 level and above sends
* NM_CLICK with the NMMOUSE structure. */
nmmouse.dwHitInfo = nHit;
if (!button)
nmmouse.dwItemSpec = -1;
else
{
nmmouse.dwItemSpec = infoPtr->buttons[nmmouse.dwHitInfo].idCommand;
nmmouse.dwItemData = infoPtr->buttons[nmmouse.dwHitInfo].dwData;
}
ClientToScreen(infoPtr->hwndSelf, &pt);
nmmouse.pt = pt;
if (!TOOLBAR_SendNotify((LPNMHDR)&nmmouse, infoPtr, NM_CLICK))
return DefWindowProcW(infoPtr->hwndSelf, WM_LBUTTONUP, wParam, lParam);
return 0;
}
static LRESULT
TOOLBAR_RButtonUp(TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
INT nHit;
NMMOUSE nmmouse;
POINT pt;
BOOL button;
pt.x = (short)LOWORD(lParam);
pt.y = (short)HIWORD(lParam);
nHit = TOOLBAR_InternalHitTest(infoPtr, &pt, &button);
nmmouse.dwHitInfo = nHit;
if (!button) {
nmmouse.dwItemSpec = -1;
} else {
nmmouse.dwItemSpec = infoPtr->buttons[nmmouse.dwHitInfo].idCommand;
nmmouse.dwItemData = infoPtr->buttons[nmmouse.dwHitInfo].dwData;
}
ClientToScreen(infoPtr->hwndSelf, &pt);
nmmouse.pt = pt;
if (!TOOLBAR_SendNotify((LPNMHDR)&nmmouse, infoPtr, NM_RCLICK))
return DefWindowProcW(infoPtr->hwndSelf, WM_RBUTTONUP, wParam, lParam);
return 0;
}
static LRESULT
TOOLBAR_RButtonDblClk( TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
NMHDR nmhdr;
if (!TOOLBAR_SendNotify(&nmhdr, infoPtr, NM_RDBLCLK))
return DefWindowProcW(infoPtr->hwndSelf, WM_RBUTTONDBLCLK, wParam, lParam);
return 0;
}
static LRESULT
TOOLBAR_CaptureChanged(TOOLBAR_INFO *infoPtr)
{
TBUTTON_INFO *btnPtr;
infoPtr->bCaptured = FALSE;
if (infoPtr->nButtonDown >= 0)
{
btnPtr = &infoPtr->buttons[infoPtr->nButtonDown];
btnPtr->fsState &= ~TBSTATE_PRESSED;
infoPtr->nOldHit = -1;
if (btnPtr->fsState & TBSTATE_ENABLED)
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
}
return 0;
}
static LRESULT
TOOLBAR_MouseLeave (TOOLBAR_INFO *infoPtr)
{
/* don't remove hot effects when in anchor highlighting mode or when a
* drop-down button is pressed */
if (infoPtr->nHotItem >= 0 && !infoPtr->bAnchor)
{
TBUTTON_INFO *hotBtnPtr = &infoPtr->buttons[infoPtr->nHotItem];
if (!hotBtnPtr->bDropDownPressed)
TOOLBAR_SetHotItemEx(infoPtr, TOOLBAR_NOWHERE, HICF_MOUSE);
}
if (infoPtr->nOldHit < 0)
return TRUE;
/* If the last button we were over is depressed then make it not */
/* depressed and redraw it */
if(infoPtr->nOldHit == infoPtr->nButtonDown)
{
TBUTTON_INFO *btnPtr;
RECT rc1;
btnPtr = &infoPtr->buttons[infoPtr->nButtonDown];
btnPtr->fsState &= ~TBSTATE_PRESSED;
rc1 = btnPtr->rect;
InflateRect (&rc1, 1, 1);
InvalidateRect (infoPtr->hwndSelf, &rc1, TRUE);
}
if (infoPtr->bCaptured && !infoPtr->bDragOutSent)
{
NMTOOLBARW nmt;
ZeroMemory(&nmt, sizeof(nmt));
nmt.iItem = infoPtr->buttons[infoPtr->nButtonDown].idCommand;
TOOLBAR_SendNotify(&nmt.hdr, infoPtr, TBN_DRAGOUT);
infoPtr->bDragOutSent = TRUE;
}
infoPtr->nOldHit = -1; /* reset the old hit index as we've left the toolbar */
return TRUE;
}
static LRESULT
TOOLBAR_MouseMove (TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
POINT pt;
TRACKMOUSEEVENT trackinfo;
INT nHit;
TBUTTON_INFO *btnPtr;
BOOL button;
if ((infoPtr->dwStyle & TBSTYLE_TOOLTIPS) && (infoPtr->hwndToolTip == NULL))
TOOLBAR_TooltipCreateControl(infoPtr);
if ((infoPtr->dwStyle & TBSTYLE_FLAT) || infoPtr->hTheme) {
/* fill in the TRACKMOUSEEVENT struct */
trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
trackinfo.dwFlags = TME_QUERY;
/* call _TrackMouseEvent to see if we are currently tracking for this hwnd */
_TrackMouseEvent(&trackinfo);
/* Make sure tracking is enabled so we receive a WM_MOUSELEAVE message */
if(trackinfo.hwndTrack != infoPtr->hwndSelf || !(trackinfo.dwFlags & TME_LEAVE)) {
trackinfo.dwFlags = TME_LEAVE; /* notify upon leaving */
trackinfo.hwndTrack = infoPtr->hwndSelf;
/* call TRACKMOUSEEVENT so we receive a WM_MOUSELEAVE message */
/* and can properly deactivate the hot toolbar button */
_TrackMouseEvent(&trackinfo);
}
}
if (infoPtr->hwndToolTip)
TOOLBAR_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwndSelf,
WM_MOUSEMOVE, wParam, lParam);
pt.x = (short)LOWORD(lParam);
pt.y = (short)HIWORD(lParam);
nHit = TOOLBAR_InternalHitTest (infoPtr, &pt, &button);
if (((infoPtr->dwStyle & TBSTYLE_FLAT) || infoPtr->hTheme) && (!infoPtr->bAnchor || button))
TOOLBAR_SetHotItemEx(infoPtr, button ? nHit : TOOLBAR_NOWHERE, HICF_MOUSE);
if (infoPtr->nOldHit != nHit)
{
if (infoPtr->bCaptured)
{
if (!infoPtr->bDragOutSent)
{
NMTOOLBARW nmt;
ZeroMemory(&nmt, sizeof(nmt));
nmt.iItem = infoPtr->buttons[infoPtr->nButtonDown].idCommand;
TOOLBAR_SendNotify(&nmt.hdr, infoPtr, TBN_DRAGOUT);
infoPtr->bDragOutSent = TRUE;
}
btnPtr = &infoPtr->buttons[infoPtr->nButtonDown];
if (infoPtr->nOldHit == infoPtr->nButtonDown) {
btnPtr->fsState &= ~TBSTATE_PRESSED;
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
}
else if (nHit == infoPtr->nButtonDown) {
btnPtr->fsState |= TBSTATE_PRESSED;
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
}
infoPtr->nOldHit = nHit;
}
}
return 0;
}
static inline LRESULT
TOOLBAR_NCActivate (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
/* if (wndPtr->dwStyle & CCS_NODIVIDER) */
return DefWindowProcW (hwnd, WM_NCACTIVATE, wParam, lParam);
/* else */
/* return TOOLBAR_NCPaint (wndPtr, wParam, lParam); */
}
static inline LRESULT
TOOLBAR_NCCalcSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
if (!(GetWindowLongW(hwnd, GWL_STYLE) & CCS_NODIVIDER))
((LPRECT)lParam)->top += GetSystemMetrics(SM_CYEDGE);
return DefWindowProcW (hwnd, WM_NCCALCSIZE, wParam, lParam);
}
static LRESULT
TOOLBAR_NCCreate (HWND hwnd, WPARAM wParam, const CREATESTRUCTW *lpcs)
{
TOOLBAR_INFO *infoPtr;
DWORD styleadd = 0;
/* allocate memory for info structure */
infoPtr = Alloc (sizeof(TOOLBAR_INFO));
SetWindowLongPtrW (hwnd, 0, (LONG_PTR)infoPtr);
/* paranoid!! */
infoPtr->dwStructSize = sizeof(TBBUTTON);
infoPtr->nRows = 1;
/* initialize info structure */
infoPtr->nButtonWidth = 23;
infoPtr->nButtonHeight = 22;
infoPtr->nBitmapHeight = 16;
infoPtr->nBitmapWidth = 16;
infoPtr->nMaxTextRows = 1;
infoPtr->cxMin = -1;
infoPtr->cxMax = -1;
infoPtr->nNumBitmaps = 0;
infoPtr->nNumStrings = 0;
infoPtr->bCaptured = FALSE;
infoPtr->nButtonDown = -1;
infoPtr->nButtonDrag = -1;
infoPtr->nOldHit = -1;
infoPtr->nHotItem = -1;
infoPtr->hwndNotify = lpcs->hwndParent;
infoPtr->dwDTFlags = (lpcs->style & TBSTYLE_LIST) ? DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS: DT_CENTER | DT_END_ELLIPSIS;
infoPtr->bAnchor = FALSE; /* no anchor highlighting */
infoPtr->bDragOutSent = FALSE;
infoPtr->iVersion = 0;
infoPtr->hwndSelf = hwnd;
infoPtr->bDoRedraw = TRUE;
infoPtr->clrBtnHighlight = CLR_DEFAULT;
infoPtr->clrBtnShadow = CLR_DEFAULT;
infoPtr->szPadding.cx = DEFPAD_CX;
infoPtr->szPadding.cy = DEFPAD_CY;
infoPtr->iListGap = DEFLISTGAP;
infoPtr->iTopMargin = default_top_margin(infoPtr);
infoPtr->dwStyle = lpcs->style;
infoPtr->tbim.iButton = -1;
/* fix instance handle, if the toolbar was created by CreateToolbarEx() */
if (!GetWindowLongPtrW (hwnd, GWLP_HINSTANCE)) {
HINSTANCE hInst = (HINSTANCE)GetWindowLongPtrW (GetParent (hwnd), GWLP_HINSTANCE);
SetWindowLongPtrW (hwnd, GWLP_HINSTANCE, (LONG_PTR)hInst);
}
/* I think the code below is a bug, but it is the way that the native
* controls seem to work. The effect is that if the user of TBSTYLE_FLAT
* forgets to specify TBSTYLE_TRANSPARENT but does specify either
* CCS_TOP or CCS_BOTTOM (_NOMOVEY and _TOP), then the control
* does *not* set TBSTYLE_TRANSPARENT even though it should!!!!
* Somehow, the only cases of this seem to be MFC programs.
*
* Note also that the addition of _TRANSPARENT occurs *only* here. It
* does not occur in the WM_STYLECHANGING routine.
* (Guy Albertelli 9/2001)
*
*/
if ((infoPtr->dwStyle & TBSTYLE_FLAT) && !(lpcs->style & TBSTYLE_TRANSPARENT))
styleadd |= TBSTYLE_TRANSPARENT;
if (!(lpcs->style & (CCS_TOP | CCS_NOMOVEY))) {
styleadd |= CCS_TOP; /* default to top */
SetWindowLongW (hwnd, GWL_STYLE, lpcs->style | styleadd);
}
return DefWindowProcW (hwnd, WM_NCCREATE, wParam, (LPARAM)lpcs);
}
static LRESULT
TOOLBAR_NCPaint (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
DWORD dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
RECT rcWindow;
HDC hdc;
if (dwStyle & WS_MINIMIZE)
return 0; /* Nothing to do */
DefWindowProcW (hwnd, WM_NCPAINT, wParam, lParam);
if (!(hdc = GetDCEx (hwnd, 0, DCX_USESTYLE | DCX_WINDOW)))
return 0;
if (!(dwStyle & CCS_NODIVIDER))
{
GetWindowRect (hwnd, &rcWindow);
OffsetRect (&rcWindow, -rcWindow.left, -rcWindow.top);
if( dwStyle & WS_BORDER )
InflateRect (&rcWindow, -1, -1);
DrawEdge (hdc, &rcWindow, EDGE_ETCHED, BF_TOP);
}
ReleaseDC( hwnd, hdc );
return 0;
}
/* handles requests from the tooltip control on what text to display */
static LRESULT TOOLBAR_TTGetDispInfo (TOOLBAR_INFO *infoPtr, NMTTDISPINFOW *lpnmtdi)
{
int index = TOOLBAR_GetButtonIndex(infoPtr, lpnmtdi->hdr.idFrom, FALSE);
NMTTDISPINFOA nmtdi;
unsigned int len;
LRESULT ret;
TRACE("button index = %d\n", index);
Free (infoPtr->pszTooltipText);
infoPtr->pszTooltipText = NULL;
if (index < 0)
return 0;
if (infoPtr->bUnicode)
{
WCHAR wszBuffer[INFOTIPSIZE+1];
NMTBGETINFOTIPW tbgit;
wszBuffer[0] = '\0';
wszBuffer[INFOTIPSIZE] = '\0';
tbgit.pszText = wszBuffer;
tbgit.cchTextMax = INFOTIPSIZE;
tbgit.iItem = lpnmtdi->hdr.idFrom;
tbgit.lParam = infoPtr->buttons[index].dwData;
TOOLBAR_SendNotify(&tbgit.hdr, infoPtr, TBN_GETINFOTIPW);
TRACE("TBN_GETINFOTIPW - got string %s\n", debugstr_w(tbgit.pszText));
len = tbgit.pszText ? lstrlenW(tbgit.pszText) : 0;
if (len > ARRAY_SIZE(lpnmtdi->szText) - 1)
{
/* need to allocate temporary buffer in infoPtr as there
* isn't enough space in buffer passed to us by the
* tooltip control */
infoPtr->pszTooltipText = Alloc((len+1)*sizeof(WCHAR));
if (infoPtr->pszTooltipText)
{
memcpy(infoPtr->pszTooltipText, tbgit.pszText, (len+1)*sizeof(WCHAR));
lpnmtdi->lpszText = infoPtr->pszTooltipText;
return 0;
}
}
else if (len > 0)
{
memcpy(lpnmtdi->lpszText, tbgit.pszText, (len+1)*sizeof(WCHAR));
return 0;
}
}
else
{
CHAR szBuffer[INFOTIPSIZE+1];
NMTBGETINFOTIPA tbgit;
szBuffer[0] = '\0';
szBuffer[INFOTIPSIZE] = '\0';
tbgit.pszText = szBuffer;
tbgit.cchTextMax = INFOTIPSIZE;
tbgit.iItem = lpnmtdi->hdr.idFrom;
tbgit.lParam = infoPtr->buttons[index].dwData;
TOOLBAR_SendNotify(&tbgit.hdr, infoPtr, TBN_GETINFOTIPA);
TRACE("TBN_GETINFOTIPA - got string %s\n", debugstr_a(tbgit.pszText));
len = MultiByteToWideChar(CP_ACP, 0, tbgit.pszText, -1, NULL, 0);
if (len > ARRAY_SIZE(lpnmtdi->szText))
{
/* need to allocate temporary buffer in infoPtr as there
* isn't enough space in buffer passed to us by the
* tooltip control */
infoPtr->pszTooltipText = Alloc(len*sizeof(WCHAR));
if (infoPtr->pszTooltipText)
{
MultiByteToWideChar(CP_ACP, 0, tbgit.pszText, -1, infoPtr->pszTooltipText, len);
lpnmtdi->lpszText = infoPtr->pszTooltipText;
return 0;
}
}
else if (tbgit.pszText && tbgit.pszText[0])
{
MultiByteToWideChar(CP_ACP, 0, tbgit.pszText, -1, lpnmtdi->lpszText, ARRAY_SIZE(lpnmtdi->szText));
return 0;
}
}
/* if button has text, but it is not shown then automatically
* use that text as tooltip */
if ((infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) &&
!(infoPtr->buttons[index].fsStyle & BTNS_SHOWTEXT))
{
LPWSTR pszText = TOOLBAR_GetText(infoPtr, &infoPtr->buttons[index]);
len = pszText ? lstrlenW(pszText) : 0;
TRACE("using button hidden text %s\n", debugstr_w(pszText));
if (len > ARRAY_SIZE(lpnmtdi->szText) - 1)
{
/* need to allocate temporary buffer in infoPtr as there
* isn't enough space in buffer passed to us by the
* tooltip control */
infoPtr->pszTooltipText = Alloc((len+1)*sizeof(WCHAR));
if (infoPtr->pszTooltipText)
{
memcpy(infoPtr->pszTooltipText, pszText, (len+1)*sizeof(WCHAR));
lpnmtdi->lpszText = infoPtr->pszTooltipText;
return 0;
}
}
else if (len > 0)
{
memcpy(lpnmtdi->lpszText, pszText, (len+1)*sizeof(WCHAR));
return 0;
}
}
TRACE("Sending tooltip notification to %p\n", infoPtr->hwndNotify);
/* Last resort, forward TTN_GETDISPINFO to the app:
- NFR_UNICODE gets TTN_GETDISPINFOW, and TTN_GETDISPINFOA if -W returned no text;
- NFR_ANSI gets only TTN_GETDISPINFOA.
*/
if (infoPtr->bUnicode)
{
ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, lpnmtdi->hdr.idFrom, (LPARAM)lpnmtdi);
TRACE("TTN_GETDISPINFOW - got string %s\n", debugstr_w(lpnmtdi->lpszText));
if (IS_INTRESOURCE(lpnmtdi->lpszText))
return ret;
if (lpnmtdi->lpszText && *lpnmtdi->lpszText)
return ret;
}
nmtdi.hdr.hwndFrom = lpnmtdi->hdr.hwndFrom;
nmtdi.hdr.idFrom = lpnmtdi->hdr.idFrom;
nmtdi.hdr.code = TTN_GETDISPINFOA;
nmtdi.lpszText = nmtdi.szText;
nmtdi.szText[0] = 0;
nmtdi.hinst = lpnmtdi->hinst;
nmtdi.uFlags = lpnmtdi->uFlags;
nmtdi.lParam = lpnmtdi->lParam;
ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmtdi.hdr.idFrom, (LPARAM)&nmtdi);
TRACE("TTN_GETDISPINFOA - got string %s\n", debugstr_a(nmtdi.lpszText));
lpnmtdi->hinst = nmtdi.hinst;
lpnmtdi->uFlags = nmtdi.uFlags;
lpnmtdi->lParam = nmtdi.lParam;
if (IS_INTRESOURCE(nmtdi.lpszText))
{
lpnmtdi->lpszText = (WCHAR *)nmtdi.lpszText;
return ret;
}
if (!nmtdi.lpszText || !*nmtdi.lpszText)
return ret;
len = MultiByteToWideChar(CP_ACP, 0, nmtdi.lpszText, -1, NULL, 0);
if (len > ARRAY_SIZE(lpnmtdi->szText))
{
infoPtr->pszTooltipText = Alloc(len * sizeof(WCHAR));
if (infoPtr->pszTooltipText)
{
MultiByteToWideChar(CP_ACP, 0, nmtdi.lpszText, -1, infoPtr->pszTooltipText, len);
lpnmtdi->lpszText = infoPtr->pszTooltipText;
return 0;
}
}
else
{
MultiByteToWideChar(CP_ACP, 0, nmtdi.lpszText, -1, lpnmtdi->lpszText, ARRAY_SIZE(nmtdi.szText));
return 0;
}
return ret;
}
static inline LRESULT
TOOLBAR_Notify (TOOLBAR_INFO *infoPtr, LPNMHDR lpnmh)
{
switch (lpnmh->code)
{
case PGN_CALCSIZE:
{
LPNMPGCALCSIZE lppgc = (LPNMPGCALCSIZE)lpnmh;
if (lppgc->dwFlag == PGF_CALCWIDTH) {
lppgc->iWidth = infoPtr->rcBound.right - infoPtr->rcBound.left;
TRACE("processed PGN_CALCSIZE, returning horz size = %d\n",
lppgc->iWidth);
}
else {
lppgc->iHeight = infoPtr->rcBound.bottom - infoPtr->rcBound.top;
TRACE("processed PGN_CALCSIZE, returning vert size = %d\n",
lppgc->iHeight);
}
return 0;
}
case PGN_SCROLL:
{
LPNMPGSCROLL lppgs = (LPNMPGSCROLL)lpnmh;
lppgs->iScroll = (lppgs->iDir & (PGF_SCROLLLEFT | PGF_SCROLLRIGHT)) ?
infoPtr->nButtonWidth : infoPtr->nButtonHeight;
TRACE("processed PGN_SCROLL, returning scroll=%d, dir=%d\n",
lppgs->iScroll, lppgs->iDir);
return 0;
}
case TTN_GETDISPINFOW:
return TOOLBAR_TTGetDispInfo(infoPtr, (LPNMTTDISPINFOW)lpnmh);
case TTN_GETDISPINFOA:
FIXME("TTN_GETDISPINFOA - should not be received; please report\n");
return 0;
default:
return 0;
}
}
static LRESULT
TOOLBAR_NotifyFormat(const TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
LRESULT format;
TRACE("wParam %Ix, lParam %Ix\n", wParam, lParam);
if (lParam == NF_QUERY)
return NFR_UNICODE;
if (lParam == NF_REQUERY) {
format = SendMessageW(infoPtr->hwndNotify,
WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
if ((format != NFR_ANSI) && (format != NFR_UNICODE)) {
ERR("wrong response to WM_NOTIFYFORMAT (%Id), assuming ANSI\n", format);
format = NFR_ANSI;
}
return format;
}
return 0;
}
static LRESULT
TOOLBAR_Paint (TOOLBAR_INFO *infoPtr, WPARAM wParam)
{
HDC hdc;
PAINTSTRUCT ps;
/* fill ps.rcPaint with a default rect */
ps.rcPaint = infoPtr->rcBound;
hdc = wParam==0 ? BeginPaint(infoPtr->hwndSelf, &ps) : (HDC)wParam;
TRACE("psrect=(%s)\n", wine_dbgstr_rect(&ps.rcPaint));
TOOLBAR_Refresh (infoPtr, hdc, &ps);
if (!wParam) EndPaint (infoPtr->hwndSelf, &ps);
return 0;
}
static LRESULT
TOOLBAR_SetFocus (TOOLBAR_INFO *infoPtr)
{
TRACE("nHotItem = %d\n", infoPtr->nHotItem);
/* make first item hot */
if (infoPtr->nNumButtons > 0)
TOOLBAR_SetHotItemEx(infoPtr, 0, HICF_OTHER);
return 0;
}
static LRESULT
TOOLBAR_SetFont(TOOLBAR_INFO *infoPtr, HFONT hFont, WORD Redraw)
{
TRACE("font=%p redraw=%d\n", hFont, Redraw);
if (hFont == 0)
infoPtr->hFont = infoPtr->hDefaultFont;
else
infoPtr->hFont = hFont;
TOOLBAR_CalcToolbar(infoPtr);
if (Redraw)
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return 1;
}
static LRESULT
TOOLBAR_SetRedraw (TOOLBAR_INFO *infoPtr, WPARAM wParam)
/*****************************************************
*
* Function;
* Handles the WM_SETREDRAW message.
*
* Documentation:
* According to testing V4.71 of COMCTL32 returns the
* *previous* status of the redraw flag (either 0 or 1)
* instead of the MSDN documented value of 0 if handled.
* (For laughs see the "consistency" with same function
* in rebar.)
*
*****************************************************/
{
BOOL oldredraw = infoPtr->bDoRedraw;
TRACE("set to %s\n",
(wParam) ? "TRUE" : "FALSE");
infoPtr->bDoRedraw = (BOOL) wParam;
if (wParam) {
InvalidateRect (infoPtr->hwndSelf, 0, TRUE);
}
return (oldredraw) ? 1 : 0;
}
static LRESULT
TOOLBAR_Size (TOOLBAR_INFO *infoPtr)
{
TRACE("sizing toolbar\n");
if (infoPtr->dwExStyle & TBSTYLE_EX_HIDECLIPPEDBUTTONS)
{
RECT delta_width, delta_height, client, dummy;
DWORD min_x, max_x, min_y, max_y;
TBUTTON_INFO *btnPtr;
INT i;
GetClientRect(infoPtr->hwndSelf, &client);
if(client.right > infoPtr->client_rect.right)
{
min_x = infoPtr->client_rect.right;
max_x = client.right;
}
else
{
max_x = infoPtr->client_rect.right;
min_x = client.right;
}
if(client.bottom > infoPtr->client_rect.bottom)
{
min_y = infoPtr->client_rect.bottom;
max_y = client.bottom;
}
else
{
max_y = infoPtr->client_rect.bottom;
min_y = client.bottom;
}
SetRect(&delta_width, min_x, 0, max_x, min_y);
SetRect(&delta_height, 0, min_y, max_x, max_y);
TRACE("delta_width %s delta_height %s\n", wine_dbgstr_rect(&delta_width), wine_dbgstr_rect(&delta_height));
btnPtr = infoPtr->buttons;
for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++)
if(IntersectRect(&dummy, &delta_width, &btnPtr->rect) ||
IntersectRect(&dummy, &delta_height, &btnPtr->rect))
InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
}
GetClientRect(infoPtr->hwndSelf, &infoPtr->client_rect);
TOOLBAR_AutoSize(infoPtr);
return 0;
}
static LRESULT
TOOLBAR_StyleChanged (TOOLBAR_INFO *infoPtr, INT nType, const STYLESTRUCT *lpStyle)
{
if (nType == GWL_STYLE)
return TOOLBAR_SetStyle(infoPtr, lpStyle->styleNew);
return 0;
}
static LRESULT
TOOLBAR_SysColorChange (void)
{
COMCTL32_RefreshSysColors();
return 0;
}
/* update theme after a WM_THEMECHANGED message */
static LRESULT theme_changed (TOOLBAR_INFO *infoPtr)
{
CloseThemeData (infoPtr->hTheme);
infoPtr->hTheme = OpenThemeDataForDpi (NULL, themeClass, GetDpiForWindow (infoPtr->hwndSelf));
InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
return 0;
}
static LRESULT WINAPI
ToolbarWindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
TOOLBAR_INFO *infoPtr = (TOOLBAR_INFO *)GetWindowLongPtrW(hwnd, 0);
TRACE("hwnd %p, msg %x, wparam %Ix, lparam %Ix\n", hwnd, uMsg, wParam, lParam);
if (!infoPtr && (uMsg != WM_NCCREATE))
return DefWindowProcW( hwnd, uMsg, wParam, lParam );
switch (uMsg)
{
case TB_ADDBITMAP:
return TOOLBAR_AddBitmap (infoPtr, (INT)wParam, (TBADDBITMAP*)lParam);
case TB_ADDBUTTONSA:
case TB_ADDBUTTONSW:
return TOOLBAR_AddButtonsT (infoPtr, wParam, (LPTBBUTTON)lParam,
uMsg == TB_ADDBUTTONSW);
case TB_ADDSTRINGA:
return TOOLBAR_AddStringA (infoPtr, (HINSTANCE)wParam, lParam);
case TB_ADDSTRINGW:
return TOOLBAR_AddStringW (infoPtr, (HINSTANCE)wParam, lParam);
case TB_AUTOSIZE:
return TOOLBAR_AutoSize (infoPtr);
case TB_BUTTONCOUNT:
return TOOLBAR_ButtonCount (infoPtr);
case TB_BUTTONSTRUCTSIZE:
return TOOLBAR_ButtonStructSize (infoPtr, wParam);
case TB_CHANGEBITMAP:
return TOOLBAR_ChangeBitmap (infoPtr, wParam, LOWORD(lParam));
case TB_CHECKBUTTON:
return TOOLBAR_CheckButton (infoPtr, wParam, lParam);
case TB_COMMANDTOINDEX:
return TOOLBAR_CommandToIndex (infoPtr, wParam);
case TB_CUSTOMIZE:
return TOOLBAR_Customize (infoPtr);
case TB_DELETEBUTTON:
return TOOLBAR_DeleteButton (infoPtr, wParam);
case TB_ENABLEBUTTON:
return TOOLBAR_EnableButton (infoPtr, wParam, lParam);
case TB_GETANCHORHIGHLIGHT:
return TOOLBAR_GetAnchorHighlight (infoPtr);
case TB_GETBITMAP:
return TOOLBAR_GetBitmap (infoPtr, wParam);
case TB_GETBITMAPFLAGS:
return TOOLBAR_GetBitmapFlags ();
case TB_GETBUTTON:
return TOOLBAR_GetButton (infoPtr, wParam, (TBBUTTON*)lParam);
case TB_GETBUTTONINFOA:
case TB_GETBUTTONINFOW:
return TOOLBAR_GetButtonInfoT (infoPtr, wParam, (LPTBBUTTONINFOW)lParam,
uMsg == TB_GETBUTTONINFOW);
case TB_GETBUTTONSIZE:
return TOOLBAR_GetButtonSize (infoPtr);
case TB_GETBUTTONTEXTA:
case TB_GETBUTTONTEXTW:
return TOOLBAR_GetButtonText (infoPtr, wParam, (LPWSTR)lParam,
uMsg == TB_GETBUTTONTEXTW);
case TB_GETDISABLEDIMAGELIST:
return TOOLBAR_GetDisabledImageList (infoPtr, wParam);
case TB_GETEXTENDEDSTYLE:
return TOOLBAR_GetExtendedStyle (infoPtr);
case TB_GETHOTIMAGELIST:
return TOOLBAR_GetHotImageList (infoPtr, wParam);
case TB_GETHOTITEM:
return TOOLBAR_GetHotItem (infoPtr);
case TB_GETIMAGELIST:
return TOOLBAR_GetDefImageList (infoPtr, wParam);
case TB_GETINSERTMARK:
return TOOLBAR_GetInsertMark (infoPtr, (TBINSERTMARK*)lParam);
case TB_GETINSERTMARKCOLOR:
return TOOLBAR_GetInsertMarkColor (infoPtr);
case TB_GETITEMRECT:
return TOOLBAR_GetItemRect (infoPtr, wParam, (LPRECT)lParam);
case TB_GETMAXSIZE:
return TOOLBAR_GetMaxSize (infoPtr, (LPSIZE)lParam);
/* case TB_GETOBJECT: */ /* 4.71 */
case TB_GETPADDING:
return TOOLBAR_GetPadding (infoPtr);
case TB_GETRECT:
return TOOLBAR_GetRect (infoPtr, wParam, (LPRECT)lParam);
case TB_GETROWS:
return TOOLBAR_GetRows (infoPtr);
case TB_GETSTATE:
return TOOLBAR_GetState (infoPtr, wParam);
case TB_GETSTRINGA:
return TOOLBAR_GetStringA (infoPtr, wParam, (LPSTR)lParam);
case TB_GETSTRINGW:
return TOOLBAR_GetStringW (infoPtr, wParam, (LPWSTR)lParam);
case TB_GETSTYLE:
return TOOLBAR_GetStyle (infoPtr);
case TB_GETTEXTROWS:
return TOOLBAR_GetTextRows (infoPtr);
case TB_GETTOOLTIPS:
return TOOLBAR_GetToolTips (infoPtr);
case TB_GETUNICODEFORMAT:
return TOOLBAR_GetUnicodeFormat (infoPtr);
case TB_HIDEBUTTON:
return TOOLBAR_HideButton (infoPtr, wParam, LOWORD(lParam));
case TB_HITTEST:
return TOOLBAR_HitTest (infoPtr, (LPPOINT)lParam);
case TB_INDETERMINATE:
return TOOLBAR_Indeterminate (infoPtr, wParam, LOWORD(lParam));
case TB_INSERTBUTTONA:
case TB_INSERTBUTTONW:
return TOOLBAR_InsertButtonT(infoPtr, wParam, (TBBUTTON*)lParam,
uMsg == TB_INSERTBUTTONW);
/* case TB_INSERTMARKHITTEST: */ /* 4.71 */
case TB_ISBUTTONCHECKED:
return TOOLBAR_IsButtonChecked (infoPtr, wParam);
case TB_ISBUTTONENABLED:
return TOOLBAR_IsButtonEnabled (infoPtr, wParam);
case TB_ISBUTTONHIDDEN:
return TOOLBAR_IsButtonHidden (infoPtr, wParam);
case TB_ISBUTTONHIGHLIGHTED:
return TOOLBAR_IsButtonHighlighted (infoPtr, wParam);
case TB_ISBUTTONINDETERMINATE:
return TOOLBAR_IsButtonIndeterminate (infoPtr, wParam);
case TB_ISBUTTONPRESSED:
return TOOLBAR_IsButtonPressed (infoPtr, wParam);
case TB_LOADIMAGES:
return TOOLBAR_LoadImages (infoPtr, wParam, (HINSTANCE)lParam);
case TB_MAPACCELERATORA:
case TB_MAPACCELERATORW:
return TOOLBAR_MapAccelerator (infoPtr, wParam, (UINT*)lParam);
case TB_MARKBUTTON:
return TOOLBAR_MarkButton (infoPtr, wParam, LOWORD(lParam));
case TB_MOVEBUTTON:
return TOOLBAR_MoveButton (infoPtr, wParam, lParam);
case TB_PRESSBUTTON:
return TOOLBAR_PressButton (infoPtr, wParam, LOWORD(lParam));
case TB_REPLACEBITMAP:
return TOOLBAR_ReplaceBitmap (infoPtr, (LPTBREPLACEBITMAP)lParam);
case TB_SAVERESTOREA:
return TOOLBAR_SaveRestoreA (infoPtr, wParam, (LPTBSAVEPARAMSA)lParam);
case TB_SAVERESTOREW:
return TOOLBAR_SaveRestoreW (infoPtr, wParam, (LPTBSAVEPARAMSW)lParam);
case TB_SETANCHORHIGHLIGHT:
return TOOLBAR_SetAnchorHighlight (infoPtr, (BOOL)wParam);
case TB_SETBITMAPSIZE:
return TOOLBAR_SetBitmapSize (infoPtr, wParam, lParam);
case TB_SETBUTTONINFOA:
case TB_SETBUTTONINFOW:
return TOOLBAR_SetButtonInfo (infoPtr, wParam, (LPTBBUTTONINFOW)lParam,
uMsg == TB_SETBUTTONINFOW);
case TB_SETBUTTONSIZE:
return TOOLBAR_SetButtonSize (infoPtr, lParam);
case TB_SETBUTTONWIDTH:
return TOOLBAR_SetButtonWidth (infoPtr, lParam);
case TB_SETCMDID:
return TOOLBAR_SetCmdId (infoPtr, wParam, lParam);
case TB_SETDISABLEDIMAGELIST:
return TOOLBAR_SetDisabledImageList (infoPtr, wParam, (HIMAGELIST)lParam);
case TB_SETDRAWTEXTFLAGS:
return TOOLBAR_SetDrawTextFlags (infoPtr, wParam, lParam);
case TB_SETEXTENDEDSTYLE:
return TOOLBAR_SetExtendedStyle (infoPtr, wParam, lParam);
case TB_SETHOTIMAGELIST:
return TOOLBAR_SetHotImageList (infoPtr, wParam, (HIMAGELIST)lParam);
case TB_SETHOTITEM:
return TOOLBAR_SetHotItem (infoPtr, wParam);
case TB_SETIMAGELIST:
return TOOLBAR_SetImageList (infoPtr, wParam, (HIMAGELIST)lParam);
case TB_SETINDENT:
return TOOLBAR_SetIndent (infoPtr, wParam);
case TB_SETINSERTMARK:
return TOOLBAR_SetInsertMark (infoPtr, (TBINSERTMARK*)lParam);
case TB_SETINSERTMARKCOLOR:
return TOOLBAR_SetInsertMarkColor (infoPtr, lParam);
case TB_SETMAXTEXTROWS:
return TOOLBAR_SetMaxTextRows (infoPtr, wParam);
case TB_SETPADDING:
return TOOLBAR_SetPadding (infoPtr, lParam);
case TB_SETPARENT:
return TOOLBAR_SetParent (infoPtr, (HWND)wParam);
case TB_SETROWS:
return TOOLBAR_SetRows (infoPtr, wParam, (LPRECT)lParam);
case TB_SETSTATE:
return TOOLBAR_SetState (infoPtr, wParam, lParam);
case TB_SETSTYLE:
return TOOLBAR_SetStyle (infoPtr, lParam);
case TB_SETTOOLTIPS:
return TOOLBAR_SetToolTips (infoPtr, (HWND)wParam);
case TB_SETUNICODEFORMAT:
return TOOLBAR_SetUnicodeFormat (infoPtr, wParam);
case TB_SETBOUNDINGSIZE:
return TOOLBAR_SetBoundingSize(hwnd, wParam, lParam);
case TB_SETHOTITEM2:
return TOOLBAR_SetHotItem2 (infoPtr, wParam, lParam);
case TB_SETLISTGAP:
return TOOLBAR_SetListGap(infoPtr, wParam);
case TB_GETIMAGELISTCOUNT:
return TOOLBAR_GetImageListCount(infoPtr);
case TB_GETIDEALSIZE:
return TOOLBAR_GetIdealSize (infoPtr, wParam, lParam);
case TB_UNKWN464:
return TOOLBAR_Unkwn464(hwnd, wParam, lParam);
/* Common Control Messages */
/* case TB_GETCOLORSCHEME: */ /* identical to CCM_ */
case CCM_GETCOLORSCHEME:
return TOOLBAR_GetColorScheme (infoPtr, (LPCOLORSCHEME)lParam);
/* case TB_SETCOLORSCHEME: */ /* identical to CCM_ */
case CCM_SETCOLORSCHEME:
return TOOLBAR_SetColorScheme (infoPtr, (LPCOLORSCHEME)lParam);
case CCM_GETVERSION:
return TOOLBAR_GetVersion (infoPtr);
case CCM_SETVERSION:
return TOOLBAR_SetVersion (infoPtr, (INT)wParam);
/* case WM_CHAR: */
case WM_CREATE:
return TOOLBAR_Create (hwnd, (CREATESTRUCTW*)lParam);
case WM_DESTROY:
return TOOLBAR_Destroy (infoPtr);
case WM_ERASEBKGND:
return TOOLBAR_EraseBackground (infoPtr, wParam, lParam);
case WM_GETFONT:
return TOOLBAR_GetFont (infoPtr);
case WM_KEYDOWN:
return TOOLBAR_KeyDown (infoPtr, wParam, lParam);
/* case WM_KILLFOCUS: */
case WM_LBUTTONDBLCLK:
return TOOLBAR_LButtonDblClk (infoPtr, wParam, lParam);
case WM_LBUTTONDOWN:
return TOOLBAR_LButtonDown (infoPtr, wParam, lParam);
case WM_LBUTTONUP:
return TOOLBAR_LButtonUp (infoPtr, wParam, lParam);
case WM_RBUTTONUP:
return TOOLBAR_RButtonUp (infoPtr, wParam, lParam);
case WM_RBUTTONDBLCLK:
return TOOLBAR_RButtonDblClk (infoPtr, wParam, lParam);
case WM_MOUSEMOVE:
return TOOLBAR_MouseMove (infoPtr, wParam, lParam);
case WM_MOUSELEAVE:
return TOOLBAR_MouseLeave (infoPtr);
case WM_CAPTURECHANGED:
if (hwnd == (HWND)lParam) return 0;
return TOOLBAR_CaptureChanged(infoPtr);
case WM_NCACTIVATE:
return TOOLBAR_NCActivate (hwnd, wParam, lParam);
case WM_NCCALCSIZE:
return TOOLBAR_NCCalcSize (hwnd, wParam, lParam);
case WM_NCCREATE:
return TOOLBAR_NCCreate (hwnd, wParam, (CREATESTRUCTW*)lParam);
case WM_NCPAINT:
return TOOLBAR_NCPaint (hwnd, wParam, lParam);
case WM_NOTIFY:
return TOOLBAR_Notify (infoPtr, (LPNMHDR)lParam);
case WM_NOTIFYFORMAT:
return TOOLBAR_NotifyFormat (infoPtr, wParam, lParam);
case WM_PRINTCLIENT:
case WM_PAINT:
return TOOLBAR_Paint (infoPtr, wParam);
case WM_SETFOCUS:
return TOOLBAR_SetFocus (infoPtr);
case WM_SETFONT:
return TOOLBAR_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
case WM_SETREDRAW:
return TOOLBAR_SetRedraw (infoPtr, wParam);
case WM_SIZE:
return TOOLBAR_Size (infoPtr);
case WM_STYLECHANGED:
return TOOLBAR_StyleChanged (infoPtr, (INT)wParam, (LPSTYLESTRUCT)lParam);
case WM_SYSCOLORCHANGE:
return TOOLBAR_SysColorChange ();
case WM_THEMECHANGED:
return theme_changed (infoPtr);
/* case WM_WININICHANGE: */
case WM_CHARTOITEM:
case WM_COMMAND:
case WM_DRAWITEM:
case WM_MEASUREITEM:
case WM_VKEYTOITEM:
return SendMessageW (infoPtr->hwndNotify, uMsg, wParam, lParam);
/* We see this in Outlook Express 5.x and just does DefWindowProc */
case PGM_FORWARDMOUSE:
return DefWindowProcW (hwnd, uMsg, wParam, lParam);
default:
if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
ERR("unknown msg %04x, wp %Ix, lp %Ix\n", uMsg, wParam, lParam);
return DefWindowProcW (hwnd, uMsg, wParam, lParam);
}
}
VOID
TOOLBAR_Register (void)
{
WNDCLASSW wndClass;
ZeroMemory (&wndClass, sizeof(WNDCLASSW));
wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
wndClass.lpfnWndProc = ToolbarWindowProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = sizeof(TOOLBAR_INFO *);
wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wndClass.lpszClassName = TOOLBARCLASSNAMEW;
RegisterClassW (&wndClass);
}
VOID
TOOLBAR_Unregister (void)
{
UnregisterClassW (TOOLBARCLASSNAMEW, NULL);
}
static HIMAGELIST TOOLBAR_InsertImageList(PIMLENTRY **pies, INT *cies, HIMAGELIST himl, INT id)
{
HIMAGELIST himlold;
PIMLENTRY c = NULL;
/* Check if the entry already exists */
c = TOOLBAR_GetImageListEntry(*pies, *cies, id);
/* Don't add new entry for NULL imagelist */
if (!c && !himl)
return NULL;
/* If this is a new entry we must create it and insert into the array */
if (!c)
{
PIMLENTRY *pnies;
c = Alloc(sizeof(IMLENTRY));
c->id = id;
pnies = Alloc((*cies + 1) * sizeof(PIMLENTRY));
memcpy(pnies, *pies, ((*cies) * sizeof(PIMLENTRY)));
pnies[*cies] = c;
(*cies)++;
Free(*pies);
*pies = pnies;
}
himlold = c->himl;
c->himl = himl;
return himlold;
}
static VOID TOOLBAR_DeleteImageList(PIMLENTRY **pies, INT *cies)
{
int i;
for (i = 0; i < *cies; i++)
Free((*pies)[i]);
Free(*pies);
*cies = 0;
*pies = NULL;
}
static PIMLENTRY TOOLBAR_GetImageListEntry(const PIMLENTRY *pies, INT cies, INT id)
{
PIMLENTRY c = NULL;
if (pies != NULL)
{
int i;
for (i = 0; i < cies; i++)
{
if (pies[i]->id == id)
{
c = pies[i];
break;
}
}
}
return c;
}
static HIMAGELIST TOOLBAR_GetImageList(const PIMLENTRY *pies, INT cies, INT id)
{
HIMAGELIST himlDef = 0;
PIMLENTRY pie = TOOLBAR_GetImageListEntry(pies, cies, id);
if (pie)
himlDef = pie->himl;
return himlDef;
}
static BOOL TOOLBAR_GetButtonInfo(const TOOLBAR_INFO *infoPtr, NMTOOLBARW *nmtb)
{
if (infoPtr->bUnicode)
return TOOLBAR_SendNotify(&nmtb->hdr, infoPtr, TBN_GETBUTTONINFOW);
else
{
CHAR Buffer[256];
NMTOOLBARA nmtba;
BOOL bRet = FALSE;
nmtba.iItem = nmtb->iItem;
nmtba.pszText = Buffer;
nmtba.cchText = 256;
ZeroMemory(nmtba.pszText, nmtba.cchText);
if (TOOLBAR_SendNotify(&nmtba.hdr, infoPtr, TBN_GETBUTTONINFOA))
{
int ccht = strlen(nmtba.pszText);
if (ccht)
MultiByteToWideChar(CP_ACP, 0, nmtba.pszText, -1,
nmtb->pszText, nmtb->cchText);
nmtb->tbButton = nmtba.tbButton;
bRet = TRUE;
}
return bRet;
}
}
static BOOL TOOLBAR_IsButtonRemovable(const TOOLBAR_INFO *infoPtr, int iItem, const CUSTOMBUTTON *btnInfo)
{
NMTOOLBARW nmtb;
/* MSDN states that iItem is the index of the button, rather than the
* command ID as used by every other NMTOOLBAR notification */
nmtb.iItem = iItem;
memcpy(&nmtb.tbButton, &btnInfo->btn, sizeof(TBBUTTON));
return TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_QUERYDELETE);
}