ReactOS 0.4.15-dev-7201-g2de6e75
toolbar.c
Go to the documentation of this file.
1/*
2 * Toolbar control
3 *
4 * Copyright 1998,1999 Eric Kohl
5 * Copyright 2000 Eric Kohl for CodeWeavers
6 * Copyright 2004 Robert Shearman
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 *
22 * NOTES
23 *
24 * This code was audited for completeness against the documented features
25 * of Comctl32.dll version 6.0 on Mar. 14, 2004, by Robert Shearman.
26 *
27 * Unless otherwise noted, we believe this code to be complete, as per
28 * the specification mentioned above.
29 * If you discover missing features or bugs please note them below.
30 *
31 * TODO:
32 * - Styles:
33 * - TBSTYLE_REGISTERDROP
34 * - TBSTYLE_EX_DOUBLEBUFFER
35 * - Messages:
36 * - TB_GETOBJECT
37 * - TB_INSERTMARKHITTEST
38 * - TB_SAVERESTORE
39 * - WM_WININICHANGE
40 * - Notifications:
41 * - NM_CHAR
42 * - TBN_GETOBJECT
43 * - TBN_SAVE
44 * - Button wrapping (under construction).
45 * - Fix TB_SETROWS and Separators.
46 * - iListGap custom draw support.
47 *
48 * Testing:
49 * - Run tests using Waite Group Windows95 API Bible Volume 2.
50 * The second cdrom contains executables addstr.exe, btncount.exe,
51 * btnstate.exe, butstrsz.exe, chkbtn.exe, chngbmp.exe, customiz.exe,
52 * enablebtn.exe, getbmp.exe, getbtn.exe, getflags.exe, hidebtn.exe,
53 * indetbtn.exe, insbtn.exe, pressbtn.exe, setbtnsz.exe, setcmdid.exe,
54 * setparnt.exe, setrows.exe, toolwnd.exe.
55 * - Microsoft's controlspy examples.
56 * - Charles Petzold's 'Programming Windows': gadgets.exe
57 *
58 * Differences between MSDN and actual native control operation:
59 * 1. MSDN says: "TBSTYLE_LIST: Creates a flat toolbar with button text
60 * to the right of the bitmap. Otherwise, this style is
61 * identical to TBSTYLE_FLAT."
62 * As implemented by both v4.71 and v5.80 of the native COMCTL32.DLL
63 * you can create a TBSTYLE_LIST without TBSTYLE_FLAT and the result
64 * is non-flat non-transparent buttons. Therefore TBSTYLE_LIST does
65 * *not* imply TBSTYLE_FLAT as documented. (GA 8/2001)
66 *
67 */
68
69#include <stdarg.h>
70#include <string.h>
71
72#include "windef.h"
73#include "winbase.h"
74#include "winreg.h"
75#include "wingdi.h"
76#include "winuser.h"
77#include "wine/unicode.h"
78#include "winnls.h"
79#include "commctrl.h"
80#include "comctl32.h"
81#include "uxtheme.h"
82#include "vssym32.h"
83#include "wine/debug.h"
84
86
88
89typedef struct
90{
101 INT cx; /* manually set size */
103
104typedef struct
105{
110
111typedef struct
112{
116
117typedef struct
118{
119 DWORD dwStructSize; /* size of TBBUTTON struct */
121 RECT rcBound; /* bounding rectangle */
127 INT nRows; /* number of button rows */
128 INT nMaxTextRows; /* maximum number of text rows */
129 INT cxMin; /* minimum button width */
130 INT cxMax; /* maximum button width */
131 INT nNumButtons; /* number of buttons */
132 INT nNumBitmaps; /* number of bitmaps */
133 INT nNumStrings; /* number of strings */
135 INT nButtonDown; /* toolbar button being pressed or -1 if none */
136 INT nButtonDrag; /* toolbar button being dragged or -1 if none */
138 INT nHotItem; /* index of the "hot" item */
139 SIZE szPadding; /* padding values around button */
140#ifdef __REACTOS__
141 SIZE szBarPadding; /* padding values around the toolbar (NOT USED BUT STORED) */
142 SIZE szSpacing; /* spacing values between buttons */
143 MARGINS themeMargins;
144#endif
145 INT iTopMargin; /* the top margin */
146 INT iListGap; /* default gap between text and image for toolbar with list style */
148 HFONT hFont; /* text font */
149 HIMAGELIST himlInt; /* image list created internally */
150 PIMLENTRY *himlDef; /* default image list array */
151 INT cimlDef; /* default image list array count */
152 PIMLENTRY *himlHot; /* hot image list array */
153 INT cimlHot; /* hot image list array count */
154 PIMLENTRY *himlDis; /* disabled image list array */
155 INT cimlDis; /* disabled image list array count */
156 HWND hwndToolTip; /* handle to tool tip control */
157 HWND hwndNotify; /* handle to the window that gets notifications */
158 HWND hwndSelf; /* my own handle */
159 BOOL bAnchor; /* anchor highlight enabled */
160 BOOL bDoRedraw; /* Redraw status */
161 BOOL bDragOutSent; /* has TBN_DRAGOUT notification been sent for this drag? */
162 BOOL bUnicode; /* Notifications are ASCII (FALSE) or Unicode (TRUE)? */
163 BOOL bCaptured; /* mouse captured? */
164 DWORD dwStyle; /* regular toolbar style */
165 DWORD dwExStyle; /* extended toolbar style */
166 DWORD dwDTFlags; /* DrawText flags */
167
168 COLORREF clrInsertMark; /* insert mark color */
169 COLORREF clrBtnHighlight; /* color for Flat Separator */
170 COLORREF clrBtnShadow; /* color for Flag Separator */
172 LPWSTR pszTooltipText; /* temporary store for a string > 80 characters
173 * for TTN_GETDISPINFOW notification */
174 TBINSERTMARK tbim; /* info on insertion mark */
175 TBUTTON_INFO *buttons; /* pointer to button array */
176 LPWSTR *strings; /* pointer to string array */
179
180
181/* used by customization dialog */
182typedef struct
183{
187
188typedef struct
189{
195
196typedef enum
197{
202
203#define SEPARATOR_WIDTH 8
204#define TOP_BORDER 2
205#define BOTTOM_BORDER 2
206#define DDARROW_WIDTH 11
207#define ARROW_HEIGHT 3
208#define INSERTMARK_WIDTH 2
209
210/* default padding inside a button */
211#define DEFPAD_CX 7
212#define DEFPAD_CY 6
213
214#ifdef __REACTOS__
215/* default space between buttons and between rows */
216#define DEFSPACE_CX 7
217#define DEFSPACE_CY 6
218#endif
219
220#define DEFLISTGAP 4
221
222/* vertical padding used in list mode when image is present */
223#ifdef __REACTOS__
224#define LISTPAD_CY 2
225#else
226#define LISTPAD_CY 9
227#endif
228
229/* how wide to treat the bitmap if it isn't present */
230#define NONLIST_NOTEXT_OFFSET 2
231
232#define TOOLBAR_NOWHERE (-1)
233
234/* Used to find undocumented extended styles */
235#define TBSTYLE_EX_ALL (TBSTYLE_EX_DRAWDDARROWS | \
236 TBSTYLE_EX_VERTICAL | \
237 TBSTYLE_EX_MIXEDBUTTONS | \
238 TBSTYLE_EX_DOUBLEBUFFER | \
239 TBSTYLE_EX_HIDECLIPPEDBUTTONS)
240
241/* all of the CCS_ styles */
242#define COMMON_STYLES (CCS_TOP|CCS_NOMOVEY|CCS_BOTTOM|CCS_NORESIZE| \
243 CCS_NOPARENTALIGN|CCS_ADJUSTABLE|CCS_NODIVIDER|CCS_VERT)
244
245#define GETIBITMAP(infoPtr, i) (infoPtr->iVersion >= 5 ? LOWORD(i) : i)
246#define GETHIMLID(infoPtr, i) (infoPtr->iVersion >= 5 ? HIWORD(i) : 0)
247#define GETDEFIMAGELIST(infoPtr, id) TOOLBAR_GetImageList(infoPtr->himlDef, infoPtr->cimlDef, id)
248#define GETHOTIMAGELIST(infoPtr, id) TOOLBAR_GetImageList(infoPtr->himlHot, infoPtr->cimlHot, id)
249#define GETDISIMAGELIST(infoPtr, id) TOOLBAR_GetImageList(infoPtr->himlDis, infoPtr->cimlDis, id)
250
251static const WCHAR themeClass[] = { 'T','o','o','l','b','a','r',0 };
252
253static BOOL TOOLBAR_GetButtonInfo(const TOOLBAR_INFO *infoPtr, NMTOOLBARW *nmtb);
254static BOOL TOOLBAR_IsButtonRemovable(const TOOLBAR_INFO *infoPtr, int iItem, const CUSTOMBUTTON *btnInfo);
255static HIMAGELIST TOOLBAR_GetImageList(const PIMLENTRY *pies, INT cies, INT id);
256static PIMLENTRY TOOLBAR_GetImageListEntry(const PIMLENTRY *pies, INT cies, INT id);
257static VOID TOOLBAR_DeleteImageList(PIMLENTRY **pies, INT *cies);
260static void TOOLBAR_LayoutToolbar(TOOLBAR_INFO *infoPtr);
261static LRESULT TOOLBAR_AutoSize(TOOLBAR_INFO *infoPtr);
262static void TOOLBAR_CheckImageListIconSize(TOOLBAR_INFO *infoPtr);
263static void TOOLBAR_TooltipAddTool(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *button);
264static void TOOLBAR_TooltipSetRect(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *button);
266 const TBBUTTONINFOW *lptbbi, BOOL isW);
267
268
269static inline int default_top_margin(const TOOLBAR_INFO *infoPtr)
270{
271#ifdef __REACTOS__
272 if (infoPtr->iVersion == 6)
273 return infoPtr->szBarPadding.cy;
274#endif
275 return (infoPtr->dwStyle & TBSTYLE_FLAT ? 0 : TOP_BORDER);
276}
277
279{
280 return (exStyle & TBSTYLE_EX_DRAWDDARROWS) != 0;
281}
282
283static inline BOOL button_has_ddarrow(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *btnPtr)
284{
285 return (TOOLBAR_HasDropDownArrows( infoPtr->dwExStyle ) && (btnPtr->fsStyle & BTNS_DROPDOWN)) ||
286 (btnPtr->fsStyle & BTNS_WHOLEDROPDOWN);
287}
288
289static LPWSTR
290TOOLBAR_GetText(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *btnPtr)
291{
292 LPWSTR lpText = NULL;
293
294 /* NOTE: iString == -1 is undocumented */
295 if (!IS_INTRESOURCE(btnPtr->iString) && (btnPtr->iString != -1))
296 lpText = (LPWSTR)btnPtr->iString;
297 else if ((btnPtr->iString >= 0) && (btnPtr->iString < infoPtr->nNumStrings))
298 lpText = infoPtr->strings[btnPtr->iString];
299
300 return lpText;
301}
302
303static void
304TOOLBAR_DumpTBButton(const TBBUTTON *tbb, BOOL fUnicode)
305{
306 TRACE("TBBUTTON: id %d, bitmap=%d, state=%02x, style=%02x, data=%p, stringid=%p (%s)\n", tbb->idCommand,
307 tbb->iBitmap, tbb->fsState, tbb->fsStyle, (void *)tbb->dwData, (void *)tbb->iString,
308 tbb->iString != -1 ? (fUnicode ? debugstr_w((LPWSTR)tbb->iString) : debugstr_a((LPSTR)tbb->iString)) : "");
309}
310
311static void
312TOOLBAR_DumpButton(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *bP, INT btn_num)
313{
314 if (TRACE_ON(toolbar)){
315 TRACE("button %d id %d, bitmap=%d, state=%02x, style=%02x, data=%08lx, stringid=0x%08lx\n",
316 btn_num, bP->idCommand, GETIBITMAP(infoPtr, bP->iBitmap),
317 bP->fsState, bP->fsStyle, bP->dwData, bP->iString);
318 TRACE("string %s\n", debugstr_w(TOOLBAR_GetText(infoPtr,bP)));
319 TRACE("button %d id %d, hot=%s, row=%d, rect=(%s)\n",
320 btn_num, bP->idCommand, (bP->bHot) ? "TRUE":"FALSE", bP->nRow,
321 wine_dbgstr_rect(&bP->rect));
322 }
323}
324
325
326static void
328{
329 if (TRACE_ON(toolbar)) {
330 INT i;
331
332 TRACE("toolbar %p at line %d, exStyle=%08x, buttons=%d, bitmaps=%d, strings=%d, style=%08x\n",
333 iP->hwndSelf, line,
334 iP->dwExStyle, iP->nNumButtons, iP->nNumBitmaps,
335 iP->nNumStrings, iP->dwStyle);
336 TRACE("toolbar %p at line %d, himlInt=%p, himlDef=%p, himlHot=%p, himlDis=%p, redrawable=%s\n",
337 iP->hwndSelf, line,
338 iP->himlInt, iP->himlDef, iP->himlHot, iP->himlDis,
339 (iP->bDoRedraw) ? "TRUE" : "FALSE");
340 for(i=0; i<iP->nNumButtons; i++) {
341 TOOLBAR_DumpButton(iP, &iP->buttons[i], i);
342 }
343 }
344}
345
346static inline BOOL
348{
349 return HIWORD(btnPtr->iString) && btnPtr->iString != -1;
350}
351
352static void set_string_index( TBUTTON_INFO *btn, INT_PTR str, BOOL unicode )
353{
354 if (!IS_INTRESOURCE( str ) && str != -1)
355 {
356 if (!TOOLBAR_ButtonHasString( btn )) btn->iString = 0;
357
358 if (unicode)
359 Str_SetPtrW( (WCHAR **)&btn->iString, (WCHAR *)str );
360 else
361 Str_SetPtrAtoW( (WCHAR **)&btn->iString, (char *)str );
362 }
363 else
364 {
365 if (TOOLBAR_ButtonHasString( btn )) Free( (WCHAR *)btn->iString );
366
367 btn->iString = str;
368 }
369}
370
371static void set_stringT( TBUTTON_INFO *btn, const WCHAR *str, BOOL unicode )
372{
373 if (IS_INTRESOURCE( (DWORD_PTR)str ) || (DWORD_PTR)str == -1) return;
374 set_string_index( btn, (DWORD_PTR)str, unicode );
375}
376
377static void free_string( TBUTTON_INFO *btn )
378{
379 set_string_index( btn, 0, TRUE );
380
381}
382
383/***********************************************************************
384* TOOLBAR_CheckStyle
385*
386* This function validates that the styles set are implemented and
387* issues FIXMEs warning of possible problems. In a perfect world this
388* function should be null.
389*/
390static void
392{
393 if (infoPtr->dwStyle & TBSTYLE_REGISTERDROP)
394 FIXME("[%p] TBSTYLE_REGISTERDROP not implemented\n", infoPtr->hwndSelf);
395}
396
397
398static INT
400{
401 if(!IsWindow(infoPtr->hwndSelf))
402 return 0; /* we have just been destroyed */
403
404 nmhdr->idFrom = GetDlgCtrlID (infoPtr->hwndSelf);
405 nmhdr->hwndFrom = infoPtr->hwndSelf;
406 nmhdr->code = code;
407
408 TRACE("to window %p, code=%08x, %s\n", infoPtr->hwndNotify, code,
409 (infoPtr->bUnicode) ? "via Unicode" : "via ANSI");
410
411 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmhdr->idFrom, (LPARAM)nmhdr);
412}
413
414/***********************************************************************
415* TOOLBAR_GetBitmapIndex
416*
417* This function returns the bitmap index associated with a button.
418* If the button specifies I_IMAGECALLBACK, then the TBN_GETDISPINFO
419* is issued to retrieve the index.
420*/
421static INT
423{
424 INT ret = btnPtr->iBitmap;
425
426 if (ret == I_IMAGECALLBACK)
427 {
428 /* issue TBN_GETDISPINFO */
429 NMTBDISPINFOW nmgd;
430
431 memset(&nmgd, 0, sizeof(nmgd));
432 nmgd.idCommand = btnPtr->idCommand;
433 nmgd.lParam = btnPtr->dwData;
434 nmgd.dwMask = TBNF_IMAGE;
435 nmgd.iImage = -1;
436 /* Windows also send TBN_GETDISPINFOW even if the control is ANSI */
437 TOOLBAR_SendNotify(&nmgd.hdr, infoPtr, TBN_GETDISPINFOW);
438 if (nmgd.dwMask & TBNF_DI_SETITEM)
439 btnPtr->iBitmap = nmgd.iImage;
440 ret = nmgd.iImage;
441 TRACE("TBN_GETDISPINFO returned bitmap id %d, mask=%08x, nNumBitmaps=%d\n",
442 ret, nmgd.dwMask, infoPtr->nNumBitmaps);
443 }
444
445 if (ret != I_IMAGENONE)
446 ret = GETIBITMAP(infoPtr, ret);
447
448 return ret;
449}
450
451
452static BOOL
454{
456 INT id = GETHIMLID(infoPtr, index);
457 INT iBitmap = GETIBITMAP(infoPtr, index);
458
459 if (((himl = GETDEFIMAGELIST(infoPtr, id)) &&
460 iBitmap >= 0 && iBitmap < ImageList_GetImageCount(himl)) ||
462 return TRUE;
463 else
464 return FALSE;
465}
466
467
468static inline BOOL
470{
471 HIMAGELIST himl = GETDEFIMAGELIST(infoPtr, GETHIMLID(infoPtr, index));
472 return (himl != NULL) && (ImageList_GetImageCount(himl) > 0);
473}
474
475
476/***********************************************************************
477* TOOLBAR_GetImageListForDrawing
478*
479* This function validates the bitmap index (including I_IMAGECALLBACK
480* functionality) and returns the corresponding image list.
481*/
482static HIMAGELIST
484 IMAGE_LIST_TYPE imagelist, INT * index)
485{
487
488 if (!TOOLBAR_IsValidBitmapIndex(infoPtr,btnPtr->iBitmap)) {
489 if (btnPtr->iBitmap == I_IMAGENONE) return NULL;
490 WARN("bitmap for ID %d, index %d is not valid, number of bitmaps in imagelist: %d\n",
491 HIWORD(btnPtr->iBitmap), LOWORD(btnPtr->iBitmap), infoPtr->nNumBitmaps);
492 return NULL;
493 }
494
495 if ((*index = TOOLBAR_GetBitmapIndex(infoPtr, btnPtr)) < 0) {
496 if ((*index == I_IMAGECALLBACK) ||
497 (*index == I_IMAGENONE)) return NULL;
498 ERR("TBN_GETDISPINFO returned invalid index %d\n",
499 *index);
500 return NULL;
501 }
502
503 switch(imagelist)
504 {
506 himl = GETDEFIMAGELIST(infoPtr, GETHIMLID(infoPtr, btnPtr->iBitmap));
507 break;
508 case IMAGE_LIST_HOT:
509 himl = GETHOTIMAGELIST(infoPtr, GETHIMLID(infoPtr, btnPtr->iBitmap));
510 break;
512 himl = GETDISIMAGELIST(infoPtr, GETHIMLID(infoPtr, btnPtr->iBitmap));
513 break;
514 default:
515 himl = NULL;
516 FIXME("Shouldn't reach here\n");
517 }
518
519 if (!himl)
520 TRACE("no image list\n");
521
522 return himl;
523}
524
525
526static void
527TOOLBAR_DrawFlatSeparator (const RECT *lpRect, HDC hdc, const TOOLBAR_INFO *infoPtr)
528{
529 RECT myrect;
530 COLORREF oldcolor, newcolor;
531
532 myrect.left = (lpRect->left + lpRect->right) / 2 - 1;
533 myrect.right = myrect.left + 1;
534 myrect.top = lpRect->top + 2;
535 myrect.bottom = lpRect->bottom - 2;
536
537 newcolor = (infoPtr->clrBtnShadow == CLR_DEFAULT) ?
539 oldcolor = SetBkColor (hdc, newcolor);
540 ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
541
542 myrect.left = myrect.right;
543 myrect.right = myrect.left + 1;
544
545 newcolor = (infoPtr->clrBtnHighlight == CLR_DEFAULT) ?
547 SetBkColor (hdc, newcolor);
548 ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
549
550 SetBkColor (hdc, oldcolor);
551}
552
553
554/***********************************************************************
555* TOOLBAR_DrawFlatHorizontalSeparator
556*
557* This function draws horizontal separator for toolbars having CCS_VERT style.
558* In this case, the separator is a pixel high line of COLOR_BTNSHADOW,
559* followed by a pixel high line of COLOR_BTNHIGHLIGHT. These separators
560* are horizontal as opposed to the vertical separators for not dropdown
561* type.
562*
563* FIXME: It is possible that the height of each line is really SM_CYBORDER.
564*/
565static void
567 const TOOLBAR_INFO *infoPtr)
568{
569 RECT myrect;
570 COLORREF oldcolor, newcolor;
571
572 myrect.left = lpRect->left;
573 myrect.right = lpRect->right;
574 myrect.top = lpRect->top + (lpRect->bottom - lpRect->top - 2)/2;
575 myrect.bottom = myrect.top + 1;
576
577 InflateRect (&myrect, -2, 0);
578
579 TRACE("rect=(%s)\n", wine_dbgstr_rect(&myrect));
580
581 newcolor = (infoPtr->clrBtnShadow == CLR_DEFAULT) ?
583 oldcolor = SetBkColor (hdc, newcolor);
584 ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
585
586 myrect.top = myrect.bottom;
587 myrect.bottom = myrect.top + 1;
588
589 newcolor = (infoPtr->clrBtnHighlight == CLR_DEFAULT) ?
591 SetBkColor (hdc, newcolor);
592 ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
593
594 SetBkColor (hdc, oldcolor);
595}
596
597
598static void
600{
601 INT x, y;
602 HPEN hPen, hOldPen;
603
604 if (!(hPen = CreatePen( PS_SOLID, 1, clr))) return;
605 hOldPen = SelectObject ( hdc, hPen );
606 x = left + 2;
607 y = top;
608 MoveToEx (hdc, x, y, NULL);
609 LineTo (hdc, x+5, y++); x++;
610 MoveToEx (hdc, x, y, NULL);
611 LineTo (hdc, x+3, y++); x++;
612 MoveToEx (hdc, x, y, NULL);
613 LineTo (hdc, x+1, y);
614 SelectObject( hdc, hOldPen );
615 DeleteObject( hPen );
616}
617
618/*
619 * Draw the text string for this button.
620 * note: infoPtr->himlDis *SHOULD* be non-zero when infoPtr->himlDef
621 * is non-zero, so we can simply check himlDef to see if we have
622 * an image list
623 */
624static void
625TOOLBAR_DrawString (const TOOLBAR_INFO *infoPtr, RECT *rcText, LPCWSTR lpText,
626 const NMTBCUSTOMDRAW *tbcd, DWORD dwItemCDFlag)
627{
628 HDC hdc = tbcd->nmcd.hdc;
629 HFONT hOldFont = 0;
630 COLORREF clrOld = 0;
631 COLORREF clrOldBk = 0;
632 int oldBkMode = 0;
633 UINT state = tbcd->nmcd.uItemState;
634#ifdef __REACTOS__
635 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
636#endif
637
638 /* draw text */
639 if (lpText && infoPtr->nMaxTextRows > 0) {
640 TRACE("string=%s rect=(%s)\n", debugstr_w(lpText),
641 wine_dbgstr_rect(rcText));
642
643 hOldFont = SelectObject (hdc, infoPtr->hFont);
644#ifdef __REACTOS__
645 if (theme)
646 {
647 DWORD dwDTFlags2 = 0;
648 int partId = TP_BUTTON;
649 int stateId = TS_NORMAL;
650
651 if (state & CDIS_DISABLED)
652 {
653 stateId = TS_DISABLED;
654 dwDTFlags2 = DTT_GRAYED;
655 }
656 else if (state & CDIS_SELECTED)
657 stateId = TS_PRESSED;
658 else if (state & CDIS_CHECKED)
659 stateId = (state & CDIS_HOT) ? TS_HOTCHECKED : TS_HOT;
660 else if (state & CDIS_HOT)
661 stateId = TS_HOT;
662
663 DrawThemeText(theme, hdc, partId, stateId, lpText, -1, infoPtr->dwDTFlags, dwDTFlags2, rcText);
664 SelectObject (hdc, hOldFont);
665 return;
666 }
667#endif
668
669 if ((state & CDIS_HOT) && (dwItemCDFlag & TBCDRF_HILITEHOTTRACK )) {
670 clrOld = SetTextColor (hdc, tbcd->clrTextHighlight);
671 }
672 else if (state & CDIS_DISABLED) {
673 clrOld = SetTextColor (hdc, tbcd->clrBtnHighlight);
674 OffsetRect (rcText, 1, 1);
675 DrawTextW (hdc, lpText, -1, rcText, infoPtr->dwDTFlags);
677 OffsetRect (rcText, -1, -1);
678 }
679 else if (state & CDIS_INDETERMINATE) {
681 }
682 else if ((state & CDIS_MARKED) && !(dwItemCDFlag & TBCDRF_NOMARK)) {
683 clrOld = SetTextColor (hdc, tbcd->clrTextHighlight);
684 clrOldBk = SetBkColor (hdc, tbcd->clrMark);
685 oldBkMode = SetBkMode (hdc, tbcd->nHLStringBkMode);
686 }
687 else {
688 clrOld = SetTextColor (hdc, tbcd->clrText);
689 }
690
691 DrawTextW (hdc, lpText, -1, rcText, infoPtr->dwDTFlags);
692 SetTextColor (hdc, clrOld);
693 if ((state & CDIS_MARKED) && !(dwItemCDFlag & TBCDRF_NOMARK))
694 {
695 SetBkColor (hdc, clrOldBk);
696 SetBkMode (hdc, oldBkMode);
697 }
698 SelectObject (hdc, hOldFont);
699 }
700}
701
702
703static void
704TOOLBAR_DrawPattern (const RECT *lpRect, const NMTBCUSTOMDRAW *tbcd)
705{
706 HDC hdc = tbcd->nmcd.hdc;
707 HBRUSH hbr = SelectObject (hdc, tbcd->hbrMonoDither);
708 COLORREF clrTextOld;
709 COLORREF clrBkOld;
710 INT cx = lpRect->right - lpRect->left;
711 INT cy = lpRect->bottom - lpRect->top;
714 clrTextOld = SetTextColor(hdc, tbcd->clrBtnHighlight);
715 clrBkOld = SetBkColor(hdc, tbcd->clrBtnFace);
716 PatBlt (hdc, lpRect->left + cxEdge, lpRect->top + cyEdge,
717 cx - (2 * cxEdge), cy - (2 * cyEdge), PATCOPY);
718 SetBkColor(hdc, clrBkOld);
719 SetTextColor(hdc, clrTextOld);
720 SelectObject (hdc, hbr);
721}
722
723
724static void TOOLBAR_DrawMasked(HIMAGELIST himl, int index, HDC hdc, INT x, INT y, UINT draw_flags)
725{
726 INT cx, cy;
727 HBITMAP hbmMask, hbmImage;
728 HDC hdcMask, hdcImage;
729
731
732 /* Create src image */
733 hdcImage = CreateCompatibleDC(hdc);
734 hbmImage = CreateCompatibleBitmap(hdc, cx, cy);
735 SelectObject(hdcImage, hbmImage);
736 ImageList_DrawEx(himl, index, hdcImage, 0, 0, cx, cy,
737 RGB(0xff, 0xff, 0xff), RGB(0,0,0), draw_flags);
738
739 /* Create Mask */
740 hdcMask = CreateCompatibleDC(0);
741 hbmMask = CreateBitmap(cx, cy, 1, 1, NULL);
742 SelectObject(hdcMask, hbmMask);
743
744 /* Remove the background and all white pixels */
745 ImageList_DrawEx(himl, index, hdcMask, 0, 0, cx, cy,
746 RGB(0xff, 0xff, 0xff), RGB(0,0,0), ILD_MASK);
747 SetBkColor(hdcImage, RGB(0xff, 0xff, 0xff));
748 BitBlt(hdcMask, 0, 0, cx, cy, hdcImage, 0, 0, NOTSRCERASE);
749
750 /* draw the new mask 'etched' to hdc */
751 SetBkColor(hdc, RGB(255, 255, 255));
753 /* E20746 op code is (Dst ^ (Src & (Pat ^ Dst))) */
754 BitBlt(hdc, x + 1, y + 1, cx, cy, hdcMask, 0, 0, 0xE20746);
756 BitBlt(hdc, x, y, cx, cy, hdcMask, 0, 0, 0xE20746);
757
758 /* Cleanup */
759 DeleteObject(hbmImage);
760 DeleteDC(hdcImage);
761 DeleteObject (hbmMask);
762 DeleteDC(hdcMask);
763}
764
765
766static UINT
768{
769 UINT retstate = 0;
770
771 retstate |= (btnPtr->fsState & TBSTATE_CHECKED) ? CDIS_CHECKED : 0;
772 retstate |= (btnPtr->fsState & TBSTATE_PRESSED) ? CDIS_SELECTED : 0;
773 retstate |= (btnPtr->fsState & TBSTATE_ENABLED) ? 0 : CDIS_DISABLED;
774 retstate |= (btnPtr->fsState & TBSTATE_MARKED ) ? CDIS_MARKED : 0;
775 retstate |= (btnPtr->bHot ) ? CDIS_HOT : 0;
777 /* NOTE: we don't set CDIS_GRAYED, CDIS_FOCUS, CDIS_DEFAULT */
778 return retstate;
779}
780
781/* draws the image on a toolbar button */
782static void
784 const NMTBCUSTOMDRAW *tbcd, DWORD dwItemCDFlag)
785{
787 BOOL draw_masked = FALSE, draw_desaturated = FALSE;
788 INT index;
789 INT offset = 0;
790 UINT draw_flags = ILD_TRANSPARENT;
791#ifdef __REACTOS__
792 IMAGEINFO info = {0};
793 BITMAP bm = {0};
794#endif
795
797 {
799 if (!himl)
800 {
802
803#ifdef __REACTOS__
805 GetObjectW(info.hbmImage, sizeof(bm), &bm);
806
807 if (bm.bmBitsPixel == 32)
808 {
809 draw_desaturated = TRUE;
810 }
811 else
812 {
813 draw_masked = TRUE;
814 }
815#else
816 draw_masked = TRUE;
817#endif
818 }
819 }
820 else if (tbcd->nmcd.uItemState & CDIS_CHECKED ||
821 ((tbcd->nmcd.uItemState & CDIS_HOT)
822 && ((infoPtr->dwStyle & TBSTYLE_FLAT) || GetWindowTheme (infoPtr->hwndSelf))))
823 {
824 /* if hot, attempt to draw with hot image list, if fails,
825 use default image list */
827 if (!himl)
829 }
830 else
832
833 if (!himl)
834 return;
835
836 if (!(dwItemCDFlag & TBCDRF_NOOFFSET) &&
838 offset = 1;
839
840 if (!(dwItemCDFlag & TBCDRF_NOMARK) &&
841 (tbcd->nmcd.uItemState & CDIS_MARKED))
842 draw_flags |= ILD_BLEND50;
843
844 TRACE("drawing index=%d, himl=%p, left=%d, top=%d, offset=%d\n",
845 index, himl, left, top, offset);
846
847 if (draw_masked)
848 {
849 /* code path for drawing flat disabled icons without alpha channel */
850 TOOLBAR_DrawMasked (himl, index, tbcd->nmcd.hdc, left + offset, top + offset, draw_flags);
851 }
852 else if (draw_desaturated)
853 {
854 /* code path for drawing disabled, alpha-blended (32bpp) icons */
855 IMAGELISTDRAWPARAMS imldp = {0};
856
857 imldp.cbSize = sizeof(imldp);
858 imldp.himl = himl;
859 imldp.i = index;
860 imldp.hdcDst = tbcd->nmcd.hdc,
861 imldp.x = offset + left;
862 imldp.y = offset + top;
863 imldp.rgbBk = CLR_NONE;
864 imldp.rgbFg = CLR_DEFAULT;
865 imldp.fStyle = ILD_TRANSPARENT;
866 imldp.fState = ILS_ALPHA | ILS_SATURATE;
867 imldp.Frame = 192;
868
869 ImageList_DrawIndirect (&imldp);
870 }
871 else
872 {
873 /* code path for drawing standard icons as-is */
874 ImageList_Draw (himl, index, tbcd->nmcd.hdc, left + offset, top + offset, draw_flags);
875 }
876}
877
878/* draws a blank frame for a toolbar button */
879static void
880TOOLBAR_DrawFrame(const TOOLBAR_INFO *infoPtr, const NMTBCUSTOMDRAW *tbcd, const RECT *rect, DWORD dwItemCDFlag)
881{
882 HDC hdc = tbcd->nmcd.hdc;
883 RECT rc = *rect;
884 /* if the state is disabled or indeterminate then the button
885 * cannot have an interactive look like pressed or hot */
886 BOOL non_interactive_state = (tbcd->nmcd.uItemState & CDIS_DISABLED) ||
888 BOOL pressed_look = !non_interactive_state &&
889 ((tbcd->nmcd.uItemState & CDIS_SELECTED) ||
890 (tbcd->nmcd.uItemState & CDIS_CHECKED));
891
892 /* app don't want us to draw any edges */
893 if (dwItemCDFlag & TBCDRF_NOEDGES)
894 return;
895
896 if (infoPtr->dwStyle & TBSTYLE_FLAT)
897 {
898 if (pressed_look)
900 else if ((tbcd->nmcd.uItemState & CDIS_HOT) && !non_interactive_state)
902 }
903 else
904 {
905 if (pressed_look)
907 else
908 DrawEdge (hdc, &rc, EDGE_RAISED,
910 }
911}
912
913static void
914TOOLBAR_DrawSepDDArrow(const TOOLBAR_INFO *infoPtr, const NMTBCUSTOMDRAW *tbcd, RECT *rcArrow, BOOL bDropDownPressed, DWORD dwItemCDFlag)
915{
916 HDC hdc = tbcd->nmcd.hdc;
917 int offset = 0;
918 BOOL pressed = bDropDownPressed ||
920
921 if (infoPtr->dwStyle & TBSTYLE_FLAT)
922 {
923 if (pressed)
924 DrawEdge (hdc, rcArrow, BDR_SUNKENOUTER, BF_RECT);
925 else if ( (tbcd->nmcd.uItemState & CDIS_HOT) &&
926 !(tbcd->nmcd.uItemState & CDIS_DISABLED) &&
928 DrawEdge (hdc, rcArrow, BDR_RAISEDINNER, BF_RECT);
929 }
930 else
931 {
932 if (pressed)
934 else
935 DrawEdge (hdc, rcArrow, EDGE_RAISED,
937 }
938
939 if (pressed)
940 offset = (dwItemCDFlag & TBCDRF_NOOFFSET) ? 0 : 1;
941
943 {
944 TOOLBAR_DrawArrow(hdc, rcArrow->left+1, rcArrow->top+1 + (rcArrow->bottom - rcArrow->top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnHighlight);
945 TOOLBAR_DrawArrow(hdc, rcArrow->left, rcArrow->top + (rcArrow->bottom - rcArrow->top - ARROW_HEIGHT) / 2, comctl32_color.clr3dShadow);
946 }
947 else
948 TOOLBAR_DrawArrow(hdc, rcArrow->left + offset, rcArrow->top + offset + (rcArrow->bottom - rcArrow->top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnText);
949}
950
951/* draws a complete toolbar button */
952static void
953TOOLBAR_DrawButton (const TOOLBAR_INFO *infoPtr, TBUTTON_INFO *btnPtr, HDC hdc, DWORD dwBaseCustDraw)
954{
955 DWORD dwStyle = infoPtr->dwStyle;
956 BOOL hasDropDownArrow = button_has_ddarrow( infoPtr, btnPtr );
957 BOOL drawSepDropDownArrow = hasDropDownArrow &&
958 (~btnPtr->fsStyle & BTNS_WHOLEDROPDOWN);
959 RECT rc, rcArrow, rcBitmap, rcText;
960 LPWSTR lpText = NULL;
961 NMTBCUSTOMDRAW tbcd;
962 DWORD ntfret;
963 INT offset;
964 INT oldBkMode;
965 DWORD dwItemCustDraw;
966 DWORD dwItemCDFlag;
967 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
968
969 rc = btnPtr->rect;
970 rcArrow = rc;
971
972 /* separator - doesn't send NM_CUSTOMDRAW */
973 if (btnPtr->fsStyle & BTNS_SEP) {
974 if (theme)
975 {
976 DrawThemeBackground (theme, hdc,
977 (dwStyle & CCS_VERT) ? TP_SEPARATORVERT : TP_SEPARATOR, 0,
978 &rc, NULL);
979 }
980 else
981 /* with the FLAT style, iBitmap is the width and has already */
982 /* been taken into consideration in calculating the width */
983 /* so now we need to draw the vertical separator */
984 /* empirical tests show that iBitmap can/will be non-zero */
985 /* when drawing the vertical bar... */
986 if ((dwStyle & TBSTYLE_FLAT) /* && (btnPtr->iBitmap == 0) */) {
987 if (dwStyle & CCS_VERT) {
988 RECT rcsep = rc;
989 InflateRect(&rcsep, -infoPtr->szPadding.cx, -infoPtr->szPadding.cy);
990 TOOLBAR_DrawFlatHorizontalSeparator (&rcsep, hdc, infoPtr);
991 }
992 else {
993 TOOLBAR_DrawFlatSeparator (&rc, hdc, infoPtr);
994 }
995 }
996 else if (btnPtr->fsStyle != BTNS_SEP) {
997 FIXME("Draw some kind of separator: fsStyle=%x\n",
998 btnPtr->fsStyle);
999 }
1000 return;
1001 }
1002
1003 /* get a pointer to the text */
1004 lpText = TOOLBAR_GetText(infoPtr, btnPtr);
1005
1006 if (hasDropDownArrow)
1007 {
1008 int right;
1009
1010 if (dwStyle & TBSTYLE_FLAT)
1011 right = max(rc.left, rc.right - DDARROW_WIDTH);
1012 else
1013 right = max(rc.left, rc.right - DDARROW_WIDTH - 2);
1014
1015 if (drawSepDropDownArrow)
1016 rc.right = right;
1017
1018 rcArrow.left = right;
1019 }
1020
1021 /* copy text & bitmap rects after adjusting for drop-down arrow
1022 * so that text & bitmap is centered in the rectangle not containing
1023 * the arrow */
1024 rcText = rc;
1025 rcBitmap = rc;
1026
1027 /* Center the bitmap horizontally and vertically */
1028 if (dwStyle & TBSTYLE_LIST)
1029 {
1030 if (lpText &&
1031 infoPtr->nMaxTextRows > 0 &&
1032 (!(infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) ||
1033 (btnPtr->fsStyle & BTNS_SHOWTEXT)) )
1034 rcBitmap.left += GetSystemMetrics(SM_CXEDGE) + infoPtr->szPadding.cx / 2;
1035 else
1036 rcBitmap.left += GetSystemMetrics(SM_CXEDGE) + infoPtr->iListGap / 2;
1037 }
1038 else
1039 rcBitmap.left += ((rc.right - rc.left) - infoPtr->nBitmapWidth) / 2;
1040
1041 rcBitmap.top += infoPtr->szPadding.cy / 2;
1042#ifdef __REACTOS__
1043 rcBitmap.top += infoPtr->themeMargins.cyTopHeight;
1044#endif
1045
1046 TRACE("iBitmap=%d, start=(%d,%d) w=%d, h=%d\n",
1047 btnPtr->iBitmap, rcBitmap.left, rcBitmap.top,
1048 infoPtr->nBitmapWidth, infoPtr->nBitmapHeight);
1049 TRACE("Text=%s\n", debugstr_w(lpText));
1050 TRACE("iListGap=%d, padding = { %d, %d }\n", infoPtr->iListGap, infoPtr->szPadding.cx, infoPtr->szPadding.cy);
1051
1052 /* calculate text position */
1053 if (lpText)
1054 {
1055 InflateRect(&rcText, -GetSystemMetrics(SM_CXEDGE), 0);
1056 if (dwStyle & TBSTYLE_LIST)
1057 {
1058 rcText.left += infoPtr->nBitmapWidth + infoPtr->iListGap + 2;
1059 }
1060 else
1061 {
1062 if (ImageList_GetImageCount(GETDEFIMAGELIST(infoPtr, 0)) > 0)
1063 rcText.top += infoPtr->szPadding.cy/2 + infoPtr->nBitmapHeight + 1;
1064 else
1065 rcText.top += infoPtr->szPadding.cy/2 + 2;
1066 }
1067 }
1068
1069 /* Initialize fields in all cases, because we use these later
1070 * NOTE: applications can and do alter these to customize their
1071 * toolbars */
1072 ZeroMemory (&tbcd, sizeof(NMTBCUSTOMDRAW));
1078 tbcd.clrHighlightHotTrack = 0;
1080 tbcd.nHLStringBkMode = OPAQUE;
1081 tbcd.rcText.left = 0;
1082 tbcd.rcText.top = 0;
1083 tbcd.rcText.right = rcText.right - rc.left;
1084 tbcd.rcText.bottom = rcText.bottom - rc.top;
1085 tbcd.nmcd.uItemState = TOOLBAR_TranslateState(btnPtr);
1086 tbcd.nmcd.hdc = hdc;
1087 tbcd.nmcd.rc = btnPtr->rect;
1089
1090 /* FIXME: what are these used for? */
1091 tbcd.hbrLines = 0;
1092 tbcd.hpenLines = 0;
1093
1094 /* Issue Item Prepaint notify */
1095 dwItemCustDraw = 0;
1096 dwItemCDFlag = 0;
1097 if (dwBaseCustDraw & CDRF_NOTIFYITEMDRAW)
1098 {
1100 tbcd.nmcd.dwItemSpec = btnPtr->idCommand;
1101 tbcd.nmcd.lItemlParam = btnPtr->dwData;
1102 ntfret = TOOLBAR_SendNotify(&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
1103 /* reset these fields so the user can't alter the behaviour like native */
1104 tbcd.nmcd.hdc = hdc;
1105 tbcd.nmcd.rc = btnPtr->rect;
1106
1107 dwItemCustDraw = ntfret & 0xffff;
1108 dwItemCDFlag = ntfret & 0xffff0000;
1109 if (dwItemCustDraw & CDRF_SKIPDEFAULT)
1110 return;
1111 /* save the only part of the rect that the user can change */
1112 rcText.right = tbcd.rcText.right + rc.left;
1113 rcText.bottom = tbcd.rcText.bottom + rc.top;
1114 }
1115
1116 if (!(dwItemCDFlag & TBCDRF_NOOFFSET) &&
1117 (btnPtr->fsState & (TBSTATE_PRESSED | TBSTATE_CHECKED)))
1118 OffsetRect(&rcText, 1, 1);
1119
1120 if (!(tbcd.nmcd.uItemState & CDIS_HOT) &&
1122 TOOLBAR_DrawPattern (&rc, &tbcd);
1123
1124 if (((infoPtr->dwStyle & TBSTYLE_FLAT) || GetWindowTheme (infoPtr->hwndSelf))
1125 && (tbcd.nmcd.uItemState & CDIS_HOT))
1126 {
1127 if ( dwItemCDFlag & TBCDRF_HILITEHOTTRACK )
1128 {
1129 COLORREF oldclr;
1130
1131 oldclr = SetBkColor(hdc, tbcd.clrHighlightHotTrack);
1132 ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, 0);
1133 if (hasDropDownArrow)
1134 ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rcArrow, NULL, 0, 0);
1135 SetBkColor(hdc, oldclr);
1136 }
1137 }
1138
1139#ifdef __REACTOS__
1140 if (theme && !(dwItemCDFlag & TBCDRF_NOBACKGROUND))
1141#else
1142 if (theme)
1143#endif
1144 {
1145 int partId = drawSepDropDownArrow ? TP_SPLITBUTTON : TP_BUTTON;
1146 int stateId = TS_NORMAL;
1147
1148 if (tbcd.nmcd.uItemState & CDIS_DISABLED)
1149 stateId = TS_DISABLED;
1150 else if (tbcd.nmcd.uItemState & CDIS_SELECTED)
1151 stateId = TS_PRESSED;
1152 else if (tbcd.nmcd.uItemState & CDIS_CHECKED)
1153#ifdef __REACTOS__
1154 stateId = (tbcd.nmcd.uItemState & CDIS_HOT) ? TS_HOTCHECKED : TS_CHECKED;
1155#else
1156 stateId = (tbcd.nmcd.uItemState & CDIS_HOT) ? TS_HOTCHECKED : TS_HOT;
1157#endif
1158 else if ((tbcd.nmcd.uItemState & CDIS_HOT)
1159 || (drawSepDropDownArrow && btnPtr->bDropDownPressed))
1160 stateId = TS_HOT;
1161
1162 DrawThemeBackground (theme, hdc, partId, stateId, &rc, NULL);
1163 }
1164
1165#ifdef __REACTOS__
1166 if (!theme)
1167#else
1168 else
1169#endif
1170 TOOLBAR_DrawFrame(infoPtr, &tbcd, &rc, dwItemCDFlag);
1171
1172 if (drawSepDropDownArrow)
1173 {
1174 if (theme)
1175 {
1176 int stateId = TS_NORMAL;
1177
1178 if (tbcd.nmcd.uItemState & CDIS_DISABLED)
1179 stateId = TS_DISABLED;
1180 else if (btnPtr->bDropDownPressed || (tbcd.nmcd.uItemState & CDIS_SELECTED))
1181 stateId = TS_PRESSED;
1182 else if (tbcd.nmcd.uItemState & CDIS_CHECKED)
1183#ifdef __REACTOS__
1184 stateId = (tbcd.nmcd.uItemState & CDIS_HOT) ? TS_HOTCHECKED : TS_CHECKED;
1185#else
1186 stateId = (tbcd.nmcd.uItemState & CDIS_HOT) ? TS_HOTCHECKED : TS_HOT;
1187#endif
1188 else if (tbcd.nmcd.uItemState & CDIS_HOT)
1189 stateId = TS_HOT;
1190
1191 DrawThemeBackground (theme, hdc, TP_DROPDOWNBUTTON, stateId, &rcArrow, NULL);
1192 DrawThemeBackground (theme, hdc, TP_SPLITBUTTONDROPDOWN, stateId, &rcArrow, NULL);
1193 }
1194 else
1195 TOOLBAR_DrawSepDDArrow(infoPtr, &tbcd, &rcArrow, btnPtr->bDropDownPressed, dwItemCDFlag);
1196 }
1197
1198 oldBkMode = SetBkMode (hdc, tbcd.nStringBkMode);
1199 if (!(infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) || (btnPtr->fsStyle & BTNS_SHOWTEXT))
1200 TOOLBAR_DrawString (infoPtr, &rcText, lpText, &tbcd, dwItemCDFlag);
1201 SetBkMode (hdc, oldBkMode);
1202
1203 TOOLBAR_DrawImage(infoPtr, btnPtr, rcBitmap.left, rcBitmap.top, &tbcd, dwItemCDFlag);
1204
1205 if (hasDropDownArrow && !drawSepDropDownArrow)
1206 {
1208 {
1209 TOOLBAR_DrawArrow(hdc, rcArrow.left+1, rcArrow.top+1 + (rcArrow.bottom - rcArrow.top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnHighlight);
1210 TOOLBAR_DrawArrow(hdc, rcArrow.left, rcArrow.top + (rcArrow.bottom - rcArrow.top - ARROW_HEIGHT) / 2, comctl32_color.clr3dShadow);
1211 }
1212#ifndef __REACTOS__
1213 else if (tbcd.nmcd.uItemState & (CDIS_SELECTED | CDIS_CHECKED))
1214 {
1215 offset = (dwItemCDFlag & TBCDRF_NOOFFSET) ? 0 : 1;
1216 TOOLBAR_DrawArrow(hdc, rcArrow.left + offset, rcArrow.top + offset + (rcArrow.bottom - rcArrow.top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnText);
1217 }
1218 else
1219 TOOLBAR_DrawArrow(hdc, rcArrow.left, rcArrow.top + (rcArrow.bottom - rcArrow.top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnText);
1220#else
1221 else
1222 {
1224 if (theme)
1226
1228 {
1229 offset = (dwItemCDFlag & TBCDRF_NOOFFSET) ? 0 : 1;
1230 TOOLBAR_DrawArrow(hdc, rcArrow.left + offset, rcArrow.top + offset + (rcArrow.bottom - rcArrow.top - ARROW_HEIGHT) / 2, clr);
1231 }
1232 else
1233 TOOLBAR_DrawArrow(hdc, rcArrow.left, rcArrow.top + (rcArrow.bottom - rcArrow.top - ARROW_HEIGHT) / 2, clr);
1234 }
1235#endif
1236 }
1237
1238 if (dwItemCustDraw & CDRF_NOTIFYPOSTPAINT)
1239 {
1241 TOOLBAR_SendNotify(&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
1242 }
1243
1244}
1245
1246
1247static void
1249{
1250 TBUTTON_INFO *btnPtr;
1251 INT i;
1252 RECT rcTemp, rcClient;
1253 NMTBCUSTOMDRAW tbcd;
1254 DWORD ntfret;
1255 DWORD dwBaseCustDraw;
1256
1257 /* the app has told us not to redraw the toolbar */
1258 if (!infoPtr->bDoRedraw)
1259 return;
1260
1261 /* if imagelist belongs to the app, it can be changed
1262 by the app after setting it */
1263 if (GETDEFIMAGELIST(infoPtr, 0) != infoPtr->himlInt)
1264 {
1265 infoPtr->nNumBitmaps = 0;
1266 for (i = 0; i < infoPtr->cimlDef; i++)
1267 infoPtr->nNumBitmaps += ImageList_GetImageCount(infoPtr->himlDef[i]->himl);
1268 }
1269
1270 TOOLBAR_DumpToolbar (infoPtr, __LINE__);
1271
1272 /* change the imagelist icon size if we manage the list and it is necessary */
1274
1275 /* Send initial notify */
1276 ZeroMemory (&tbcd, sizeof(NMTBCUSTOMDRAW));
1278 tbcd.nmcd.hdc = hdc;
1279 tbcd.nmcd.rc = ps->rcPaint;
1280 ntfret = TOOLBAR_SendNotify(&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
1281 dwBaseCustDraw = ntfret & 0xffff;
1282
1283 GetClientRect(infoPtr->hwndSelf, &rcClient);
1284
1285 /* redraw necessary buttons */
1286 btnPtr = infoPtr->buttons;
1287 for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++)
1288 {
1289 BOOL bDraw;
1290 if (!RectVisible(hdc, &btnPtr->rect))
1291 continue;
1293 {
1294 IntersectRect(&rcTemp, &rcClient, &btnPtr->rect);
1295 bDraw = EqualRect(&rcTemp, &btnPtr->rect);
1296 }
1297 else
1298 bDraw = TRUE;
1299 bDraw &= IntersectRect(&rcTemp, &(ps->rcPaint), &(btnPtr->rect));
1300 bDraw = (btnPtr->fsState & TBSTATE_HIDDEN) ? FALSE : bDraw;
1301 if (bDraw)
1302 TOOLBAR_DrawButton(infoPtr, btnPtr, hdc, dwBaseCustDraw);
1303 }
1304
1305 /* draw insert mark if required */
1306 if (infoPtr->tbim.iButton != -1)
1307 {
1308 RECT rcButton = infoPtr->buttons[infoPtr->tbim.iButton].rect;
1309 RECT rcInsertMark;
1310 rcInsertMark.top = rcButton.top;
1311 rcInsertMark.bottom = rcButton.bottom;
1312 if (infoPtr->tbim.dwFlags & TBIMHT_AFTER)
1313 rcInsertMark.left = rcInsertMark.right = rcButton.right;
1314 else
1315 rcInsertMark.left = rcInsertMark.right = rcButton.left - INSERTMARK_WIDTH;
1316 COMCTL32_DrawInsertMark(hdc, &rcInsertMark, infoPtr->clrInsertMark, FALSE);
1317 }
1318
1319 if (dwBaseCustDraw & CDRF_NOTIFYPOSTPAINT)
1320 {
1321 ZeroMemory (&tbcd, sizeof(NMTBCUSTOMDRAW));
1323 tbcd.nmcd.hdc = hdc;
1324 tbcd.nmcd.rc = ps->rcPaint;
1325 TOOLBAR_SendNotify(&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
1326 }
1327}
1328
1329/***********************************************************************
1330* TOOLBAR_MeasureString
1331*
1332* This function gets the width and height of a string in pixels. This
1333* is done first by using GetTextExtentPoint to get the basic width
1334* and height. The DrawText is called with DT_CALCRECT to get the exact
1335* width. The reason is because the text may have more than one "&" (or
1336* prefix characters as M$ likes to call them). The prefix character
1337* indicates where the underline goes, except for the string "&&" which
1338* is reduced to a single "&". GetTextExtentPoint does not process these
1339* only DrawText does. Note that the BTNS_NOPREFIX is handled here.
1340*/
1341static void
1343 HDC hdc, LPSIZE lpSize)
1344{
1345 RECT myrect;
1346
1347 lpSize->cx = 0;
1348 lpSize->cy = 0;
1349
1350 if (infoPtr->nMaxTextRows > 0 &&
1351 !(btnPtr->fsState & TBSTATE_HIDDEN) &&
1352 (!(infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) ||
1353 (btnPtr->fsStyle & BTNS_SHOWTEXT)) )
1354 {
1355 LPWSTR lpText = TOOLBAR_GetText(infoPtr, btnPtr);
1356
1357 if(lpText != NULL) {
1358 /* first get size of all the text */
1359 GetTextExtentPoint32W (hdc, lpText, strlenW (lpText), lpSize);
1360
1361 /* feed above size into the rectangle for DrawText */
1362 SetRect(&myrect, 0, 0, lpSize->cx, lpSize->cy);
1363
1364 /* Use DrawText to get true size as drawn (less pesky "&") */
1365 DrawTextW (hdc, lpText, -1, &myrect, DT_VCENTER | DT_SINGLELINE |
1366 DT_CALCRECT | ((btnPtr->fsStyle & BTNS_NOPREFIX) ?
1367 DT_NOPREFIX : 0));
1368
1369 /* feed back to caller */
1370 lpSize->cx = myrect.right;
1371 lpSize->cy = myrect.bottom;
1372 }
1373 }
1374
1375 TRACE("string size %d x %d!\n", lpSize->cx, lpSize->cy);
1376}
1377
1378/***********************************************************************
1379* TOOLBAR_CalcStrings
1380*
1381* This function walks through each string and measures it and returns
1382* the largest height and width to caller.
1383*/
1384static void
1386{
1387 TBUTTON_INFO *btnPtr;
1388 INT i;
1389 SIZE sz;
1390 HDC hdc;
1391 HFONT hOldFont;
1392
1393 lpSize->cx = 0;
1394 lpSize->cy = 0;
1395
1396 if (infoPtr->nMaxTextRows == 0)
1397 return;
1398
1399 hdc = GetDC (infoPtr->hwndSelf);
1400 hOldFont = SelectObject (hdc, infoPtr->hFont);
1401
1402 if (infoPtr->nNumButtons == 0 && infoPtr->nNumStrings > 0)
1403 {
1405
1407 lpSize->cy = tm.tmHeight;
1408 }
1409
1410 btnPtr = infoPtr->buttons;
1411 for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++) {
1412 if(TOOLBAR_GetText(infoPtr, btnPtr))
1413 {
1414 TOOLBAR_MeasureString(infoPtr, btnPtr, hdc, &sz);
1415 if (sz.cx > lpSize->cx)
1416 lpSize->cx = sz.cx;
1417 if (sz.cy > lpSize->cy)
1418 lpSize->cy = sz.cy;
1419 }
1420 }
1421
1422 SelectObject (hdc, hOldFont);
1423 ReleaseDC (infoPtr->hwndSelf, hdc);
1424
1425 TRACE("max string size %d x %d\n", lpSize->cx, lpSize->cy);
1426}
1427
1428/***********************************************************************
1429* TOOLBAR_WrapToolbar
1430*
1431* This function walks through the buttons and separators in the
1432* toolbar, and sets the TBSTATE_WRAP flag only on those items where
1433* wrapping should occur based on the width of the toolbar window.
1434* It does *not* calculate button placement itself. That task
1435* takes place in TOOLBAR_CalcToolbar. If the program wants to manage
1436* the toolbar wrapping on its own, it can use the TBSTYLE_WRAPABLE
1437* flag, and set the TBSTATE_WRAP flags manually on the appropriate items.
1438*
1439* Note: TBSTYLE_WRAPABLE or TBSTYLE_EX_VERTICAL can be used also to allow
1440* vertical toolbar lists.
1441*/
1442
1443static void
1445{
1446 TBUTTON_INFO *btnPtr;
1447 INT x, cx, i, j, width;
1448 BOOL bButtonWrap;
1449
1450 /* When the toolbar window style is not TBSTYLE_WRAPABLE, */
1451 /* no layout is necessary. Applications may use this style */
1452 /* to perform their own layout on the toolbar. */
1453 if( !(infoPtr->dwStyle & TBSTYLE_WRAPABLE) &&
1454 !(infoPtr->dwExStyle & TBSTYLE_EX_VERTICAL) ) return;
1455
1456#ifdef __REACTOS__ /* workaround CORE-16169 part 1 of 2 */
1457 /* if width is zero then return */
1458 if (infoPtr->client_rect.right == 0) return;
1459#endif
1460
1461 btnPtr = infoPtr->buttons;
1462 x = infoPtr->nIndent;
1463 width = infoPtr->client_rect.right - infoPtr->client_rect.left;
1464
1465 bButtonWrap = FALSE;
1466
1467 TRACE("start ButtonWidth=%d, BitmapWidth=%d, width=%d, nIndent=%d\n",
1468 infoPtr->nButtonWidth, infoPtr->nBitmapWidth, width,
1469 infoPtr->nIndent);
1470
1471 for (i = 0; i < infoPtr->nNumButtons; i++ )
1472 {
1473 btnPtr[i].fsState &= ~TBSTATE_WRAP;
1474
1475 if (btnPtr[i].fsState & TBSTATE_HIDDEN)
1476 continue;
1477
1478 if (btnPtr[i].cx > 0)
1479 cx = btnPtr[i].cx;
1480 /* horizontal separators are treated as buttons for width */
1481 else if ((btnPtr[i].fsStyle & BTNS_SEP) &&
1482 !(infoPtr->dwStyle & CCS_VERT))
1483 cx = (btnPtr[i].iBitmap > 0) ? btnPtr[i].iBitmap : SEPARATOR_WIDTH;
1484 else
1485 cx = infoPtr->nButtonWidth;
1486
1487 if (!btnPtr[i].cx && button_has_ddarrow( infoPtr, btnPtr + i ))
1488 cx += DDARROW_WIDTH;
1489
1490 /* Two or more adjacent separators form a separator group. */
1491 /* The first separator in a group should be wrapped to the */
1492 /* next row if the previous wrapping is on a button. */
1493 if( bButtonWrap &&
1494 (btnPtr[i].fsStyle & BTNS_SEP) &&
1495 (i + 1 < infoPtr->nNumButtons ) &&
1496 (btnPtr[i + 1].fsStyle & BTNS_SEP) )
1497 {
1498 TRACE("wrap point 1 btn %d style %02x\n", i, btnPtr[i].fsStyle);
1499 btnPtr[i].fsState |= TBSTATE_WRAP;
1500 x = infoPtr->nIndent;
1501 i++;
1502 bButtonWrap = FALSE;
1503 continue;
1504 }
1505
1506 /* The layout makes sure the bitmap is visible, but not the button. */
1507 /* Test added to also wrap after a button that starts a row but */
1508 /* is bigger than the area. - GA 8/01 */
1509 if ((x + cx - (infoPtr->nButtonWidth - infoPtr->nBitmapWidth) / 2 > width) ||
1510 ((x == infoPtr->nIndent) && (cx > width)))
1511 {
1512 BOOL bFound = FALSE;
1513
1514 /* If the current button is a separator and not hidden, */
1515 /* go to the next until it reaches a non separator. */
1516 /* Wrap the last separator if it is before a button. */
1517 while( ( ((btnPtr[i].fsStyle & BTNS_SEP) &&
1518 !(btnPtr[i].fsStyle & BTNS_DROPDOWN)) ||
1519 (btnPtr[i].fsState & TBSTATE_HIDDEN) ) &&
1520 i < infoPtr->nNumButtons )
1521 {
1522 i++;
1523 bFound = TRUE;
1524 }
1525
1526 if( bFound && i < infoPtr->nNumButtons )
1527 {
1528 i--;
1529 TRACE("wrap point 2 btn %d style %02x, x=%d, cx=%d\n",
1530 i, btnPtr[i].fsStyle, x, cx);
1531 btnPtr[i].fsState |= TBSTATE_WRAP;
1532 x = infoPtr->nIndent;
1533 bButtonWrap = FALSE;
1534 continue;
1535 }
1536 else if ( i >= infoPtr->nNumButtons)
1537 break;
1538
1539 /* If the current button is not a separator, find the last */
1540 /* separator and wrap it. */
1541 for ( j = i - 1; j >= 0 && !(btnPtr[j].fsState & TBSTATE_WRAP); j--)
1542 {
1543 if ((btnPtr[j].fsStyle & BTNS_SEP) &&
1544 !(btnPtr[j].fsState & TBSTATE_HIDDEN))
1545 {
1546 bFound = TRUE;
1547 i = j;
1548 TRACE("wrap point 3 btn %d style %02x, x=%d, cx=%d\n",
1549 i, btnPtr[i].fsStyle, x, cx);
1550 x = infoPtr->nIndent;
1551 btnPtr[j].fsState |= TBSTATE_WRAP;
1552 bButtonWrap = FALSE;
1553 break;
1554 }
1555 }
1556
1557 /* If no separator available for wrapping, wrap one of */
1558 /* non-hidden previous button. */
1559 if (!bFound)
1560 {
1561 for ( j = i - 1;
1562 j >= 0 && !(btnPtr[j].fsState & TBSTATE_WRAP); j--)
1563 {
1564 if (btnPtr[j].fsState & TBSTATE_HIDDEN)
1565 continue;
1566
1567 bFound = TRUE;
1568 i = j;
1569 TRACE("wrap point 4 btn %d style %02x, x=%d, cx=%d\n",
1570 i, btnPtr[i].fsStyle, x, cx);
1571 x = infoPtr->nIndent;
1572 btnPtr[j].fsState |= TBSTATE_WRAP;
1573 bButtonWrap = TRUE;
1574 break;
1575 }
1576 }
1577
1578 /* If all above failed, wrap the current button. */
1579 if (!bFound)
1580 {
1581 TRACE("wrap point 5 btn %d style %02x, x=%d, cx=%d\n",
1582 i, btnPtr[i].fsStyle, x, cx);
1583 btnPtr[i].fsState |= TBSTATE_WRAP;
1584 x = infoPtr->nIndent;
1585 if (btnPtr[i].fsStyle & BTNS_SEP )
1586 bButtonWrap = FALSE;
1587 else
1588 bButtonWrap = TRUE;
1589 }
1590 }
1591 else {
1592 TRACE("wrap point 6 btn %d style %02x, x=%d, cx=%d\n",
1593 i, btnPtr[i].fsStyle, x, cx);
1594 x += cx;
1595 }
1596 }
1597}
1598
1599
1600/***********************************************************************
1601* TOOLBAR_MeasureButton
1602*
1603* Calculates the width and height required for a button. Used in
1604* TOOLBAR_CalcToolbar to set the all-button width and height and also for
1605* the width of buttons that are autosized.
1606*
1607* Note that it would have been rather elegant to use one piece of code for
1608* both the laying out of the toolbar and for controlling where button parts
1609* are drawn, but the native control has inconsistencies between the two that
1610* prevent this from being effectively. These inconsistencies can be seen as
1611* artefacts where parts of the button appear outside of the bounding button
1612* rectangle.
1613*
1614* There are several cases for the calculation of the button dimensions and
1615* button part positioning:
1616*
1617* List
1618* ====
1619*
1620* With Bitmap:
1621*
1622* +--------------------------------------------------------+ ^
1623* | ^ ^ | |
1624* | | pad.cy / 2 | centered | |
1625* | pad.cx/2 + cxedge +--------------+ +------------+ | | DEFPAD_CY +
1626* |<----------------->| nBitmapWidth | | Text | | | max(nBitmapHeight, szText.cy)
1627* | |<------------>| | | | |
1628* | +--------------+ +------------+ | |
1629* |<-------------------------------------->| | |
1630* | cxedge + iListGap + nBitmapWidth + 2 |<-----------> | |
1631* | szText.cx | |
1632* +--------------------------------------------------------+ -
1633* <-------------------------------------------------------->
1634* 2*cxedge + nBitmapWidth + iListGap + szText.cx + pad.cx
1635*
1636* Without Bitmap (I_IMAGENONE):
1637*
1638* +-----------------------------------+ ^
1639* | ^ | |
1640* | | centered | | LISTPAD_CY +
1641* | +------------+ | | szText.cy
1642* | | Text | | |
1643* | | | | |
1644* | +------------+ | |
1645* |<----------------->| | |
1646* | cxedge |<-----------> | |
1647* | szText.cx | |
1648* +-----------------------------------+ -
1649* <----------------------------------->
1650* szText.cx + pad.cx
1651*
1652* Without text:
1653*
1654* +--------------------------------------+ ^
1655* | ^ | |
1656* | | padding.cy/2 | | DEFPAD_CY +
1657* | +------------+ | | nBitmapHeight
1658* | | Bitmap | | |
1659* | | | | |
1660* | +------------+ | |
1661* |<------------------->| | |
1662* | cxedge + iListGap/2 |<-----------> | |
1663* | nBitmapWidth | |
1664* +--------------------------------------+ -
1665* <-------------------------------------->
1666* 2*cxedge + nBitmapWidth + iListGap
1667*
1668* Non-List
1669* ========
1670*
1671* With bitmap:
1672*
1673* +-----------------------------------+ ^
1674* | ^ | |
1675* | | pad.cy / 2 | | nBitmapHeight +
1676* | - | | szText.cy +
1677* | +------------+ | | DEFPAD_CY + 1
1678* | centered | Bitmap | | |
1679* |<----------------->| | | |
1680* | +------------+ | |
1681* | ^ | |
1682* | 1 | | |
1683* | - | |
1684* | centered +---------------+ | |
1685* |<--------------->| Text | | |
1686* | +---------------+ | |
1687* +-----------------------------------+ -
1688* <----------------------------------->
1689* pad.cx + max(nBitmapWidth, szText.cx)
1690*
1691* Without bitmaps (NULL imagelist or ImageList_GetImageCount() = 0):
1692*
1693* +---------------------------------------+ ^
1694* | ^ | |
1695* | | 2 + pad.cy / 2 | |
1696* | - | | szText.cy +
1697* | centered +-----------------+ | | pad.cy + 2
1698* |<--------------->| Text | | |
1699* | +-----------------+ | |
1700* | | |
1701* +---------------------------------------+ -
1702* <--------------------------------------->
1703* 2*cxedge + pad.cx + szText.cx
1704*
1705* Without text:
1706* As for with bitmaps, but with szText.cx zero.
1707*/
1708static inline SIZE TOOLBAR_MeasureButton(const TOOLBAR_INFO *infoPtr, SIZE sizeString,
1709 BOOL bHasBitmap, BOOL bValidImageList)
1710{
1711 SIZE sizeButton;
1712 if (infoPtr->dwStyle & TBSTYLE_LIST)
1713 {
1714 /* set button height from bitmap / text height... */
1715 sizeButton.cy = max((bHasBitmap ? infoPtr->nBitmapHeight : 0),
1716 sizeString.cy);
1717
1718 /* ... add on the necessary padding */
1719 if (bValidImageList)
1720 {
1721#ifdef __REACTOS__
1722 sizeButton.cy += infoPtr->szPadding.cy;
1723 if (!bHasBitmap)
1724#else
1725 if (bHasBitmap)
1726 sizeButton.cy += DEFPAD_CY;
1727 else
1728#endif
1729 sizeButton.cy += LISTPAD_CY;
1730 }
1731 else
1732 sizeButton.cy += infoPtr->szPadding.cy;
1733
1734 /* calculate button width */
1735 sizeButton.cx = 2*GetSystemMetrics(SM_CXEDGE) +
1736 infoPtr->nBitmapWidth + infoPtr->iListGap;
1737 if (sizeString.cx > 0)
1738 sizeButton.cx += sizeString.cx + infoPtr->szPadding.cx;
1739
1740 }
1741 else
1742 {
1743 if (bHasBitmap)
1744 {
1745#ifdef __REACTOS__
1746 sizeButton.cy = infoPtr->nBitmapHeight + infoPtr->szPadding.cy;
1747#else
1748 sizeButton.cy = infoPtr->nBitmapHeight + DEFPAD_CY;
1749#endif
1750 if (sizeString.cy > 0)
1751 sizeButton.cy += 1 + sizeString.cy;
1752 sizeButton.cx = infoPtr->szPadding.cx +
1753 max(sizeString.cx, infoPtr->nBitmapWidth);
1754 }
1755 else
1756 {
1757 sizeButton.cy = sizeString.cy + infoPtr->szPadding.cy +
1759 sizeButton.cx = infoPtr->szPadding.cx +
1760 max(2*GetSystemMetrics(SM_CXEDGE) + sizeString.cx, infoPtr->nBitmapWidth);
1761 }
1762 }
1763
1764#ifdef __REACTOS__
1765 sizeButton.cx += infoPtr->themeMargins.cxLeftWidth + infoPtr->themeMargins.cxRightWidth;
1766 sizeButton.cy += infoPtr->themeMargins.cyTopHeight + infoPtr->themeMargins.cyBottomHeight;
1767#endif
1768
1769 return sizeButton;
1770}
1771
1772
1773/***********************************************************************
1774* TOOLBAR_CalcToolbar
1775*
1776* This function calculates button and separator placement. It first
1777* calculates the button sizes, gets the toolbar window width and then
1778* calls TOOLBAR_WrapToolbar to determine which buttons we need to wrap
1779* on. It assigns a new location to each item and sends this location to
1780* the tooltip window if appropriate. Finally, it updates the rcBound
1781* rect and calculates the new required toolbar window height.
1782*/
1783static void
1785{
1786 SIZE sizeString, sizeButton;
1787 BOOL validImageList = FALSE;
1788
1789 TOOLBAR_CalcStrings (infoPtr, &sizeString);
1790
1791 TOOLBAR_DumpToolbar (infoPtr, __LINE__);
1792
1793 if (TOOLBAR_IsValidImageList(infoPtr, 0))
1794 validImageList = TRUE;
1795 sizeButton = TOOLBAR_MeasureButton(infoPtr, sizeString, TRUE, validImageList);
1796 infoPtr->nButtonWidth = sizeButton.cx;
1797 infoPtr->nButtonHeight = sizeButton.cy;
1798 infoPtr->iTopMargin = default_top_margin(infoPtr);
1799
1800 if ( infoPtr->cxMin >= 0 && infoPtr->nButtonWidth < infoPtr->cxMin )
1801 infoPtr->nButtonWidth = infoPtr->cxMin;
1802 if ( infoPtr->cxMax > 0 && infoPtr->nButtonWidth > infoPtr->cxMax )
1803 infoPtr->nButtonWidth = infoPtr->cxMax;
1804
1805 TOOLBAR_LayoutToolbar(infoPtr);
1806}
1807
1808static void
1810{
1811 TBUTTON_INFO *btnPtr;
1812 SIZE sizeButton;
1813 INT i, nRows, nSepRows;
1814 INT x, y, cx, cy;
1815 BOOL bWrap;
1816 BOOL validImageList = TOOLBAR_IsValidImageList(infoPtr, 0);
1817
1818 TOOLBAR_WrapToolbar(infoPtr);
1819
1820 x = infoPtr->nIndent;
1821 y = infoPtr->iTopMargin;
1822 cx = infoPtr->nButtonWidth;
1823 cy = infoPtr->nButtonHeight;
1824
1825 nRows = nSepRows = 0;
1826
1827 infoPtr->rcBound.top = y;
1828 infoPtr->rcBound.left = x;
1829 infoPtr->rcBound.bottom = y + cy;
1830 infoPtr->rcBound.right = x;
1831
1832 btnPtr = infoPtr->buttons;
1833
1834 TRACE("cy=%d\n", cy);
1835
1836 for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++ )
1837 {
1838 bWrap = FALSE;
1839 if (btnPtr->fsState & TBSTATE_HIDDEN)
1840 {
1841 SetRectEmpty (&btnPtr->rect);
1842 TOOLBAR_TooltipSetRect(infoPtr, btnPtr);
1843 continue;
1844 }
1845
1846 cy = infoPtr->nButtonHeight;
1847
1848 if (btnPtr->fsStyle & BTNS_SEP) {
1849 if (infoPtr->dwStyle & CCS_VERT) {
1850 cy = (btnPtr->iBitmap > 0) ? btnPtr->iBitmap : SEPARATOR_WIDTH;
1851 cx = (btnPtr->cx > 0) ? btnPtr->cx : infoPtr->nButtonWidth;
1852 }
1853 else
1854 cx = (btnPtr->cx > 0) ? btnPtr->cx :
1855 (btnPtr->iBitmap > 0) ? btnPtr->iBitmap : SEPARATOR_WIDTH;
1856 }
1857 else
1858 {
1859 if (btnPtr->cx)
1860 cx = btnPtr->cx;
1861#ifdef __REACTOS__
1862 /* Revert Wine Commit 5b7b911 as it breaks Explorer Toolbar Buttons
1863 FIXME: Revisit this when the bug is fixed. CORE-9970 */
1864 else if ((infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) ||
1865 (btnPtr->fsStyle & BTNS_AUTOSIZE))
1866#else
1867 else if (btnPtr->fsStyle & BTNS_AUTOSIZE)
1868#endif
1869 {
1870 SIZE sz;
1871 HDC hdc;
1872 HFONT hOldFont;
1873
1874 hdc = GetDC (infoPtr->hwndSelf);
1875 hOldFont = SelectObject (hdc, infoPtr->hFont);
1876
1877 TOOLBAR_MeasureString(infoPtr, btnPtr, hdc, &sz);
1878
1879 SelectObject (hdc, hOldFont);
1880 ReleaseDC (infoPtr->hwndSelf, hdc);
1881
1882 sizeButton = TOOLBAR_MeasureButton(infoPtr, sz,
1883 TOOLBAR_IsValidBitmapIndex(infoPtr, infoPtr->buttons[i].iBitmap),
1884 validImageList);
1885 cx = sizeButton.cx;
1886 }
1887 else
1888 cx = infoPtr->nButtonWidth;
1889
1890 /* if size has been set manually then don't add on extra space
1891 * for the drop down arrow */
1892 if (!btnPtr->cx && button_has_ddarrow( infoPtr, btnPtr ))
1893 cx += DDARROW_WIDTH;
1894 }
1895 if (btnPtr->fsState & TBSTATE_WRAP)
1896 bWrap = TRUE;
1897
1898 SetRect (&btnPtr->rect, x, y, x + cx, y + cy);
1899
1900 if (infoPtr->rcBound.left > x)
1901 infoPtr->rcBound.left = x;
1902 if (infoPtr->rcBound.right < x + cx)
1903 infoPtr->rcBound.right = x + cx;
1904 if (infoPtr->rcBound.bottom < y + cy)
1905 infoPtr->rcBound.bottom = y + cy;
1906
1907 TOOLBAR_TooltipSetRect(infoPtr, btnPtr);
1908
1909 /* btnPtr->nRow is zero based. The space between the rows is */
1910 /* also considered as a row. */
1911 btnPtr->nRow = nRows + nSepRows;
1912
1913 TRACE("button %d style=%x, bWrap=%d, nRows=%d, nSepRows=%d, btnrow=%d, (%d,%d)-(%d,%d)\n",
1914 i, btnPtr->fsStyle, bWrap, nRows, nSepRows, btnPtr->nRow,
1915 x, y, x+cx, y+cy);
1916
1917 if( bWrap )
1918 {
1919 if ( !(btnPtr->fsStyle & BTNS_SEP) )
1920#ifdef __REACTOS__
1921 y += cy + infoPtr->szSpacing.cy;
1922#else
1923 y += cy;
1924#endif
1925 else
1926 {
1927 if ( !(infoPtr->dwStyle & CCS_VERT))
1928 y += cy + ( (btnPtr->cx > 0 ) ?
1929 btnPtr->cx : SEPARATOR_WIDTH) * 2 /3;
1930 else
1931#ifdef __REACTOS__
1932 y += cy + infoPtr->szSpacing.cy;
1933#else
1934 y += cy;
1935#endif
1936
1937 /* nSepRows is used to calculate the extra height following */
1938 /* the last row. */
1939 nSepRows++;
1940 }
1941 x = infoPtr->nIndent;
1942
1943 /* Increment row number unless this is the last button */
1944 /* and it has Wrap set. */
1945 if (i != infoPtr->nNumButtons-1)
1946 nRows++;
1947 }
1948 else
1949#ifdef __REACTOS__
1950 x += cx + infoPtr->szSpacing.cx;
1951#else
1952 x += cx;
1953#endif
1954 }
1955
1956 /* infoPtr->nRows is the number of rows on the toolbar */
1957 infoPtr->nRows = nRows + nSepRows + 1;
1958
1959 TRACE("toolbar button width %d\n", infoPtr->nButtonWidth);
1960}
1961
1962
1963static INT
1965{
1966 TBUTTON_INFO *btnPtr;
1967 INT i;
1968
1969 if (button)
1970 *button = FALSE;
1971
1972 btnPtr = infoPtr->buttons;
1973 for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++) {
1974 if (btnPtr->fsState & TBSTATE_HIDDEN)
1975 continue;
1976
1977 if (btnPtr->fsStyle & BTNS_SEP) {
1978 if (PtInRect (&btnPtr->rect, *lpPt)) {
1979 TRACE(" ON SEPARATOR %d\n", i);
1980 return -i;
1981 }
1982 }
1983 else {
1984 if (PtInRect (&btnPtr->rect, *lpPt)) {
1985 TRACE(" ON BUTTON %d\n", i);
1986 if (button)
1987 *button = TRUE;
1988 return i;
1989 }
1990 }
1991 }
1992
1993 TRACE(" NOWHERE\n");
1994 return TOOLBAR_NOWHERE;
1995}
1996
1997
1998/* worker for TB_ADDBUTTONS and TB_INSERTBUTTON */
1999static BOOL
2000TOOLBAR_InternalInsertButtonsT(TOOLBAR_INFO *infoPtr, INT iIndex, UINT nAddButtons, const TBBUTTON *lpTbb, BOOL fUnicode)
2001{
2002 INT nOldButtons, nNewButtons, iButton;
2003 BOOL fHasString = FALSE;
2004
2005 if (iIndex < 0) /* iIndex can be negative, what means adding at the end */
2006 iIndex = infoPtr->nNumButtons;
2007
2008 nOldButtons = infoPtr->nNumButtons;
2009 nNewButtons = nOldButtons + nAddButtons;
2010
2011 infoPtr->buttons = ReAlloc(infoPtr->buttons, sizeof(TBUTTON_INFO)*nNewButtons);
2012 memmove(&infoPtr->buttons[iIndex + nAddButtons], &infoPtr->buttons[iIndex],
2013 (nOldButtons - iIndex) * sizeof(TBUTTON_INFO));
2014 infoPtr->nNumButtons += nAddButtons;
2015
2016 /* insert new buttons data */
2017 for (iButton = 0; iButton < nAddButtons; iButton++) {
2018 TBUTTON_INFO *btnPtr = &infoPtr->buttons[iIndex + iButton];
2019 INT_PTR str;
2020
2021 TOOLBAR_DumpTBButton(lpTbb + iButton, fUnicode);
2022
2023 ZeroMemory(btnPtr, sizeof(*btnPtr));
2024
2025 btnPtr->iBitmap = lpTbb[iButton].iBitmap;
2026 btnPtr->idCommand = lpTbb[iButton].idCommand;
2027 btnPtr->fsState = lpTbb[iButton].fsState;
2028 btnPtr->fsStyle = lpTbb[iButton].fsStyle;
2029 btnPtr->dwData = lpTbb[iButton].dwData;
2030
2031 if (btnPtr->fsStyle & BTNS_SEP)
2032 str = -1;
2033 else
2034 str = lpTbb[iButton].iString;
2035 set_string_index( btnPtr, str, fUnicode );
2036 fHasString |= TOOLBAR_ButtonHasString( btnPtr );
2037
2038 TOOLBAR_TooltipAddTool(infoPtr, btnPtr);
2039 }
2040
2041 if (infoPtr->nNumStrings > 0 || fHasString)
2042 TOOLBAR_CalcToolbar(infoPtr);
2043 else
2044 TOOLBAR_LayoutToolbar(infoPtr);
2045 TOOLBAR_AutoSize(infoPtr);
2046
2047 TOOLBAR_DumpToolbar(infoPtr, __LINE__);
2048 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
2049 return TRUE;
2050}
2051
2052
2053static INT
2054TOOLBAR_GetButtonIndex (const TOOLBAR_INFO *infoPtr, INT idCommand, BOOL CommandIsIndex)
2055{
2056 TBUTTON_INFO *btnPtr;
2057 INT i;
2058
2059 if (CommandIsIndex) {
2060 TRACE("command is really index command=%d\n", idCommand);
2061 if (idCommand >= infoPtr->nNumButtons) return -1;
2062 return idCommand;
2063 }
2064 btnPtr = infoPtr->buttons;
2065 for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++) {
2066 if (btnPtr->idCommand == idCommand) {
2067 TRACE("command=%d index=%d\n", idCommand, i);
2068 return i;
2069 }
2070 }
2071 TRACE("no index found for command=%d\n", idCommand);
2072 return -1;
2073}
2074
2075
2076static INT
2078{
2079 TBUTTON_INFO *btnPtr;
2080 INT nRunIndex;
2081
2082 if ((nIndex < 0) || (nIndex > infoPtr->nNumButtons))
2083 return -1;
2084
2085 /* check index button */
2086 btnPtr = &infoPtr->buttons[nIndex];
2087 if ((btnPtr->fsStyle & BTNS_CHECKGROUP) == BTNS_CHECKGROUP) {
2088 if (btnPtr->fsState & TBSTATE_CHECKED)
2089 return nIndex;
2090 }
2091
2092 /* check previous buttons */
2093 nRunIndex = nIndex - 1;
2094 while (nRunIndex >= 0) {
2095 btnPtr = &infoPtr->buttons[nRunIndex];
2096 if ((btnPtr->fsStyle & BTNS_GROUP) == BTNS_GROUP) {
2097 if (btnPtr->fsState & TBSTATE_CHECKED)
2098 return nRunIndex;
2099 }
2100 else
2101 break;
2102 nRunIndex--;
2103 }
2104
2105 /* check next buttons */
2106 nRunIndex = nIndex + 1;
2107 while (nRunIndex < infoPtr->nNumButtons) {
2108 btnPtr = &infoPtr->buttons[nRunIndex];
2109 if ((btnPtr->fsStyle & BTNS_GROUP) == BTNS_GROUP) {
2110 if (btnPtr->fsState & TBSTATE_CHECKED)
2111 return nRunIndex;
2112 }
2113 else
2114 break;
2115 nRunIndex++;
2116 }
2117
2118 return -1;
2119}
2120
2121
2122static VOID
2123TOOLBAR_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
2125{
2126 MSG msg;
2127
2128 msg.hwnd = hwndMsg;
2129 msg.message = uMsg;
2130 msg.wParam = wParam;
2131 msg.lParam = lParam;
2132 msg.time = GetMessageTime ();
2133 msg.pt.x = (short)LOWORD(GetMessagePos ());
2134 msg.pt.y = (short)HIWORD(GetMessagePos ());
2135
2136 SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
2137}
2138
2139static void
2141{
2142 if (infoPtr->hwndToolTip && !(button->fsStyle & BTNS_SEP)) {
2143 TTTOOLINFOW ti;
2144
2145 ZeroMemory(&ti, sizeof(TTTOOLINFOW));
2146 ti.cbSize = sizeof (TTTOOLINFOW);
2147 ti.hwnd = infoPtr->hwndSelf;
2148 ti.uId = button->idCommand;
2149 ti.hinst = 0;
2151 /* ti.lParam = random value from the stack? */
2152
2154 0, (LPARAM)&ti);
2155 }
2156}
2157
2158static void
2160{
2161 if ((infoPtr->hwndToolTip) && !(button->fsStyle & BTNS_SEP)) {
2162 TTTOOLINFOW ti;
2163
2164 ZeroMemory(&ti, sizeof(ti));
2165 ti.cbSize = sizeof(ti);
2166 ti.hwnd = infoPtr->hwndSelf;
2167 ti.uId = button->idCommand;
2168
2169 SendMessageW(infoPtr->hwndToolTip, TTM_DELTOOLW, 0, (LPARAM)&ti);
2170 }
2171}
2172
2173static void TOOLBAR_TooltipSetRect(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *button)
2174{
2175 /* Set the toolTip only for non-hidden, non-separator button */
2176 if (infoPtr->hwndToolTip && !(button->fsStyle & BTNS_SEP))
2177 {
2178 TTTOOLINFOW ti;
2179
2180 ZeroMemory(&ti, sizeof(ti));
2181 ti.cbSize = sizeof(ti);
2182 ti.hwnd = infoPtr->hwndSelf;
2183 ti.uId = button->idCommand;
2184 ti.rect = button->rect;
2185 SendMessageW(infoPtr->hwndToolTip, TTM_NEWTOOLRECTW, 0, (LPARAM)&ti);
2186 }
2187}
2188
2189/* Creates the tooltip control */
2190static void
2192{
2193 int i;
2194 NMTOOLTIPSCREATED nmttc;
2195
2198 infoPtr->hwndSelf, 0, 0, 0);
2199
2200 if (!infoPtr->hwndToolTip)
2201 return;
2202
2203 /* Send NM_TOOLTIPSCREATED notification */
2204 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2205 TOOLBAR_SendNotify(&nmttc.hdr, infoPtr, NM_TOOLTIPSCREATED);
2206
2207 for (i = 0; i < infoPtr->nNumButtons; i++)
2208 {
2209 TOOLBAR_TooltipAddTool(infoPtr, &infoPtr->buttons[i]);
2210 TOOLBAR_TooltipSetRect(infoPtr, &infoPtr->buttons[i]);
2211 }
2212}
2213
2214/* keeps available button list box sorted by button id */
2216{
2217 int i;
2218 int count;
2219 PCUSTOMBUTTON btnInfo;
2220 HWND hwndAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
2221
2222 TRACE("button %s, idCommand %d\n", debugstr_w(btnInfoNew->text), btnInfoNew->btn.idCommand);
2223
2224 count = SendMessageW(hwndAvail, LB_GETCOUNT, 0, 0);
2225
2226 /* position 0 is always separator */
2227 for (i = 1; i < count; i++)
2228 {
2229 btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndAvail, LB_GETITEMDATA, i, 0);
2230 if (btnInfoNew->btn.idCommand < btnInfo->btn.idCommand)
2231 {
2232 i = SendMessageW(hwndAvail, LB_INSERTSTRING, i, 0);
2233 SendMessageW(hwndAvail, LB_SETITEMDATA, i, (LPARAM)btnInfoNew);
2234 return;
2235 }
2236 }
2237 /* id higher than all others add to end */
2238 i = SendMessageW(hwndAvail, LB_ADDSTRING, 0, 0);
2239 SendMessageW(hwndAvail, LB_SETITEMDATA, i, (LPARAM)btnInfoNew);
2240}
2241
2242static void TOOLBAR_Cust_MoveButton(const CUSTDLG_INFO *custInfo, HWND hwnd, INT nIndexFrom, INT nIndexTo)
2243{
2244 NMTOOLBARW nmtb;
2245
2246 TRACE("index from %d, index to %d\n", nIndexFrom, nIndexTo);
2247
2248 if (nIndexFrom == nIndexTo)
2249 return;
2250
2251 /* MSDN states that iItem is the index of the button, rather than the
2252 * command ID as used by every other NMTOOLBAR notification */
2253 nmtb.iItem = nIndexFrom;
2254 if (TOOLBAR_SendNotify(&nmtb.hdr, custInfo->tbInfo, TBN_QUERYINSERT))
2255 {
2256 PCUSTOMBUTTON btnInfo;
2257 NMHDR hdr;
2259 int count = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2260
2261 btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndList, LB_GETITEMDATA, nIndexFrom, 0);
2262
2263 SendMessageW(hwndList, LB_DELETESTRING, nIndexFrom, 0);
2264 SendMessageW(hwndList, LB_INSERTSTRING, nIndexTo, 0);
2265 SendMessageW(hwndList, LB_SETITEMDATA, nIndexTo, (LPARAM)btnInfo);
2266 SendMessageW(hwndList, LB_SETCURSEL, nIndexTo, 0);
2267
2268 if (nIndexTo <= 0)
2270 else
2272
2273 /* last item is always separator, so -2 instead of -1 */
2274 if (nIndexTo >= (count - 2))
2276 else
2278
2279 SendMessageW(custInfo->tbHwnd, TB_DELETEBUTTON, nIndexFrom, 0);
2280 SendMessageW(custInfo->tbHwnd, TB_INSERTBUTTONW, nIndexTo, (LPARAM)&(btnInfo->btn));
2281
2283 }
2284}
2285
2286static void TOOLBAR_Cust_AddButton(const CUSTDLG_INFO *custInfo, HWND hwnd, INT nIndexAvail, INT nIndexTo)
2287{
2288 NMTOOLBARW nmtb;
2289
2290 TRACE("Add: nIndexAvail %d, nIndexTo %d\n", nIndexAvail, nIndexTo);
2291
2292 /* MSDN states that iItem is the index of the button, rather than the
2293 * command ID as used by every other NMTOOLBAR notification */
2294 nmtb.iItem = nIndexAvail;
2295 if (TOOLBAR_SendNotify(&nmtb.hdr, custInfo->tbInfo, TBN_QUERYINSERT))
2296 {
2297 PCUSTOMBUTTON btnInfo;
2298 NMHDR hdr;
2300 HWND hwndAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
2301 int count = SendMessageW(hwndAvail, LB_GETCOUNT, 0, 0);
2302
2303 btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndAvail, LB_GETITEMDATA, nIndexAvail, 0);
2304
2305 if (nIndexAvail != 0) /* index == 0 indicates separator */
2306 {
2307 /* remove from 'available buttons' list */
2308 SendMessageW(hwndAvail, LB_DELETESTRING, nIndexAvail, 0);
2309 if (nIndexAvail == count-1)
2310 SendMessageW(hwndAvail, LB_SETCURSEL, nIndexAvail-1 , 0);
2311 else
2312 SendMessageW(hwndAvail, LB_SETCURSEL, nIndexAvail , 0);
2313 }
2314 else
2315 {
2316 PCUSTOMBUTTON btnNew;
2317
2318 /* duplicate 'separator' button */
2319 btnNew = Alloc(sizeof(CUSTOMBUTTON));
2320 *btnNew = *btnInfo;
2321 btnInfo = btnNew;
2322 }
2323
2324 /* insert into 'toolbar button' list */
2325 SendMessageW(hwndList, LB_INSERTSTRING, nIndexTo, 0);
2326 SendMessageW(hwndList, LB_SETITEMDATA, nIndexTo, (LPARAM)btnInfo);
2327
2328 SendMessageW(custInfo->tbHwnd, TB_INSERTBUTTONW, nIndexTo, (LPARAM)&(btnInfo->btn));
2329
2331 }
2332}
2333
2335{
2336 PCUSTOMBUTTON btnInfo;
2338
2339 TRACE("Remove: index %d\n", index);
2340
2341 btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndList, LB_GETITEMDATA, index, 0);
2342
2343 /* send TBN_QUERYDELETE notification */
2344 if (TOOLBAR_IsButtonRemovable(custInfo->tbInfo, index, btnInfo))
2345 {
2346 NMHDR hdr;
2347
2348 SendMessageW(hwndList, LB_DELETESTRING, index, 0);
2349 SendMessageW(hwndList, LB_SETCURSEL, index , 0);
2350
2351 SendMessageW(custInfo->tbHwnd, TB_DELETEBUTTON, index, 0);
2352
2353 /* insert into 'available button' list */
2354 if (!(btnInfo->btn.fsStyle & BTNS_SEP))
2356 else
2357 Free(btnInfo);
2358
2360 }
2361}
2362
2363/* drag list notification function for toolbar buttons list box */
2365 const DRAGLISTINFO *pDLI)
2366{
2368 switch (pDLI->uNotification)
2369 {
2370 case DL_BEGINDRAG:
2371 {
2372 INT nCurrentItem = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2373 INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2374 /* no dragging for last item (separator) */
2375 if (nCurrentItem >= (nCount - 1)) return FALSE;
2376 return TRUE;
2377 }
2378 case DL_DRAGGING:
2379 {
2380 INT nCurrentItem = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2381 INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2382 /* no dragging past last item (separator) */
2383 if ((nCurrentItem >= 0) && (nCurrentItem < (nCount - 1)))
2384 {
2385 DrawInsert(hwnd, hwndList, nCurrentItem);
2386 /* FIXME: native uses "move button" cursor */
2387 return DL_COPYCURSOR;
2388 }
2389
2390 /* not over toolbar buttons list */
2391 if (nCurrentItem < 0)
2392 {
2393 POINT ptWindow = pDLI->ptCursor;
2394 HWND hwndListAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
2395 MapWindowPoints(NULL, hwnd, &ptWindow, 1);
2396 /* over available buttons list? */
2397 if (ChildWindowFromPoint(hwnd, ptWindow) == hwndListAvail)
2398 /* FIXME: native uses "move button" cursor */
2399 return DL_COPYCURSOR;
2400 }
2401 /* clear drag arrow */
2402 DrawInsert(hwnd, hwndList, -1);
2403 return DL_STOPCURSOR;
2404 }
2405 case DL_DROPPED:
2406 {
2407 INT nIndexTo = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2408 INT nIndexFrom = SendMessageW(hwndList, LB_GETCURSEL, 0, 0);
2409 INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2410 if ((nIndexTo >= 0) && (nIndexTo < (nCount - 1)))
2411 {
2412 /* clear drag arrow */
2413 DrawInsert(hwnd, hwndList, -1);
2414 /* move item */
2415 TOOLBAR_Cust_MoveButton(custInfo, hwnd, nIndexFrom, nIndexTo);
2416 }
2417 /* not over toolbar buttons list */
2418 if (nIndexTo < 0)
2419 {
2420 POINT ptWindow = pDLI->ptCursor;
2421 HWND hwndListAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
2422 MapWindowPoints(NULL, hwnd, &ptWindow, 1);
2423 /* over available buttons list? */
2424 if (ChildWindowFromPoint(hwnd, ptWindow) == hwndListAvail)
2425 TOOLBAR_Cust_RemoveButton(custInfo, hwnd, nIndexFrom);
2426 }
2427 break;
2428 }
2429 case DL_CANCELDRAG:
2430 /* Clear drag arrow */
2431 DrawInsert(hwnd, hwndList, -1);
2432 break;
2433 }
2434
2435 return 0;
2436}
2437
2438/* drag list notification function for available buttons list box */
2440 const DRAGLISTINFO *pDLI)
2441{
2443 switch (pDLI->uNotification)
2444 {
2445 case DL_BEGINDRAG:
2446 return TRUE;
2447 case DL_DRAGGING:
2448 {
2449 INT nCurrentItem = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2450 INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2451 /* no dragging past last item (separator) */
2452 if ((nCurrentItem >= 0) && (nCurrentItem < nCount))
2453 {
2454 DrawInsert(hwnd, hwndList, nCurrentItem);
2455 /* FIXME: native uses "move button" cursor */
2456 return DL_COPYCURSOR;
2457 }
2458
2459 /* not over toolbar buttons list */
2460 if (nCurrentItem < 0)
2461 {
2462 POINT ptWindow = pDLI->ptCursor;
2463 HWND hwndListAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
2464 MapWindowPoints(NULL, hwnd, &ptWindow, 1);
2465 /* over available buttons list? */
2466 if (ChildWindowFromPoint(hwnd, ptWindow) == hwndListAvail)
2467 /* FIXME: native uses "move button" cursor */
2468 return DL_COPYCURSOR;
2469 }
2470 /* clear drag arrow */
2471 DrawInsert(hwnd, hwndList, -1);
2472 return DL_STOPCURSOR;
2473 }
2474 case DL_DROPPED:
2475 {
2476 INT nIndexTo = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2477 INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2479 if ((nIndexTo >= 0) && (nIndexTo < nCount))
2480 {
2481 /* clear drag arrow */
2482 DrawInsert(hwnd, hwndList, -1);
2483 /* add item */
2484 TOOLBAR_Cust_AddButton(custInfo, hwnd, nIndexFrom, nIndexTo);
2485 }
2486 }
2487 case DL_CANCELDRAG:
2488 /* Clear drag arrow */
2489 DrawInsert(hwnd, hwndList, -1);
2490 break;
2491 }
2492 return 0;
2493}
2494
2496
2497/***********************************************************************
2498 * TOOLBAR_CustomizeDialogProc
2499 * This function implements the toolbar customization dialog.
2500 */
2501static INT_PTR CALLBACK
2503{
2505 PCUSTOMBUTTON btnInfo;
2506 NMTOOLBARA nmtb;
2507 TOOLBAR_INFO *infoPtr = custInfo ? custInfo->tbInfo : NULL;
2508
2509 switch (uMsg)
2510 {
2511 case WM_INITDIALOG:
2512 custInfo = (PCUSTDLG_INFO)lParam;
2514
2515 if (custInfo)
2516 {
2517 WCHAR Buffer[256];
2518 int i = 0;
2519 int index;
2520 NMTBINITCUSTOMIZE nmtbic;
2521
2522 infoPtr = custInfo->tbInfo;
2523
2524 /* send TBN_QUERYINSERT notification */
2525 nmtb.iItem = custInfo->tbInfo->nNumButtons;
2526
2527 if (!TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_QUERYINSERT))
2528 return FALSE;
2529
2530 nmtbic.hwndDialog = hwnd;
2531 /* Send TBN_INITCUSTOMIZE notification */
2532 if (TOOLBAR_SendNotify (&nmtbic.hdr, infoPtr, TBN_INITCUSTOMIZE) ==
2534 {
2535 TRACE("TBNRF_HIDEHELP requested\n");
2537 }
2538
2539 /* add items to 'toolbar buttons' list and check if removable */
2540 for (i = 0; i < custInfo->tbInfo->nNumButtons; i++)
2541 {
2542 btnInfo = Alloc(sizeof(CUSTOMBUTTON));
2543 memset (&btnInfo->btn, 0, sizeof(TBBUTTON));
2544 btnInfo->btn.fsStyle = BTNS_SEP;
2545 btnInfo->bVirtual = FALSE;
2547
2548 /* send TBN_QUERYDELETE notification */
2549 btnInfo->bRemovable = TOOLBAR_IsButtonRemovable(infoPtr, i, btnInfo);
2550
2553 }
2554
2556
2557 /* insert separator button into 'available buttons' list */
2558 btnInfo = Alloc(sizeof(CUSTOMBUTTON));
2559 memset (&btnInfo->btn, 0, sizeof(TBBUTTON));
2560 btnInfo->btn.fsStyle = BTNS_SEP;
2561 btnInfo->bVirtual = FALSE;
2562 btnInfo->bRemovable = TRUE;
2566
2567 /* insert all buttons into dsa */
2568 for (i = 0;; i++)
2569 {
2570 /* send TBN_GETBUTTONINFO notification */
2571 NMTOOLBARW nmtb;
2572 nmtb.iItem = i;
2573 nmtb.pszText = Buffer;
2574 nmtb.cchText = 256;
2575
2576 /* Clear previous button's text */
2577 ZeroMemory(nmtb.pszText, nmtb.cchText * sizeof(WCHAR));
2578
2579 if (!TOOLBAR_GetButtonInfo(infoPtr, &nmtb))
2580 break;
2581
2582 TRACE("WM_INITDIALOG style: %x iItem(%d) idCommand(%d) iString(%ld) %s\n",
2583 nmtb.tbButton.fsStyle, i,
2584 nmtb.tbButton.idCommand,
2585 nmtb.tbButton.iString,
2586 nmtb.tbButton.iString >= 0 ? debugstr_w(infoPtr->strings[nmtb.tbButton.iString])
2587 : "");
2588
2589 /* insert button into the appropriate list */
2590 index = TOOLBAR_GetButtonIndex (custInfo->tbInfo, nmtb.tbButton.idCommand, FALSE);
2591 if (index == -1)
2592 {
2593 btnInfo = Alloc(sizeof(CUSTOMBUTTON));
2594 btnInfo->bVirtual = FALSE;
2595 btnInfo->bRemovable = TRUE;
2596 }
2597 else
2598 {
2601 }
2602
2603 btnInfo->btn = nmtb.tbButton;
2604 if (!(nmtb.tbButton.fsStyle & BTNS_SEP))
2605 {
2606 if (lstrlenW(nmtb.pszText))
2607 lstrcpyW(btnInfo->text, nmtb.pszText);
2608 else if (nmtb.tbButton.iString >= 0 &&
2609 nmtb.tbButton.iString < infoPtr->nNumStrings)
2610 {
2611 lstrcpyW(btnInfo->text,
2612 infoPtr->strings[nmtb.tbButton.iString]);
2613 }
2614 }
2615
2616 if (index == -1)
2618 }
2619
2621
2622 /* select first item in the 'available' list */
2624
2625 /* append 'virtual' separator button to the 'toolbar buttons' list */
2626 btnInfo = Alloc(sizeof(CUSTOMBUTTON));
2627 memset (&btnInfo->btn, 0, sizeof(TBBUTTON));
2628 btnInfo->btn.fsStyle = BTNS_SEP;
2629 btnInfo->bVirtual = TRUE;
2630 btnInfo->bRemovable = FALSE;
2631 LoadStringW (COMCTL32_hModule, IDS_SEPARATOR, btnInfo->text, 64);
2634
2635 /* select last item in the 'toolbar' list */
2638
2641
2642 /* set focus and disable buttons */
2643 PostMessageW (hwnd, WM_USER, 0, 0);
2644 }
2645 return TRUE;
2646
2647 case WM_USER:
2652 return TRUE;
2653
2654 case WM_CLOSE:
2656 return TRUE;
2657
2658 case WM_COMMAND:
2659 switch (LOWORD(wParam))
2660 {
2662 if (HIWORD(wParam) == LBN_SELCHANGE)
2663 {
2664 PCUSTOMBUTTON btnInfo;
2665 NMTOOLBARA nmtb;
2666 int count;
2667 int index;
2668
2671
2672 /* send TBN_QUERYINSERT notification */
2673 nmtb.iItem = index;
2674 TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_QUERYINSERT);
2675
2676 /* get list box item */
2678
2679 if (index == (count - 1))
2680 {
2681 /* last item (virtual separator) */
2684 }
2685 else if (index == (count - 2))
2686 {
2687 /* second last item (last non-virtual item) */
2690 }
2691 else if (index == 0)
2692 {
2693 /* first item */
2696 }
2697 else
2698 {
2701 }
2702
2704 }
2705 break;
2706
2707 case IDC_MOVEUP_BTN:
2708 {
2710 TOOLBAR_Cust_MoveButton(custInfo, hwnd, index, index-1);
2711 }
2712 break;
2713
2714 case IDC_MOVEDN_BTN: /* move down */
2715 {
2717 TOOLBAR_Cust_MoveButton(custInfo, hwnd, index, index+1);
2718 }
2719 break;
2720
2721 case IDC_REMOVE_BTN: /* remove button */
2722 {
2724
2725 if (LB_ERR == index)
2726 break;
2727
2729 }
2730 break;
2731 case IDC_HELP_BTN:
2732 TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_CUSTHELP);
2733 break;
2734 case IDC_RESET_BTN:
2735 TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_RESET);
2736 break;
2737
2738 case IDOK: /* Add button */
2739 {
2740 int index;
2741 int indexto;
2742
2745
2746 TOOLBAR_Cust_AddButton(custInfo, hwnd, index, indexto);
2747 }
2748 break;
2749
2750 case IDCANCEL:
2752 break;
2753 }
2754 return TRUE;
2755
2756 case WM_DESTROY:
2757 {
2758 int count;
2759 int i;
2760
2761 /* delete items from 'toolbar buttons' listbox*/
2763 for (i = 0; i < count; i++)
2764 {
2766 Free(btnInfo);
2768 }
2770
2771
2772 /* delete items from 'available buttons' listbox*/
2774 for (i = 0; i < count; i++)
2775 {
2777 Free(btnInfo);
2779 }
2781 }
2782 return TRUE;
2783
2784 case WM_DRAWITEM:
2786 {
2788 RECT rcButton;
2789 RECT rcText;
2790 HPEN hPen, hOldPen;
2791 HBRUSH hOldBrush;
2792 COLORREF oldText = 0;
2793 COLORREF oldBk = 0;
2794
2795 /* get item data */
2797 if (btnInfo == NULL)
2798 {
2799 FIXME("btnInfo invalid\n");
2800 return TRUE;
2801 }
2802
2803 /* set colors and select objects */
2805 if (btnInfo->bVirtual)
2806 oldText = SetTextColor (lpdis->hDC, comctl32_color.clrGrayText);
2807 else
2809 hPen = CreatePen( PS_SOLID, 1,
2811 hOldPen = SelectObject (lpdis->hDC, hPen );
2813
2814 /* fill background rectangle */
2815 Rectangle (lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top,
2816 lpdis->rcItem.right, lpdis->rcItem.bottom);
2817
2818 /* calculate button and text rectangles */
2819 rcButton = lpdis->rcItem;
2820 InflateRect (&rcButton, -1, -1);
2821 rcText = rcButton;
2822 rcButton.right = rcButton.left + custInfo->tbInfo->nBitmapWidth + 6;
2823 rcText.left = rcButton.right + 2;
2824
2825 /* draw focus rectangle */
2826 if (lpdis->itemState & ODS_FOCUS)
2827 DrawFocusRect (lpdis->hDC, &lpdis->rcItem);
2828
2829 /* draw button */
2830 if (!(infoPtr->dwStyle & TBSTYLE_FLAT))
2831 DrawEdge (lpdis->hDC, &rcButton, EDGE_RAISED, BF_RECT|BF_MIDDLE|BF_SOFT);
2832
2833 /* draw image and text */
2834 if ((btnInfo->btn.fsStyle & BTNS_SEP) == 0) {
2835 HIMAGELIST himl = GETDEFIMAGELIST(infoPtr, GETHIMLID(infoPtr,
2836 btnInfo->btn.iBitmap));
2837 ImageList_Draw (himl, GETIBITMAP(infoPtr, btnInfo->btn.iBitmap),
2838 lpdis->hDC, rcButton.left+3, rcButton.top+3, ILD_NORMAL);
2839 }
2840 DrawTextW (lpdis->hDC, btnInfo->text, -1, &rcText,
2842
2843 /* delete objects and reset colors */
2844 SelectObject (lpdis->hDC, hOldBrush);
2845 SelectObject (lpdis->hDC, hOldPen);
2846 SetBkColor (lpdis->hDC, oldBk);
2847 SetTextColor (lpdis->hDC, oldText);
2848 DeleteObject( hPen );
2849 return TRUE;
2850 }
2851 return FALSE;
2852
2853 case WM_MEASUREITEM:
2855 {
2857
2858 lpmis->itemHeight = 15 + 8; /* default height */
2859
2860 return TRUE;
2861 }
2862 return FALSE;
2863
2864 default:
2865 if (uDragListMessage && (uMsg == uDragListMessage))
2866 {
2868 {
2870 custInfo, hwnd, (DRAGLISTINFO *)lParam);
2872 return TRUE;
2873 }
2874 else if (wParam == IDC_AVAILBTN_LBOX)
2875 {
2877 custInfo, hwnd, (DRAGLISTINFO *)lParam);
2879 return TRUE;
2880 }
2881 }
2882 return FALSE;
2883 }
2884}
2885
2886static BOOL
2888{
2889 HBITMAP hbmLoad;
2890 INT nCountBefore = ImageList_GetImageCount(himlDef);
2891 INT nCountAfter;
2892 INT cxIcon, cyIcon;
2893 INT nAdded;
2894 INT nIndex;
2895
2896 TRACE("adding hInst=%p nID=%d nButtons=%d\n", bitmap->hInst, bitmap->nID, bitmap->nButtons);
2897 /* Add bitmaps to the default image list */
2898 if (bitmap->hInst == NULL) /* a handle was passed */
2899 hbmLoad = CopyImage(ULongToHandle(bitmap->nID), IMAGE_BITMAP, 0, 0, 0);
2900 else if (bitmap->hInst == COMCTL32_hModule)
2901 hbmLoad = LoadImageW( bitmap->hInst, MAKEINTRESOURCEW(bitmap->nID),
2903 else
2904 hbmLoad = CreateMappedBitmap(bitmap->hInst, bitmap->nID, 0, NULL, 0);
2905
2906 /* enlarge the bitmap if needed */
2907 ImageList_GetIconSize(himlDef, &cxIcon, &cyIcon);
2908 if (bitmap->hInst != COMCTL32_hModule)
2909 COMCTL32_EnsureBitmapSize(&hbmLoad, cxIcon*(INT)bitmap->nButtons, cyIcon, comctl32_color.clrBtnFace);
2910
2911 nIndex = ImageList_AddMasked(himlDef, hbmLoad, comctl32_color.clrBtnFace);
2912 DeleteObject(hbmLoad);
2913 if (nIndex == -1)
2914 return FALSE;
2915
2916 nCountAfter = ImageList_GetImageCount(himlDef);
2917 nAdded = nCountAfter - nCountBefore;
2918 if (bitmap->nButtons == 0) /* wParam == 0 is special and means add only one image */
2919 {
2920 ImageList_SetImageCount(himlDef, nCountBefore + 1);
2921 } else if (nAdded > (INT)bitmap->nButtons) {
2922 TRACE("Added more images than wParam: Previous image number %i added %i while wParam %i. Images in list %i\n",
2923 nCountBefore, nAdded, bitmap->nButtons, nCountAfter);
2924 }
2925
2926 infoPtr->nNumBitmaps += nAdded;
2927 return TRUE;
2928}
2929
2930static void
2932{
2933 HIMAGELIST himlDef;
2934 HIMAGELIST himlNew;
2935 INT cx, cy;
2936 INT i;
2937
2938 himlDef = GETDEFIMAGELIST(infoPtr, 0);
2939 if (himlDef == NULL || himlDef != infoPtr->himlInt)
2940 return;
2941 if (!ImageList_GetIconSize(himlDef, &cx, &cy))
2942 return;
2943 if (cx == infoPtr->nBitmapWidth && cy == infoPtr->nBitmapHeight)
2944 return;
2945
2946 TRACE("Update icon size: %dx%d -> %dx%d\n",
2947 cx, cy, infoPtr->nBitmapWidth, infoPtr->nBitmapHeight);
2948
2949 himlNew = ImageList_Create(infoPtr->nBitmapWidth, infoPtr->nBitmapHeight,
2950 ILC_COLOR32|ILC_MASK, 8, 2);
2951 for (i = 0; i < infoPtr->nNumBitmapInfos; i++)
2952 TOOLBAR_AddBitmapToImageList(infoPtr, himlNew, &infoPtr->bitmaps[i]);
2953 TOOLBAR_InsertImageList(&infoPtr->himlDef, &infoPtr->cimlDef, himlNew, 0);
2954 infoPtr->himlInt = himlNew;
2955
2956 infoPtr->nNumBitmaps -= ImageList_GetImageCount(himlDef);
2957 ImageList_Destroy(himlDef);
2958}
2959
2960/***********************************************************************
2961 * TOOLBAR_AddBitmap: Add the bitmaps to the default image list.
2962 *
2963 */
2964static LRESULT
2966{
2968 INT iSumButtons, i;
2969 HIMAGELIST himlDef;
2970
2971 TRACE("hwnd=%p count=%d lpAddBmp=%p\n", infoPtr->hwndSelf, count, lpAddBmp);
2972 if (!lpAddBmp)
2973 return -1;
2974
2975 if (lpAddBmp->hInst == HINST_COMMCTRL)
2976 {
2977 info.hInst = COMCTL32_hModule;
2978 switch (lpAddBmp->nID)
2979 {
2981 case 2:
2982 info.nButtons = 15;
2983 info.nID = IDB_STD_SMALL;
2984 break;
2986 case 3:
2987 info.nButtons = 15;
2988 info.nID = IDB_STD_LARGE;
2989 break;
2991 case 6:
2992 info.nButtons = 12;
2993 info.nID = IDB_VIEW_SMALL;
2994 break;
2996 case 7:
2997 info.nButtons = 12;
2998 info.nID = IDB_VIEW_LARGE;
2999 break;
3001 info.nButtons = 5;
3002 info.nID = IDB_HIST_SMALL;
3003 break;
3005 info.nButtons = 5;
3006 info.nID = IDB_HIST_LARGE;
3007 break;
3008 default:
3009 WARN("unknown bitmap id, %ld\n", lpAddBmp->nID);
3010 return -1;
3011 }
3012
3013 TRACE ("adding %d internal bitmaps\n", info.nButtons);
3014
3015 /* Windows resize all the buttons to the size of a newly added standard image */
3016 if (lpAddBmp->nID & 1)
3017 {
3018 /* large icons: 24x24. Will make the button 31x30 */
3019 SendMessageW (infoPtr->hwndSelf, TB_SETBITMAPSIZE, 0, MAKELPARAM(24, 24));
3020 }
3021 else
3022 {
3023 /* small icons: 16x16. Will make the buttons 23x22 */
3024 SendMessageW (infoPtr->hwndSelf, TB_SETBITMAPSIZE, 0, MAKELPARAM(16, 16));
3025 }
3026
3027 TOOLBAR_CalcToolbar (infoPtr);
3028 }
3029 else
3030 {
3031 info.nButtons = count;
3032 info.hInst = lpAddBmp->hInst;
3033 info.nID = lpAddBmp->nID;
3034 TRACE("adding %d bitmaps\n", info.nButtons);
3035 }
3036
3037 /* check if the bitmap is already loaded and compute iSumButtons */
3038 iSumButtons = 0;
3039 for (i = 0; i < infoPtr->nNumBitmapInfos; i++)
3040 {
3041 if (infoPtr->bitmaps[i].hInst == info.hInst &&
3042 infoPtr->bitmaps[i].nID == info.nID)
3043 return iSumButtons;
3044 iSumButtons += infoPtr->bitmaps[i].nButtons;
3045 }
3046
3047 if (!infoPtr->cimlDef) {
3048 /* create new default image list */
3049 TRACE ("creating default image list\n");
3050
3051 himlDef = ImageList_Create (infoPtr->nBitmapWidth, infoPtr->nBitmapHeight,
3052 ILC_COLOR32 | ILC_MASK, info.nButtons, 2);
3053 TOOLBAR_InsertImageList(&infoPtr->himlDef, &infoPtr->cimlDef, himlDef, 0);
3054 infoPtr->himlInt = himlDef;
3055 }
3056 else {
3057 himlDef = GETDEFIMAGELIST(infoPtr, 0);
3058 }
3059
3060 if (!himlDef) {
3061 WARN("No default image list available\n");
3062 return -1;
3063 }
3064
3065 if (!TOOLBAR_AddBitmapToImageList(infoPtr, himlDef, &info))
3066 return -1;
3067
3068 TRACE("Number of bitmap infos: %d\n", infoPtr->nNumBitmapInfos);
3069 infoPtr->bitmaps = ReAlloc(infoPtr->bitmaps, (infoPtr->nNumBitmapInfos + 1) * sizeof(TBITMAP_INFO));
3070 infoPtr->bitmaps[infoPtr->nNumBitmapInfos] = info;
3071 infoPtr->nNumBitmapInfos++;
3072 TRACE("Number of bitmap infos: %d\n", infoPtr->nNumBitmapInfos);
3073
3074 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
3075 return iSumButtons;
3076}
3077
3078
3079static LRESULT
3080TOOLBAR_AddButtonsT(TOOLBAR_INFO *infoPtr, INT nAddButtons, const TBBUTTON* lpTbb, BOOL fUnicode)
3081{
3082 TRACE("adding %d buttons (unicode=%d)\n", nAddButtons, fUnicode);
3083
3084 return TOOLBAR_InternalInsertButtonsT(infoPtr, -1, nAddButtons, lpTbb, fUnicode);
3085}
3086
3087
3088static LRESULT
3090{
3091#define MAX_RESOURCE_STRING_LENGTH 512
3092 BOOL fFirstString = (infoPtr->nNumStrings == 0);
3093 INT nIndex = infoPtr->nNumStrings;
3094
3095 TRACE("%p, %lx\n", hInstance, lParam);
3096
3097 if (IS_INTRESOURCE(lParam)) {
3100 WCHAR *next_delim;
3101 HRSRC hrsrc;
3102 WCHAR *p;
3103 INT len;
3104
3105 TRACE("adding string from resource\n");
3106
3107 if (!hInstance) return -1;
3108
3109 hrsrc = FindResourceW( hInstance, MAKEINTRESOURCEW((LOWORD(lParam) >> 4) + 1),
3110 (LPWSTR)RT_STRING );
3111 if (!hrsrc)
3112 {
3113 TRACE("string not found in resources\n");
3114 return -1;
3115 }
3116
3118 szString, MAX_RESOURCE_STRING_LENGTH);
3119
3120 TRACE("len=%d %s\n", len, debugstr_w(szString));
3121 if (len == 0 || len == 1)
3122 return nIndex;
3123
3124 TRACE("delimiter: 0x%x\n", *szString);
3125 delimiter = *szString;
3126 p = szString + 1;
3127
3128 while ((next_delim = strchrW(p, delimiter)) != NULL) {
3129 *next_delim = 0;
3130 if (next_delim + 1 >= szString + len)
3131 {
3132 /* this may happen if delimiter == '\0' or if the last char is a
3133 * delimiter (then it is ignored like the native does) */
3134 break;
3135 }
3136
3137 infoPtr->strings = ReAlloc(infoPtr->strings, sizeof(LPWSTR)*(infoPtr->nNumStrings+1));
3138 Str_SetPtrW(&infoPtr->strings[infoPtr->nNumStrings], p);
3139 infoPtr->nNumStrings++;
3140
3141 p = next_delim + 1;
3142 }
3143 }
3144 else {
3145 LPWSTR p = (LPWSTR)lParam;
3146 INT len;
3147
3148 if (p == NULL)
3149 return -1;
3150 TRACE("adding string(s) from array\n");
3151 while (*p) {
3152 len = strlenW (p);
3153
3154 TRACE("len=%d %s\n", len, debugstr_w(p));
3155 infoPtr->strings = ReAlloc(infoPtr->strings, sizeof(LPWSTR)*(infoPtr->nNumStrings+1));
3156 Str_SetPtrW (&infoPtr->strings[infoPtr->nNumStrings], p);
3157 infoPtr->nNumStrings++;
3158
3159 p += (len+1);
3160 }
3161 }
3162
3163 if (fFirstString)
3164 TOOLBAR_CalcToolbar(infoPtr);
3165 return nIndex;
3166}
3167
3168
3169static LRESULT
3171{
3172 BOOL fFirstString = (infoPtr->nNumStrings == 0);
3173 LPSTR p;
3174 INT nIndex;
3175 INT len;
3176
3177 TRACE("%p, %lx\n", hInstance, lParam);
3178
3179 if (IS_INTRESOURCE(lParam)) /* load from resources */
3180 return TOOLBAR_AddStringW(infoPtr, hInstance, lParam);
3181
3182 p = (LPSTR)lParam;
3183 if (p == NULL)
3184 return -1;
3185
3186 TRACE("adding string(s) from array\n");
3187 nIndex = infoPtr->nNumStrings;
3188 while (*p) {
3189 len = strlen (p);
3190 TRACE("len=%d \"%s\"\n", len, p);
3191
3192 infoPtr->strings = ReAlloc(infoPtr->strings, sizeof(LPWSTR)*(infoPtr->nNumStrings+1));
3193 Str_SetPtrAtoW(&infoPtr->strings[infoPtr->nNumStrings], p);
3194 infoPtr->nNumStrings++;
3195
3196 p += (len+1);
3197 }
3198
3199 if (fFirstString)
3200 TOOLBAR_CalcToolbar(infoPtr);
3201 return nIndex;
3202}
3203
3204
3205static LRESULT
3207{
3208 TRACE("auto sizing, style=%#x\n", infoPtr->dwStyle);
3209 TRACE("nRows: %d, infoPtr->nButtonHeight: %d\n", infoPtr->nRows, infoPtr->nButtonHeight);
3210
3211#ifdef __REACTOS__ /* workaround CORE-16169 part 2 of 2 */
3212 if ((infoPtr->dwStyle & TBSTYLE_WRAPABLE) || (infoPtr->dwExStyle & TBSTYLE_EX_VERTICAL))
3213 {
3214 TOOLBAR_LayoutToolbar(infoPtr);
3215 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
3216 }
3217#endif
3218
3219 if (!(infoPtr->dwStyle & CCS_NORESIZE))
3220 {
3221 RECT window_rect, parent_rect;
3222 UINT uPosFlags = SWP_NOZORDER | SWP_NOACTIVATE;
3223 HWND parent;
3224 INT x, y, cx, cy;
3225
3226 parent = GetParent (infoPtr->hwndSelf);
3227
3228 if (!parent || !infoPtr->bDoRedraw)
3229 return 0;
3230
3231 GetClientRect(parent, &parent_rect);
3232
3233 x = parent_rect.left;
3234 y = parent_rect.top;
3235
3236 cy = TOP_BORDER + infoPtr->nRows * infoPtr->nButtonHeight + BOTTOM_BORDER;
3237 cx = parent_rect.right - parent_rect.left;
3238
3239 if ((infoPtr->dwStyle & CCS_BOTTOM) == CCS_NOMOVEY)
3240 {
3241 GetWindowRect(infoPtr->hwndSelf, &window_rect);
3242 MapWindowPoints( 0, parent, (POINT *)&window_rect, 2 );
3243 y = window_rect.top;
3244 }
3245 if ((infoPtr->dwStyle & CCS_BOTTOM) == CCS_BOTTOM)
3246 {
3247 GetWindowRect(infoPtr->hwndSelf, &window_rect);
3248 y = parent_rect.bottom - ( window_rect.bottom - window_rect.top);
3249 }
3250
3251 if (infoPtr->dwStyle & CCS_NOPARENTALIGN)
3252 uPosFlags |= SWP_NOMOVE;
3253
3254 if (!(infoPtr->dwStyle & CCS_NODIVIDER))
3256
3257 if (infoPtr->dwStyle & WS_BORDER)
3258 {
3261 }
3262
3263 SetWindowPos(infoPtr->hwndSelf, NULL, x, y, cx, cy, uPosFlags);
3264 }
3265
3266 if ((infoPtr->dwStyle & TBSTYLE_WRAPABLE) || (infoPtr->dwExStyle & TBSTYLE_EX_VERTICAL))
3267 {
3268 TOOLBAR_LayoutToolbar(infoPtr);
3269 InvalidateRect( infoPtr->hwndSelf, NULL, TRUE );
3270 }
3271
3272 return 0;
3273}
3274
3275
3276static inline LRESULT
3278{
3279 return infoPtr->nNumButtons;
3280}
3281
3282
3283static inline LRESULT
3285{
3286 infoPtr->dwStructSize = Size;
3287
3288 return 0;
3289}
3290
3291
3292static LRESULT
3294{
3295 TBUTTON_INFO *btnPtr;
3296 INT nIndex;
3297
3298 TRACE("button %d, iBitmap now %d\n", Id, Index);
3299
3300 nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3301 if (nIndex == -1)
3302 return FALSE;
3303
3304 btnPtr = &infoPtr->buttons[nIndex];
3305 btnPtr->iBitmap = Index;
3306
3307 /* we HAVE to erase the background, the new bitmap could be */
3308 /* transparent */
3309 InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
3310
3311 return TRUE;
3312}
3313
3314
3315static LRESULT
3317{
3318 TBUTTON_INFO *btnPtr;
3319 INT nIndex;
3320 INT nOldIndex = -1;
3321 BOOL bChecked = FALSE;
3322
3323 nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3324
3325 TRACE("hwnd=%p, btn index=%d, lParam=0x%08lx\n", infoPtr->hwndSelf, nIndex, lParam);
3326
3327 if (nIndex == -1)
3328 return FALSE;
3329
3330 btnPtr = &infoPtr->buttons[nIndex];
3331
3332 bChecked = (btnPtr->fsState & TBSTATE_CHECKED) != 0;
3333
3334 if (!LOWORD(lParam))
3335 btnPtr->fsState &= ~TBSTATE_CHECKED;
3336 else {
3337 if (btnPtr->fsStyle & BTNS_GROUP) {
3338 nOldIndex =
3339 TOOLBAR_GetCheckedGroupButtonIndex (infoPtr, nIndex);
3340 if (nOldIndex == nIndex)
3341 return 0;
3342 if (nOldIndex != -1)
3343 infoPtr->buttons[nOldIndex].fsState &= ~TBSTATE_CHECKED;
3344 }
3345 btnPtr->fsState |= TBSTATE_CHECKED;
3346 }
3347
3348 if( bChecked != LOWORD(lParam) )
3349 {
3350 if (nOldIndex != -1)
3351 InvalidateRect(infoPtr->hwndSelf, &infoPtr->buttons[nOldIndex].rect, TRUE);
3352 InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
3353 }
3354
3355 /* FIXME: Send a WM_NOTIFY?? */
3356
3357 return TRUE;
3358}
3359
3360
3361static LRESULT
3363{
3364 return TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3365}
3366
3367
3368static LRESULT
3370{
3371 CUSTDLG_INFO custInfo;
3372 LRESULT ret;
3373 NMHDR nmhdr;
3374
3375 custInfo.tbInfo = infoPtr;
3376 custInfo.tbHwnd = infoPtr->hwndSelf;
3377
3378 /* send TBN_BEGINADJUST notification */
3379 TOOLBAR_SendNotify (&nmhdr, infoPtr, TBN_BEGINADJUST);
3380
3382 infoPtr->hwndSelf, TOOLBAR_CustomizeDialogProc, (LPARAM)&custInfo);
3383
3384 /* send TBN_ENDADJUST notification */
3385 TOOLBAR_SendNotify (&nmhdr, infoPtr, TBN_ENDADJUST);
3386
3387 return ret;
3388}
3389
3390
3391static LRESULT
3393{
3394 NMTOOLBARW nmtb;
3395 TBUTTON_INFO *btnPtr = &infoPtr->buttons[nIndex];
3396
3397 if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
3398 return FALSE;
3399
3400 memset(&nmtb, 0, sizeof(nmtb));
3401 nmtb.iItem = btnPtr->idCommand;
3402 nmtb.tbButton.iBitmap = btnPtr->iBitmap;
3403 nmtb.tbButton.idCommand = btnPtr->idCommand;
3404 nmtb.tbButton.fsState = btnPtr->fsState;
3405 nmtb.tbButton.fsStyle = btnPtr->fsStyle;
3406 nmtb.tbButton.dwData = btnPtr->dwData;
3407 nmtb.tbButton.iString = btnPtr->iString;
3408 TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_DELETINGBUTTON);
3409
3410 TOOLBAR_TooltipDelTool(infoPtr, &infoPtr->buttons[nIndex]);
3411
3412 infoPtr->nHotItem = -1;
3413 if (infoPtr->nNumButtons == 1) {
3414 TRACE(" simple delete\n");
3415 free_string( infoPtr->buttons );
3416 Free (infoPtr->buttons);
3417 infoPtr->buttons = NULL;
3418 infoPtr->nNumButtons = 0;
3419 }
3420 else {
3421 TBUTTON_INFO *oldButtons = infoPtr->buttons;
3422 TRACE("complex delete [nIndex=%d]\n", nIndex);
3423
3424 infoPtr->nNumButtons--;
3425 infoPtr->buttons = Alloc (sizeof (TBUTTON_INFO) * infoPtr->nNumButtons);
3426 if (nIndex > 0) {
3427 memcpy (&infoPtr->buttons[0], &oldButtons[0],
3428 nIndex * sizeof(TBUTTON_INFO));
3429 }
3430
3431 if (nIndex < infoPtr->nNumButtons) {
3432 memcpy (&infoPtr->buttons[nIndex], &oldButtons[nIndex+1],
3433 (infoPtr->nNumButtons - nIndex) * sizeof(TBUTTON_INFO));
3434 }
3435
3436 free_string( oldButtons + nIndex );
3437 Free (oldButtons);
3438 }
3439
3440 TOOLBAR_LayoutToolbar(infoPtr);
3441
3442 InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
3443
3444 return TRUE;
3445}
3446
3447
3448static LRESULT
3450{
3451 TBUTTON_INFO *btnPtr;
3452 INT nIndex;
3453 DWORD bState;
3454
3455 nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3456
3457 TRACE("hwnd=%p, btn id=%d, lParam=0x%08lx\n", infoPtr->hwndSelf, Id, lParam);
3458
3459 if (nIndex == -1)
3460 return FALSE;
3461
3462 btnPtr = &infoPtr->buttons[nIndex];
3463
3464 bState = btnPtr->fsState & TBSTATE_ENABLED;
3465
3466 /* update the toolbar button state */
3467 if(!LOWORD(lParam)) {
3468 btnPtr->fsState &= ~(TBSTATE_ENABLED | TBSTATE_PRESSED);
3469 } else {
3470 btnPtr->fsState |= TBSTATE_ENABLED;
3471 }
3472
3473 /* redraw the button only if the state of the button changed */
3474 if(bState != (btnPtr->fsState & TBSTATE_ENABLED))
3475 InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
3476
3477 return TRUE;
3478}
3479
3480
3481static inline LRESULT
3483{
3484 return infoPtr->bAnchor;
3485}
3486
3487
3488static LRESULT
3490{
3491 INT nIndex;
3492
3493 nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3494 if (nIndex == -1)
3495 return -1;
3496
3497 return infoPtr->buttons[nIndex].iBitmap;
3498}
3499
3500
3501static inline LRESULT
3503{
3504 return (GetDeviceCaps (0, LOGPIXELSX) >= 120) ? TBBF_LARGE : 0;
3505}
3506
3507
3508static LRESULT
3509TOOLBAR_GetButton (const TOOLBAR_INFO *infoPtr, INT nIndex, TBBUTTON *lpTbb)
3510{
3511 TBUTTON_INFO *btnPtr;
3512
3513 if (lpTbb == NULL)
3514 return FALSE;
3515
3516 if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
3517 return FALSE;
3518
3519 btnPtr = &infoPtr->buttons[nIndex];
3520 lpTbb->iBitmap = btnPtr->iBitmap;
3521 lpTbb->idCommand = btnPtr->idCommand;
3522 lpTbb->fsState = btnPtr->fsState;
3523 lpTbb->fsStyle = btnPtr->fsStyle;
3524 lpTbb->bReserved[0] = 0;
3525 lpTbb->bReserved[1] = 0;
3526 lpTbb->dwData = btnPtr->dwData;
3527 lpTbb->iString = btnPtr->iString;
3528
3529 return TRUE;
3530}
3531
3532
3533static LRESULT
3534TOOLBAR_GetButtonInfoT(const TOOLBAR_INFO *infoPtr, INT Id, LPTBBUTTONINFOW lpTbInfo, BOOL bUnicode)
3535{
3536 /* TBBUTTONINFOW and TBBUTTONINFOA have the same layout*/
3537 TBUTTON_INFO *btnPtr;
3538 INT nIndex;
3539
3540 if (lpTbInfo == NULL)
3541 return -1;
3542
3543 /* MSDN documents an iImageLabel field added in Vista but it is not present in
3544 * the headers and tests shows that even with comctl 6 Vista accepts only the
3545 * original TBBUTTONINFO size
3546 */
3547 if (lpTbInfo->cbSize != sizeof(TBBUTTONINFOW))
3548 {
3549 WARN("Invalid button size\n");
3550 return -1;
3551 }
3552
3553 nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, lpTbInfo->dwMask & TBIF_BYINDEX);
3554 if (nIndex == -1)
3555 return -1;
3556
3557 btnPtr = &infoPtr->buttons[nIndex];
3558 if (lpTbInfo->dwMask & TBIF_COMMAND)
3559 lpTbInfo->idCommand = btnPtr->idCommand;
3560 if (lpTbInfo->dwMask & TBIF_IMAGE)
3561 lpTbInfo->iImage = btnPtr->iBitmap;
3562 if (lpTbInfo->dwMask & TBIF_LPARAM)
3563 lpTbInfo->lParam = btnPtr->dwData;
3564 if (lpTbInfo->dwMask & TBIF_SIZE)
3565 /* tests show that for separators TBIF_SIZE returns not calculated width,
3566 but cx property, that differs from 0 only if application have
3567 specifically set it */
3568 lpTbInfo->cx = (btnPtr->fsStyle & BTNS_SEP)
3569 ? btnPtr->cx : (WORD)(btnPtr->rect.right - btnPtr->rect.left);
3570 if (lpTbInfo->dwMask & TBIF_STATE)
3571 lpTbInfo->fsState = btnPtr->fsState;
3572 if (lpTbInfo->dwMask & TBIF_STYLE)
3573 lpTbInfo->fsStyle = btnPtr->fsStyle;
3574 if (lpTbInfo->dwMask & TBIF_TEXT) {
3575 /* TB_GETBUTTONINFO doesn't retrieve text from the string list, so we
3576 can't use TOOLBAR_GetText here */
3577 if (!IS_INTRESOURCE(btnPtr->iString) && (btnPtr->iString != -1)) {
3578 LPWSTR lpText = (LPWSTR)btnPtr->iString;
3579 if (bUnicode)
3580 Str_GetPtrW(lpText, lpTbInfo->pszText, lpTbInfo->cchText);
3581 else
3582 Str_GetPtrWtoA(lpText, (LPSTR)lpTbInfo->pszText, lpTbInfo->cchText);
3583 } else if (!bUnicode || lpTbInfo->pszText)
3584 lpTbInfo->pszText[0] = '\0';
3585 }
3586 return nIndex;
3587}
3588
3589
3590static inline LRESULT
3592{
3593 return MAKELONG((WORD)infoPtr->nButtonWidth,
3594 (WORD)infoPtr->nButtonHeight);
3595}
3596
3597
3598static LRESULT
3600{
3601 INT nIndex;
3602 LPWSTR lpText;
3603 LRESULT ret = 0;
3604
3605 nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3606 if (nIndex == -1)
3607 return -1;
3608
3609 lpText = TOOLBAR_GetText(infoPtr,&infoPtr->buttons[nIndex]);
3610
3611 if (isW)
3612 {
3613 if (lpText)
3614 {
3615 ret = strlenW (lpText);
3616 if (lpStr) strcpyW (lpStr, lpText);
3617 }
3618 }
3619 else
3620 ret = WideCharToMultiByte( CP_ACP, 0, lpText, -1,
3621 (LPSTR)lpStr, lpStr ? 0x7fffffff : 0, NULL, NULL ) - 1;
3622 return ret;
3623}
3624
3625
3626static LRESULT
3628{
3629 TRACE("hwnd=%p, wParam=%ld\n", infoPtr->hwndSelf, wParam);
3630 /* UNDOCUMENTED: wParam is actually the ID of the image list to return */
3631 return (LRESULT)GETDISIMAGELIST(infoPtr, wParam);
3632}
3633
3634
3635static inline LRESULT
3637{
3638 TRACE("\n");
3639
3640 return infoPtr->dwExStyle;
3641}
3642
3643
3644static LRESULT
3646{
3647 TRACE("hwnd=%p, wParam=%ld\n", infoPtr->hwndSelf, wParam);
3648 /* UNDOCUMENTED: wParam is actually the ID of the image list to return */
3649 return (LRESULT)GETHOTIMAGELIST(infoPtr, wParam);
3650}
3651
3652
3653static LRESULT
3655{
3656 if (!((infoPtr->dwStyle & TBSTYLE_FLAT) || GetWindowTheme (infoPtr->hwndSelf)))
3657 return -1;
3658
3659 if (infoPtr->nHotItem < 0)
3660 return -1;
3661
3662 return (LRESULT)infoPtr->nHotItem;
3663}
3664
3665
3666static LRESULT
3668{
3669 TRACE("hwnd=%p, wParam=%ld\n", infoPtr->hwndSelf, wParam);
3670 /* UNDOCUMENTED: wParam is actually the ID of the image list to return */
3671 return (LRESULT) GETDEFIMAGELIST(infoPtr, wParam);
3672}
3673
3674
3675static LRESULT
3677{
3678 TRACE("hwnd = %p, lptbim = %p\n", infoPtr->hwndSelf, lptbim);
3679
3680 *lptbim = infoPtr->tbim;
3681
3682 return 0;
3683}
3684
3685
3686static inline LRESULT
3688{
3689 TRACE("hwnd = %p\n", infoPtr->hwndSelf);
3690
3691 return (LRESULT)infoPtr->clrInsertMark;
3692}
3693
3694
3695static LRESULT
3696TOOLBAR_GetItemRect (const TOOLBAR_INFO *infoPtr, INT nIndex, LPRECT lpRect)
3697{
3698 TBUTTON_INFO *btnPtr;
3699
3700 btnPtr = &infoPtr->buttons[nIndex];
3701 if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
3702 return FALSE;
3703
3704 if (lpRect == NULL)
3705 return FALSE;
3706 if (btnPtr->fsState & TBSTATE_HIDDEN)
3707 return FALSE;
3708
3709 lpRect->left = btnPtr->rect.left;
3710 lpRect->right = btnPtr->rect.right;
3711 lpRect->bottom = btnPtr->rect.bottom;
3712 lpRect->top = btnPtr->rect.top;
3713
3714 return TRUE;
3715}
3716
3717
3718static LRESULT
3720{
3721 if (lpSize == NULL)
3722 return FALSE;
3723
3724 lpSize->cx = infoPtr->rcBound.right - infoPtr->rcBound.left;
3725 lpSize->cy = infoPtr->rcBound.bottom - infoPtr->rcBound.top;
3726
3727 TRACE("maximum size %d x %d\n",
3728 infoPtr->rcBound.right - infoPtr->rcBound.left,
3729 infoPtr->rcBound.bottom - infoPtr->rcBound.top);
3730
3731 return TRUE;
3732}
3733
3734#ifdef __REACTOS__
3735static LRESULT
3736TOOLBAR_GetMetrics(const TOOLBAR_INFO *infoPtr, TBMETRICS *pMetrics)
3737{
3738 if (pMetrics == NULL || pMetrics->cbSize != sizeof(TBMETRICS))
3739 return 0;
3740
3741 if (pMetrics->dwMask & TBMF_PAD)
3742 {
3743 pMetrics->cxPad = infoPtr->szPadding.cx;
3744 pMetrics->cyPad = infoPtr->szPadding.cy;
3745 }
3746
3747 if (pMetrics->dwMask & TBMF_BARPAD)
3748 {
3749 pMetrics->cxBarPad = infoPtr->szBarPadding.cx;
3750 pMetrics->cyBarPad = infoPtr->szBarPadding.cy;
3751 }
3752
3753 if (pMetrics->dwMask & TBMF_BUTTONSPACING)
3754 {
3755 pMetrics->cxButtonSpacing = infoPtr->szSpacing.cx;
3756 pMetrics->cyButtonSpacing = infoPtr->szSpacing.cy;
3757 }
3758
3759 return 0;
3760}
3761#endif
3762
3763/* << TOOLBAR_GetObject >> */
3764
3765
3766static inline LRESULT
3768{
3769 return MAKELONG(infoPtr->szPadding.cx, infoPtr->szPadding.cy);
3770}
3771
3772
3773static LRESULT
3774TOOLBAR_GetRect (const TOOLBAR_INFO *infoPtr, INT Id, LPRECT lpRect)
3775{
3776 TBUTTON_INFO *btnPtr;
3777 INT nIndex;
3778
3779 nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3780 btnPtr = &infoPtr->buttons[nIndex];
3781 if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
3782 return FALSE;
3783
3784 if (lpRect == NULL)
3785 return FALSE;
3786
3787 lpRect->left = btnPtr->rect.left;
3788 lpRect->right = btnPtr->rect.right;
3789 lpRect->bottom = btnPtr->rect.bottom;
3790 lpRect->top = btnPtr->rect.top;
3791
3792 return TRUE;
3793}
3794
3795
3796static inline LRESULT
3798{
3799 return infoPtr->nRows;
3800}
3801
3802
3803static LRESULT
3805{
3806 INT nIndex;
3807
3808 nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3809 if (nIndex == -1)
3810 return -1;
3811
3812 return infoPtr->buttons[nIndex].fsState;
3813}
3814
3815
3816static inline LRESULT
3818{
3819 return infoPtr->dwStyle;
3820}
3821
3822
3823static inline LRESULT
3825{
3826 return infoPtr->nMaxTextRows;
3827}
3828
3829
3830static LRESULT
3832{
3833 if ((infoPtr->dwStyle & TBSTYLE_TOOLTIPS) && (infoPtr->hwndToolTip == NULL))
3835 return (LRESULT)infoPtr->hwndToolTip;
3836}
3837
3838
3839static LRESULT
3841{
3842 TRACE("%s hwnd=%p\n",
3843 infoPtr->bUnicode ? "TRUE" : "FALSE", infoPtr->hwndSelf);
3844
3845 return infoPtr->bUnicode;
3846}
3847
3848
3849static inline LRESULT
3851{
3852 return infoPtr->iVersion;
3853}
3854
3855
3856static LRESULT
3858{
3859 TBUTTON_INFO *btnPtr;
3860 BYTE oldState;
3861 INT nIndex;
3862
3863 TRACE("\n");
3864
3865 nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3866 if (nIndex == -1)
3867 return FALSE;
3868
3869 btnPtr = &infoPtr->buttons[nIndex];
3870 oldState = btnPtr->fsState;
3871
3872 if (fHide)
3873 btnPtr->fsState |= TBSTATE_HIDDEN;
3874 else
3875 btnPtr->fsState &= ~TBSTATE_HIDDEN;
3876
3877 if (oldState != btnPtr->fsState) {
3878 TOOLBAR_LayoutToolbar (infoPtr);
3879 InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
3880 }
3881
3882 return TRUE;
3883}
3884
3885
3886static inline LRESULT
3887TOOLBAR_HitTest (const TOOLBAR_INFO *infoPtr, const POINT* lpPt)
3888{
3889 return TOOLBAR_InternalHitTest (infoPtr, lpPt, NULL);
3890}
3891
3892
3893static LRESULT
3894TOOLBAR_Indeterminate (const TOOLBAR_INFO *infoPtr, INT Id, BOOL fIndeterminate)
3895{
3896 TBUTTON_INFO *btnPtr;
3897 INT nIndex;
3898