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