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