--- /dev/null
- TBSTYLE_EX_UNDOC1 | \
+/*
+ * 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 "comctl32.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(toolbar);
+
+static HCURSOR hCursorDrag = NULL;
+
+typedef struct
+{
+ INT iBitmap;
+ INT idCommand;
+ BYTE fsState;
+ BYTE fsStyle;
+ BYTE bHot;
+ BYTE 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 */
+ INT nWidth; /* width of the toolbar */
+ 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 */
+ 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 ASCII (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 | \
- * Note: TBSTYLE_WRAPABLE or TBSTYLE_EX_UNDOC1 can be used also to allow
++ 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[] = { 'T','o','o','l','b','a','r',0 };
+
+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 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 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=%08lx, stringid=0x%08lx (%s)\n",
+ tbb->idCommand,tbb->iBitmap, tbb->fsState, tbb->fsStyle, tbb->dwData, tbb->iString,
+ (fUnicode ? wine_dbgstr_w((LPWSTR)tbb->iString) : wine_dbgstr_a((LPSTR)tbb->iString)));
+}
+
+static void
+TOOLBAR_DumpButton(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *bP, INT btn_num)
+{
+ if (TRACE_ON(toolbar)){
+ TRACE("button %d id %d, bitmap=%d, state=%02x, style=%02x, data=%08lx, stringid=0x%08lx\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)
+{
+ if (TRACE_ON(toolbar)) {
+ INT i;
+
+ TRACE("toolbar %p at line %d, exStyle=%08x, buttons=%d, bitmaps=%d, strings=%d, style=%08x\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;
+}
+
+/***********************************************************************
+* 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=%08x, 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)
+{
+ INT cx, cy;
+ HBITMAP hbmMask, hbmImage;
+ HDC hdcMask, hdcImage;
+
+ ImageList_GetIconSize(himl, &cx, &cy);
+
+ /* Create src image */
+ hdcImage = CreateCompatibleDC(hdc);
+ hbmImage = CreateCompatibleBitmap(hdc, cx, cy);
+ SelectObject(hdcImage, hbmImage);
+ ImageList_DrawEx(himl, index, hdcImage, 0, 0, cx, cy,
+ RGB(0xff, 0xff, 0xff), RGB(0,0,0), draw_flags);
+
+ /* Create Mask */
+ hdcMask = CreateCompatibleDC(0);
+ hbmMask = CreateBitmap(cx, cy, 1, 1, NULL);
+ SelectObject(hdcMask, hbmMask);
+
+ /* Remove the background and all white pixels */
+ ImageList_DrawEx(himl, index, hdcMask, 0, 0, cx, cy,
+ RGB(0xff, 0xff, 0xff), RGB(0,0,0), ILD_MASK);
+ 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)
+{
+ 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 ) ? 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) || GetWindowTheme (infoPtr->hwndSelf))))
+ {
+ /* 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, DWORD dwItemCDFlag)
+{
+ HDC hdc = tbcd->nmcd.hdc;
+ RECT rc = tbcd->nmcd.rc;
+ /* 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 = (TOOLBAR_HasDropDownArrows(infoPtr->dwExStyle) &&
+ (btnPtr->fsStyle & BTNS_DROPDOWN)) ||
+ (btnPtr->fsStyle & BTNS_WHOLEDROPDOWN);
+ 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 = GetWindowTheme (infoPtr->hwndSelf);
+
+ rc = btnPtr->rect;
+ CopyRect (&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 */
+ CopyRect(&rcText, &rc);
+ CopyRect(&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=(%d,%d) 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 = { %d, %d }\n", infoPtr->iListGap, infoPtr->szPadding.cx, infoPtr->szPadding.cy);
+
+ /* calculate text position */
+ if (lpText)
+ {
+ rcText.left += GetSystemMetrics(SM_CXEDGE);
+ rcText.right -= GetSystemMetrics(SM_CXEDGE);
+ 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);
+ tbcd.nmcd.hdc = hdc;
+ tbcd.nmcd.rc = rc;
+ 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 = rc;
+
+ 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 (!(tbcd.nmcd.uItemState & CDIS_HOT) &&
+ ((tbcd.nmcd.uItemState & CDIS_CHECKED) || (tbcd.nmcd.uItemState & CDIS_INDETERMINATE)))
+ TOOLBAR_DrawPattern (&rc, &tbcd);
+
+ if (((infoPtr->dwStyle & TBSTYLE_FLAT) || GetWindowTheme (infoPtr->hwndSelf))
+ && (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)
+ {
+ 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_HOT;
+ else if ((tbcd.nmcd.uItemState & CDIS_HOT)
+ || (drawSepDropDownArrow && btnPtr->bDropDownPressed))
+ stateId = TS_HOT;
+
+ DrawThemeBackground (theme, hdc, partId, stateId, &tbcd.nmcd.rc, NULL);
+ }
+ else
+ TOOLBAR_DrawFrame(infoPtr, &tbcd, 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_HOT;
+ 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, strlenW (lpText), lpSize);
+
+ /* feed above size into the rectangle for DrawText */
+ myrect.left = myrect.top = 0;
+ myrect.right = lpSize->cx;
+ myrect.bottom = 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 %d x %d!\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 %d x %d!\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.
+*
- !(infoPtr->dwExStyle & TBSTYLE_EX_UNDOC1) ) return;
++* 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;
+ RECT rc;
+ 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) &&
- if ((infoPtr->dwStyle & TBSTYLE_WRAPABLE) || (infoPtr->dwExStyle & TBSTYLE_EX_UNDOC1))
++ !(infoPtr->dwExStyle & TBSTYLE_EX_VERTICAL) ) return;
+
+ btnPtr = infoPtr->buttons;
+ x = infoPtr->nIndent;
+
+ if (GetParent(infoPtr->hwndSelf))
+ {
+ /* this can get the parents width, to know how far we can extend
+ * this toolbar. We cannot use its height, as there may be multiple
+ * toolbars in a rebar control
+ */
+ GetClientRect( GetParent(infoPtr->hwndSelf), &rc );
+ infoPtr->nWidth = rc.right - rc.left;
+ }
+ else
+ {
+ GetWindowRect( infoPtr->hwndSelf, &rc );
+ infoPtr->nWidth = rc.right - rc.left;
+ }
+
+ bButtonWrap = FALSE;
+
+ TRACE("start ButtonWidth=%d, BitmapWidth=%d, nWidth=%d, nIndent=%d\n",
+ infoPtr->nButtonWidth, infoPtr->nBitmapWidth, infoPtr->nWidth,
+ 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;
+
+ /* 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
+ > infoPtr->nWidth ) ||
+ ((x == infoPtr->nIndent) && (cx > infoPtr->nWidth)))
+ {
+ 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);
+ BOOL hasDropDownArrows = TOOLBAR_HasDropDownArrows(infoPtr->dwExStyle);
+
+ 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);
+ 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 ((infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) ||
+ (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 && hasDropDownArrows &&
+ ((btnPtr->fsStyle & BTNS_DROPDOWN) || (btnPtr->fsStyle & BTNS_WHOLEDROPDOWN)))
+ 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];
+
+ 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)
+ btnPtr->iString = -1;
+ else if(!IS_INTRESOURCE(lpTbb[iButton].iString) && lpTbb[iButton].iString != -1)
+ {
+ if (fUnicode)
+ Str_SetPtrW((LPWSTR*)&btnPtr->iString, (LPWSTR)lpTbb[iButton].iString );
+ else
+ Str_SetPtrAtoW((LPWSTR*)&btnPtr->iString, (LPSTR)lpTbb[iButton].iString);
+ fHasString = TRUE;
+ }
+ else
+ btnPtr->iString = lpTbb[iButton].iString;
+
+ 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 DECLSPEC_HIDDEN;
+
+/***********************************************************************
+ * 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(%ld) %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 (lstrlenW(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 */
+ CopyRect (&rcButton, &lpdis->rcItem);
+ InflateRect (&rcButton, -1, -1);
+ CopyRect (&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:
+ info.nButtons = 15;
+ info.nID = IDB_STD_SMALL;
+ break;
+ case IDB_STD_LARGE_COLOR:
+ info.nButtons = 15;
+ info.nID = IDB_STD_LARGE;
+ break;
+ case IDB_VIEW_SMALL_COLOR:
+ info.nButtons = 12;
+ info.nID = IDB_VIEW_SMALL;
+ break;
+ case IDB_VIEW_LARGE_COLOR:
+ 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:
+ 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, %lx\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 = strchrW(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 = strlenW (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, %lx\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)
+{
+ RECT parent_rect;
+ HWND parent;
+ INT x, y;
+ INT cx, cy;
+
+ TRACE("auto sizing, style=%x!\n", infoPtr->dwStyle);
+
+ parent = GetParent (infoPtr->hwndSelf);
+
+ if (!parent || !infoPtr->bDoRedraw)
+ return 0;
+
+ GetClientRect(parent, &parent_rect);
+
+ x = parent_rect.left;
+ y = parent_rect.top;
+
+ TRACE("nRows: %d, infoPtr->nButtonHeight: %d\n", infoPtr->nRows, infoPtr->nButtonHeight);
+
+ cy = TOP_BORDER + infoPtr->nRows * infoPtr->nButtonHeight + BOTTOM_BORDER;
+ cx = parent_rect.right - parent_rect.left;
+
- /* MSDN documents a iImageLabel field added in Vista but it is not present in
++ if ((infoPtr->dwStyle & TBSTYLE_WRAPABLE) || (infoPtr->dwExStyle & TBSTYLE_EX_VERTICAL))
+ {
+ TOOLBAR_LayoutToolbar(infoPtr);
+ InvalidateRect( infoPtr->hwndSelf, NULL, TRUE );
+ }
+
+ if (!(infoPtr->dwStyle & CCS_NORESIZE))
+ {
+ RECT window_rect;
+ UINT uPosFlags = SWP_NOZORDER | SWP_NOACTIVATE;
+
+ if ((infoPtr->dwStyle & CCS_BOTTOM) == CCS_NOMOVEY)
+ {
+ GetWindowRect(infoPtr->hwndSelf, &window_rect);
+ MapWindowPoints( 0, parent, (POINT *)&window_rect, 2 );
+ y = window_rect.top;
+ }
+ if ((infoPtr->dwStyle & CCS_BOTTOM) == CCS_BOTTOM)
+ {
+ GetWindowRect(infoPtr->hwndSelf, &window_rect);
+ y = parent_rect.bottom - ( window_rect.bottom - window_rect.top);
+ }
+
+ if (infoPtr->dwStyle & CCS_NOPARENTALIGN)
+ uPosFlags |= SWP_NOMOVE;
+
+ if (!(infoPtr->dwStyle & CCS_NODIVIDER))
+ cy += GetSystemMetrics(SM_CYEDGE);
+
+ if (infoPtr->dwStyle & WS_BORDER)
+ {
+ cx += 2 * GetSystemMetrics(SM_CXBORDER);
+ cy += 2 * GetSystemMetrics(SM_CYBORDER);
+ }
+
+ SetWindowPos(infoPtr->hwndSelf, NULL, x, y, cx, cy, uPosFlags);
+ }
+
+ 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=0x%08lx\n", infoPtr->hwndSelf, nIndex, lParam);
+
+ if (nIndex == -1)
+ return FALSE;
+
+ btnPtr = &infoPtr->buttons[nIndex];
+
+ bChecked = (btnPtr->fsState & TBSTATE_CHECKED) != 0;
+
+ if (LOWORD(lParam) == FALSE)
+ 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]);
+
+ if (infoPtr->nNumButtons == 1) {
+ TRACE(" simple delete!\n");
+ if (TOOLBAR_ButtonHasString(infoPtr->buttons))
+ Free((LPWSTR)infoPtr->buttons[0].iString);
+ 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));
+ }
+
+ if (TOOLBAR_ButtonHasString(&oldButtons[nIndex]))
+ Free((LPWSTR)oldButtons[nIndex].iString);
+ 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=0x%08lx\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) == FALSE) {
+ 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;
+
- /* when inserting separators, iBitmap controls it's size.
++ /* 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;
+
+ if (!(btnPtr = &infoPtr->buttons[nIndex])) return -1;
+
+ 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
+ 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 = strlenW (lpText);
+ if (lpStr) strcpyW (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=%ld\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=%ld\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) || GetWindowTheme (infoPtr->hwndSelf)))
+ 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=%ld\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 %d x %d\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 = %lu\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 = strlenW(wszAccel);
+ LPCWSTR lpszStr = TOOLBAR_GetText(infoPtr, btnPtr);
+
+ if (!lpszStr)
+ continue;
+
+ while (*lpszStr)
+ {
+ if ((lpszStr[0] == '&') && (lpszStr[1] == '&'))
+ {
+ lpszStr += 2;
+ continue;
+ }
+ if (!strncmpiW(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);
+ &