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