ReactOS  0.4.15-dev-1201-gb2cf5a4
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 #ifdef __REACTOS__ /* workaround CORE-16169 part 1 of 2 */
1457  /* if width is zero then return */
1458  if (infoPtr->client_rect.right == 0) return;
1459 #endif
1460 
1461  btnPtr = infoPtr->buttons;
1462  x = infoPtr->nIndent;
1463  width = infoPtr->client_rect.right - infoPtr->client_rect.left;
1464 
1465  bButtonWrap = FALSE;
1466 
1467  TRACE("start ButtonWidth=%d, BitmapWidth=%d, width=%d, nIndent=%d\n",
1468  infoPtr->nButtonWidth, infoPtr->nBitmapWidth, width,
1469  infoPtr->nIndent);
1470 
1471  for (i = 0; i < infoPtr->nNumButtons; i++ )
1472  {
1473  btnPtr[i].fsState &= ~TBSTATE_WRAP;
1474 
1475  if (btnPtr[i].fsState & TBSTATE_HIDDEN)
1476  continue;
1477 
1478  if (btnPtr[i].cx > 0)
1479  cx = btnPtr[i].cx;
1480  /* horizontal separators are treated as buttons for width */
1481  else if ((btnPtr[i].fsStyle & BTNS_SEP) &&
1482  !(infoPtr->dwStyle & CCS_VERT))
1483  cx = (btnPtr[i].iBitmap > 0) ? btnPtr[i].iBitmap : SEPARATOR_WIDTH;
1484  else
1485  cx = infoPtr->nButtonWidth;
1486 
1487  if (!btnPtr[i].cx && button_has_ddarrow( infoPtr, btnPtr + i ))
1488  cx += DDARROW_WIDTH;
1489 
1490  /* Two or more adjacent separators form a separator group. */
1491  /* The first separator in a group should be wrapped to the */
1492  /* next row if the previous wrapping is on a button. */
1493  if( bButtonWrap &&
1494  (btnPtr[i].fsStyle & BTNS_SEP) &&
1495  (i + 1 < infoPtr->nNumButtons ) &&
1496  (btnPtr[i + 1].fsStyle & BTNS_SEP) )
1497  {
1498  TRACE("wrap point 1 btn %d style %02x\n", i, btnPtr[i].fsStyle);
1499  btnPtr[i].fsState |= TBSTATE_WRAP;
1500  x = infoPtr->nIndent;
1501  i++;
1502  bButtonWrap = FALSE;
1503  continue;
1504  }
1505 
1506  /* The layout makes sure the bitmap is visible, but not the button. */
1507  /* Test added to also wrap after a button that starts a row but */
1508  /* is bigger than the area. - GA 8/01 */
1509  if ((x + cx - (infoPtr->nButtonWidth - infoPtr->nBitmapWidth) / 2 > width) ||
1510  ((x == infoPtr->nIndent) && (cx > width)))
1511  {
1512  BOOL bFound = FALSE;
1513 
1514  /* If the current button is a separator and not hidden, */
1515  /* go to the next until it reaches a non separator. */
1516  /* Wrap the last separator if it is before a button. */
1517  while( ( ((btnPtr[i].fsStyle & BTNS_SEP) &&
1518  !(btnPtr[i].fsStyle & BTNS_DROPDOWN)) ||
1519  (btnPtr[i].fsState & TBSTATE_HIDDEN) ) &&
1520  i < infoPtr->nNumButtons )
1521  {
1522  i++;
1523  bFound = TRUE;
1524  }
1525 
1526  if( bFound && i < infoPtr->nNumButtons )
1527  {
1528  i--;
1529  TRACE("wrap point 2 btn %d style %02x, x=%d, cx=%d\n",
1530  i, btnPtr[i].fsStyle, x, cx);
1531  btnPtr[i].fsState |= TBSTATE_WRAP;
1532  x = infoPtr->nIndent;
1533  bButtonWrap = FALSE;
1534  continue;
1535  }
1536  else if ( i >= infoPtr->nNumButtons)
1537  break;
1538 
1539  /* If the current button is not a separator, find the last */
1540  /* separator and wrap it. */
1541  for ( j = i - 1; j >= 0 && !(btnPtr[j].fsState & TBSTATE_WRAP); j--)
1542  {
1543  if ((btnPtr[j].fsStyle & BTNS_SEP) &&
1544  !(btnPtr[j].fsState & TBSTATE_HIDDEN))
1545  {
1546  bFound = TRUE;
1547  i = j;
1548  TRACE("wrap point 3 btn %d style %02x, x=%d, cx=%d\n",
1549  i, btnPtr[i].fsStyle, x, cx);
1550  x = infoPtr->nIndent;
1551  btnPtr[j].fsState |= TBSTATE_WRAP;
1552  bButtonWrap = FALSE;
1553  break;
1554  }
1555  }
1556 
1557  /* If no separator available for wrapping, wrap one of */
1558  /* non-hidden previous button. */
1559  if (!bFound)
1560  {
1561  for ( j = i - 1;
1562  j >= 0 && !(btnPtr[j].fsState & TBSTATE_WRAP); j--)
1563  {
1564  if (btnPtr[j].fsState & TBSTATE_HIDDEN)
1565  continue;
1566 
1567  bFound = TRUE;
1568  i = j;
1569  TRACE("wrap point 4 btn %d style %02x, x=%d, cx=%d\n",
1570  i, btnPtr[i].fsStyle, x, cx);
1571  x = infoPtr->nIndent;
1572  btnPtr[j].fsState |= TBSTATE_WRAP;
1573  bButtonWrap = TRUE;
1574  break;
1575  }
1576  }
1577 
1578  /* If all above failed, wrap the current button. */
1579  if (!bFound)
1580  {
1581  TRACE("wrap point 5 btn %d style %02x, x=%d, cx=%d\n",
1582  i, btnPtr[i].fsStyle, x, cx);
1583  btnPtr[i].fsState |= TBSTATE_WRAP;
1584  x = infoPtr->nIndent;
1585  if (btnPtr[i].fsStyle & BTNS_SEP )
1586  bButtonWrap = FALSE;
1587  else
1588  bButtonWrap = TRUE;
1589  }
1590  }
1591  else {
1592  TRACE("wrap point 6 btn %d style %02x, x=%d, cx=%d\n",
1593  i, btnPtr[i].fsStyle, x, cx);
1594  x += cx;
1595  }
1596  }
1597 }
1598 
1599 
1600 /***********************************************************************
1601 * TOOLBAR_MeasureButton
1602 *
1603 * Calculates the width and height required for a button. Used in
1604 * TOOLBAR_CalcToolbar to set the all-button width and height and also for
1605 * the width of buttons that are autosized.
1606 *
1607 * Note that it would have been rather elegant to use one piece of code for
1608 * both the laying out of the toolbar and for controlling where button parts
1609 * are drawn, but the native control has inconsistencies between the two that
1610 * prevent this from being effectively. These inconsistencies can be seen as
1611 * artefacts where parts of the button appear outside of the bounding button
1612 * rectangle.
1613 *
1614 * There are several cases for the calculation of the button dimensions and
1615 * button part positioning:
1616 *
1617 * List
1618 * ====
1619 *
1620 * With Bitmap:
1621 *
1622 * +--------------------------------------------------------+ ^
1623 * | ^ ^ | |
1624 * | | pad.cy / 2 | centered | |
1625 * | pad.cx/2 + cxedge +--------------+ +------------+ | | DEFPAD_CY +
1626 * |<----------------->| nBitmapWidth | | Text | | | max(nBitmapHeight, szText.cy)
1627 * | |<------------>| | | | |
1628 * | +--------------+ +------------+ | |
1629 * |<-------------------------------------->| | |
1630 * | cxedge + iListGap + nBitmapWidth + 2 |<-----------> | |
1631 * | szText.cx | |
1632 * +--------------------------------------------------------+ -
1633 * <-------------------------------------------------------->
1634 * 2*cxedge + nBitmapWidth + iListGap + szText.cx + pad.cx
1635 *
1636 * Without Bitmap (I_IMAGENONE):
1637 *
1638 * +-----------------------------------+ ^
1639 * | ^ | |
1640 * | | centered | | LISTPAD_CY +
1641 * | +------------+ | | szText.cy
1642 * | | Text | | |
1643 * | | | | |
1644 * | +------------+ | |
1645 * |<----------------->| | |
1646 * | cxedge |<-----------> | |
1647 * | szText.cx | |
1648 * +-----------------------------------+ -
1649 * <----------------------------------->
1650 * szText.cx + pad.cx
1651 *
1652 * Without text:
1653 *
1654 * +--------------------------------------+ ^
1655 * | ^ | |
1656 * | | padding.cy/2 | | DEFPAD_CY +
1657 * | +------------+ | | nBitmapHeight
1658 * | | Bitmap | | |
1659 * | | | | |
1660 * | +------------+ | |
1661 * |<------------------->| | |
1662 * | cxedge + iListGap/2 |<-----------> | |
1663 * | nBitmapWidth | |
1664 * +--------------------------------------+ -
1665 * <-------------------------------------->
1666 * 2*cxedge + nBitmapWidth + iListGap
1667 *
1668 * Non-List
1669 * ========
1670 *
1671 * With bitmap:
1672 *
1673 * +-----------------------------------+ ^
1674 * | ^ | |
1675 * | | pad.cy / 2 | | nBitmapHeight +
1676 * | - | | szText.cy +
1677 * | +------------+ | | DEFPAD_CY + 1
1678 * | centered | Bitmap | | |
1679 * |<----------------->| | | |
1680 * | +------------+ | |
1681 * | ^ | |
1682 * | 1 | | |
1683 * | - | |
1684 * | centered +---------------+ | |
1685 * |<--------------->| Text | | |
1686 * | +---------------+ | |
1687 * +-----------------------------------+ -
1688 * <----------------------------------->
1689 * pad.cx + max(nBitmapWidth, szText.cx)
1690 *
1691 * Without bitmaps (NULL imagelist or ImageList_GetImageCount() = 0):
1692 *
1693 * +---------------------------------------+ ^
1694 * | ^ | |
1695 * | | 2 + pad.cy / 2 | |
1696 * | - | | szText.cy +
1697 * | centered +-----------------+ | | pad.cy + 2
1698 * |<--------------->| Text | | |
1699 * | +-----------------+ | |
1700 * | | |
1701 * +---------------------------------------+ -
1702 * <--------------------------------------->
1703 * 2*cxedge + pad.cx + szText.cx
1704 *
1705 * Without text:
1706 * As for with bitmaps, but with szText.cx zero.
1707 */
1708 static inline SIZE TOOLBAR_MeasureButton(const TOOLBAR_INFO *infoPtr, SIZE sizeString,
1709  BOOL bHasBitmap, BOOL bValidImageList)
1710 {
1711  SIZE sizeButton;
1712  if (infoPtr->dwStyle & TBSTYLE_LIST)
1713  {
1714  /* set button height from bitmap / text height... */
1715  sizeButton.cy = max((bHasBitmap ? infoPtr->nBitmapHeight : 0),
1716  sizeString.cy);
1717 
1718  /* ... add on the necessary padding */
1719  if (bValidImageList)
1720  {
1721 #ifdef __REACTOS__
1722  sizeButton.cy += infoPtr->szPadding.cy;
1723  if (!bHasBitmap)
1724 #else
1725  if (bHasBitmap)
1726  sizeButton.cy += DEFPAD_CY;
1727  else
1728 #endif
1729  sizeButton.cy += LISTPAD_CY;
1730  }
1731  else
1732  sizeButton.cy += infoPtr->szPadding.cy;
1733 
1734  /* calculate button width */
1735  sizeButton.cx = 2*GetSystemMetrics(SM_CXEDGE) +
1736  infoPtr->nBitmapWidth + infoPtr->iListGap;
1737  if (sizeString.cx > 0)
1738  sizeButton.cx += sizeString.cx + infoPtr->szPadding.cx;
1739 
1740  }
1741  else
1742  {
1743  if (bHasBitmap)
1744  {
1745 #ifdef __REACTOS__
1746  sizeButton.cy = infoPtr->nBitmapHeight + infoPtr->szPadding.cy;
1747 #else
1748  sizeButton.cy = infoPtr->nBitmapHeight + DEFPAD_CY;
1749 #endif
1750  if (sizeString.cy > 0)
1751  sizeButton.cy += 1 + sizeString.cy;
1752  sizeButton.cx = infoPtr->szPadding.cx +
1753  max(sizeString.cx, infoPtr->nBitmapWidth);
1754  }
1755  else
1756  {
1757  sizeButton.cy = sizeString.cy + infoPtr->szPadding.cy +
1759  sizeButton.cx = infoPtr->szPadding.cx +
1760  max(2*GetSystemMetrics(SM_CXEDGE) + sizeString.cx, infoPtr->nBitmapWidth);
1761  }
1762  }
1763 
1764 #ifdef __REACTOS__
1765  sizeButton.cx += infoPtr->themeMargins.cxLeftWidth + infoPtr->themeMargins.cxRightWidth;
1766  sizeButton.cy += infoPtr->themeMargins.cyTopHeight + infoPtr->themeMargins.cyBottomHeight;
1767 #endif
1768 
1769  return sizeButton;
1770 }
1771 
1772 
1773 /***********************************************************************
1774 * TOOLBAR_CalcToolbar
1775 *
1776 * This function calculates button and separator placement. It first
1777 * calculates the button sizes, gets the toolbar window width and then
1778 * calls TOOLBAR_WrapToolbar to determine which buttons we need to wrap
1779 * on. It assigns a new location to each item and sends this location to
1780 * the tooltip window if appropriate. Finally, it updates the rcBound
1781 * rect and calculates the new required toolbar window height.
1782 */
1783 static void
1785 {
1786  SIZE sizeString, sizeButton;
1787  BOOL validImageList = FALSE;
1788 
1789  TOOLBAR_CalcStrings (infoPtr, &sizeString);
1790 
1791  TOOLBAR_DumpToolbar (infoPtr, __LINE__);
1792 
1793  if (TOOLBAR_IsValidImageList(infoPtr, 0))
1794  validImageList = TRUE;
1795  sizeButton = TOOLBAR_MeasureButton(infoPtr, sizeString, TRUE, validImageList);
1796  infoPtr->nButtonWidth = sizeButton.cx;
1797  infoPtr->nButtonHeight = sizeButton.cy;
1798  infoPtr->iTopMargin = default_top_margin(infoPtr);
1799 
1800  if ( infoPtr->cxMin >= 0 && infoPtr->nButtonWidth < infoPtr->cxMin )
1801  infoPtr->nButtonWidth = infoPtr->cxMin;
1802  if ( infoPtr->cxMax > 0 && infoPtr->nButtonWidth > infoPtr->cxMax )
1803  infoPtr->nButtonWidth = infoPtr->cxMax;
1804 
1805  TOOLBAR_LayoutToolbar(infoPtr);
1806 }
1807 
1808 static void
1810 {
1811  TBUTTON_INFO *btnPtr;
1812  SIZE sizeButton;
1813  INT i, nRows, nSepRows;
1814  INT x, y, cx, cy;
1815  BOOL bWrap;
1816  BOOL validImageList = TOOLBAR_IsValidImageList(infoPtr, 0);
1817 
1818  TOOLBAR_WrapToolbar(infoPtr);
1819 
1820  x = infoPtr->nIndent;
1821  y = infoPtr->iTopMargin;
1822  cx = infoPtr->nButtonWidth;
1823  cy = infoPtr->nButtonHeight;
1824 
1825  nRows = nSepRows = 0;
1826 
1827  infoPtr->rcBound.top = y;
1828  infoPtr->rcBound.left = x;
1829  infoPtr->rcBound.bottom = y + cy;
1830  infoPtr->rcBound.right = x;
1831 
1832  btnPtr = infoPtr->buttons;
1833 
1834  TRACE("cy=%d\n", cy);
1835 
1836  for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++ )
1837  {
1838  bWrap = FALSE;
1839  if (btnPtr->fsState & TBSTATE_HIDDEN)
1840  {
1841  SetRectEmpty (&btnPtr->rect);
1842  TOOLBAR_TooltipSetRect(infoPtr, btnPtr);
1843  continue;
1844  }
1845 
1846  cy = infoPtr->nButtonHeight;
1847 
1848  if (btnPtr->fsStyle & BTNS_SEP) {
1849  if (infoPtr->dwStyle & CCS_VERT) {
1850  cy = (btnPtr->iBitmap > 0) ? btnPtr->iBitmap : SEPARATOR_WIDTH;
1851  cx = (btnPtr->cx > 0) ? btnPtr->cx : infoPtr->nButtonWidth;
1852  }
1853  else
1854  cx = (btnPtr->cx > 0) ? btnPtr->cx :
1855  (btnPtr->iBitmap > 0) ? btnPtr->iBitmap : SEPARATOR_WIDTH;
1856  }
1857  else
1858  {
1859  if (btnPtr->cx)
1860  cx = btnPtr->cx;
1861 #ifdef __REACTOS__
1862  /* Revert Wine Commit 5b7b911 as it breaks Explorer Toolbar Buttons
1863  FIXME: Revisit this when the bug is fixed. CORE-9970 */
1864  else if ((infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) ||
1865  (btnPtr->fsStyle & BTNS_AUTOSIZE))
1866 #else
1867  else if (btnPtr->fsStyle & BTNS_AUTOSIZE)
1868 #endif
1869  {
1870  SIZE sz;
1871  HDC hdc;
1872  HFONT hOldFont;
1873 
1874  hdc = GetDC (infoPtr->hwndSelf);
1875  hOldFont = SelectObject (hdc, infoPtr->hFont);
1876 
1877  TOOLBAR_MeasureString(infoPtr, btnPtr, hdc, &sz);
1878 
1879  SelectObject (hdc, hOldFont);
1880  ReleaseDC (infoPtr->hwndSelf, hdc);
1881 
1882  sizeButton = TOOLBAR_MeasureButton(infoPtr, sz,
1883  TOOLBAR_IsValidBitmapIndex(infoPtr, infoPtr->buttons[i].iBitmap),
1884  validImageList);
1885  cx = sizeButton.cx;
1886  }
1887  else
1888  cx = infoPtr->nButtonWidth;
1889 
1890  /* if size has been set manually then don't add on extra space
1891  * for the drop down arrow */
1892  if (!btnPtr->cx && button_has_ddarrow( infoPtr, btnPtr ))
1893  cx += DDARROW_WIDTH;
1894  }
1895  if (btnPtr->fsState & TBSTATE_WRAP)
1896  bWrap = TRUE;
1897 
1898  SetRect (&btnPtr->rect, x, y, x + cx, y + cy);
1899 
1900  if (infoPtr->rcBound.left > x)
1901  infoPtr->rcBound.left = x;
1902  if (infoPtr->rcBound.right < x + cx)
1903  infoPtr->rcBound.right = x + cx;
1904  if (infoPtr->rcBound.bottom < y + cy)
1905  infoPtr->rcBound.bottom = y + cy;
1906 
1907  TOOLBAR_TooltipSetRect(infoPtr, btnPtr);
1908 
1909  /* btnPtr->nRow is zero based. The space between the rows is */
1910  /* also considered as a row. */
1911  btnPtr->nRow = nRows + nSepRows;
1912 
1913  TRACE("button %d style=%x, bWrap=%d, nRows=%d, nSepRows=%d, btnrow=%d, (%d,%d)-(%d,%d)\n",
1914  i, btnPtr->fsStyle, bWrap, nRows, nSepRows, btnPtr->nRow,
1915  x, y, x+cx, y+cy);
1916 
1917  if( bWrap )
1918  {
1919  if ( !(btnPtr->fsStyle & BTNS_SEP) )
1920 #ifdef __REACTOS__
1921  y += cy + infoPtr->szSpacing.cy;
1922 #else
1923  y += cy;
1924 #endif
1925  else
1926  {
1927  if ( !(infoPtr->dwStyle & CCS_VERT))
1928  y += cy + ( (btnPtr->cx > 0 ) ?
1929  btnPtr->cx : SEPARATOR_WIDTH) * 2 /3;
1930  else
1931 #ifdef __REACTOS__
1932  y += cy + infoPtr->szSpacing.cy;
1933 #else
1934  y += cy;
1935 #endif
1936 
1937  /* nSepRows is used to calculate the extra height following */
1938  /* the last row. */
1939  nSepRows++;
1940  }
1941  x = infoPtr->nIndent;
1942 
1943  /* Increment row number unless this is the last button */
1944  /* and it has Wrap set. */
1945  if (i != infoPtr->nNumButtons-1)
1946  nRows++;
1947  }
1948  else
1949 #ifdef __REACTOS__
1950  x += cx + infoPtr->szSpacing.cx;
1951 #else
1952  x += cx;
1953 #endif
1954  }
1955 
1956  /* infoPtr->nRows is the number of rows on the toolbar */
1957  infoPtr->nRows = nRows + nSepRows + 1;
1958 
1959  TRACE("toolbar button width %d\n", infoPtr->nButtonWidth);
1960 }
1961 
1962 
1963 static INT
1964 TOOLBAR_InternalHitTest (const TOOLBAR_INFO *infoPtr, const POINT *lpPt, BOOL *button)
1965 {
1966  TBUTTON_INFO *btnPtr;
1967  INT i;
1968 
1969  if (button)
1970  *button = FALSE;
1971 
1972  btnPtr = infoPtr->buttons;
1973  for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++) {
1974  if (btnPtr->fsState & TBSTATE_HIDDEN)
1975  continue;
1976 
1977  if (btnPtr->fsStyle & BTNS_SEP) {
1978  if (PtInRect (&btnPtr->rect, *lpPt)) {
1979  TRACE(" ON SEPARATOR %d\n", i);
1980  return -i;
1981  }
1982  }
1983  else {
1984  if (PtInRect (&btnPtr->rect, *lpPt)) {
1985  TRACE(" ON BUTTON %d\n", i);
1986  if (button)
1987  *button = TRUE;
1988  return i;
1989  }
1990  }
1991  }
1992 
1993  TRACE(" NOWHERE\n");
1994  return TOOLBAR_NOWHERE;
1995 }
1996 
1997 
1998 /* worker for TB_ADDBUTTONS and TB_INSERTBUTTON */
1999 static BOOL
2000 TOOLBAR_InternalInsertButtonsT(TOOLBAR_INFO *infoPtr, INT iIndex, UINT nAddButtons, const TBBUTTON *lpTbb, BOOL fUnicode)
2001 {
2002  INT nOldButtons, nNewButtons, iButton;
2003  BOOL fHasString = FALSE;
2004 
2005  if (iIndex < 0) /* iIndex can be negative, what means adding at the end */
2006  iIndex = infoPtr->nNumButtons;
2007 
2008  nOldButtons = infoPtr->nNumButtons;
2009  nNewButtons = nOldButtons + nAddButtons;
2010 
2011  infoPtr->buttons = ReAlloc(infoPtr->buttons, sizeof(TBUTTON_INFO)*nNewButtons);
2012  memmove(&infoPtr->buttons[iIndex + nAddButtons], &infoPtr->buttons[iIndex],
2013  (nOldButtons - iIndex) * sizeof(TBUTTON_INFO));
2014  infoPtr->nNumButtons += nAddButtons;
2015 
2016  /* insert new buttons data */
2017  for (iButton = 0; iButton < nAddButtons; iButton++) {
2018  TBUTTON_INFO *btnPtr = &infoPtr->buttons[iIndex + iButton];
2019  INT_PTR str;
2020 
2021  TOOLBAR_DumpTBButton(lpTbb + iButton, fUnicode);
2022 
2023  ZeroMemory(btnPtr, sizeof(*btnPtr));
2024 
2025  btnPtr->iBitmap = lpTbb[iButton].iBitmap;
2026  btnPtr->idCommand = lpTbb[iButton].idCommand;
2027  btnPtr->fsState = lpTbb[iButton].fsState;
2028  btnPtr->fsStyle = lpTbb[iButton].fsStyle;
2029  btnPtr->dwData = lpTbb[iButton].dwData;
2030 
2031  if (btnPtr->fsStyle & BTNS_SEP)
2032  str = -1;
2033  else
2034  str = lpTbb[iButton].iString;
2035  set_string_index( btnPtr, str, fUnicode );
2036  fHasString |= TOOLBAR_ButtonHasString( btnPtr );
2037 
2038  TOOLBAR_TooltipAddTool(infoPtr, btnPtr);
2039  }
2040 
2041  if (infoPtr->nNumStrings > 0 || fHasString)
2042  TOOLBAR_CalcToolbar(infoPtr);
2043  else
2044  TOOLBAR_LayoutToolbar(infoPtr);
2045  TOOLBAR_AutoSize(infoPtr);
2046 
2047  TOOLBAR_DumpToolbar(infoPtr, __LINE__);
2048  InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
2049  return TRUE;
2050 }
2051 
2052 
2053 static INT
2054 TOOLBAR_GetButtonIndex (const TOOLBAR_INFO *infoPtr, INT idCommand, BOOL CommandIsIndex)
2055 {
2056  TBUTTON_INFO *btnPtr;
2057  INT i;
2058 
2059  if (CommandIsIndex) {
2060  TRACE("command is really index command=%d\n", idCommand);
2061  if (idCommand >= infoPtr->nNumButtons) return -1;
2062  return idCommand;
2063  }
2064  btnPtr = infoPtr->buttons;
2065  for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++) {
2066  if (btnPtr->idCommand == idCommand) {
2067  TRACE("command=%d index=%d\n", idCommand, i);
2068  return i;
2069  }
2070  }
2071  TRACE("no index found for command=%d\n", idCommand);
2072  return -1;
2073 }
2074 
2075 
2076 static INT
2078 {
2079  TBUTTON_INFO *btnPtr;
2080  INT nRunIndex;
2081 
2082  if ((nIndex < 0) || (nIndex > infoPtr->nNumButtons))
2083  return -1;
2084 
2085  /* check index button */
2086  btnPtr = &infoPtr->buttons[nIndex];
2087  if ((btnPtr->fsStyle & BTNS_CHECKGROUP) == BTNS_CHECKGROUP) {
2088  if (btnPtr->fsState & TBSTATE_CHECKED)
2089  return nIndex;
2090  }
2091 
2092  /* check previous buttons */
2093  nRunIndex = nIndex - 1;
2094  while (nRunIndex >= 0) {
2095  btnPtr = &infoPtr->buttons[nRunIndex];
2096  if ((btnPtr->fsStyle & BTNS_GROUP) == BTNS_GROUP) {
2097  if (btnPtr->fsState & TBSTATE_CHECKED)
2098  return nRunIndex;
2099  }
2100  else
2101  break;
2102  nRunIndex--;
2103  }
2104 
2105  /* check next buttons */
2106  nRunIndex = nIndex + 1;
2107  while (nRunIndex < infoPtr->nNumButtons) {
2108  btnPtr = &infoPtr->buttons[nRunIndex];
2109  if ((btnPtr->fsStyle & BTNS_GROUP) == BTNS_GROUP) {
2110  if (btnPtr->fsState & TBSTATE_CHECKED)
2111  return nRunIndex;
2112  }
2113  else
2114  break;
2115  nRunIndex++;
2116  }
2117 
2118  return -1;
2119 }
2120 
2121 
2122 static VOID
2123 TOOLBAR_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
2125 {
2126  MSG msg;
2127 
2128  msg.hwnd = hwndMsg;
2129  msg.message = uMsg;
2130  msg.wParam = wParam;
2131  msg.lParam = lParam;
2132  msg.time = GetMessageTime ();
2133  msg.pt.x = (short)LOWORD(GetMessagePos ());
2134  msg.pt.y = (short)HIWORD(GetMessagePos ());
2135 
2136  SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
2137 }
2138 
2139 static void
2141 {
2142  if (infoPtr->hwndToolTip && !(button->fsStyle & BTNS_SEP)) {
2143  TTTOOLINFOW ti;
2144 
2145  ZeroMemory(&ti, sizeof(TTTOOLINFOW));
2146  ti.cbSize = sizeof (TTTOOLINFOW);
2147  ti.hwnd = infoPtr->hwndSelf;
2148  ti.uId = button->idCommand;
2149  ti.hinst = 0;
2151  /* ti.lParam = random value from the stack? */
2152 
2154  0, (LPARAM)&ti);
2155  }
2156 }
2157 
2158 static void
2160 {
2161  if ((infoPtr->hwndToolTip) && !(button->fsStyle & BTNS_SEP)) {
2162  TTTOOLINFOW ti;
2163 
2164  ZeroMemory(&ti, sizeof(ti));
2165  ti.cbSize = sizeof(ti);
2166  ti.hwnd = infoPtr->hwndSelf;
2167  ti.uId = button->idCommand;
2168 
2169  SendMessageW(infoPtr->hwndToolTip, TTM_DELTOOLW, 0, (LPARAM)&ti);
2170  }
2171 }
2172 
2173 static void TOOLBAR_TooltipSetRect(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *button)
2174 {
2175  /* Set the toolTip only for non-hidden, non-separator button */
2176  if (infoPtr->hwndToolTip && !(button->fsStyle & BTNS_SEP))
2177  {
2178  TTTOOLINFOW ti;
2179 
2180  ZeroMemory(&ti, sizeof(ti));
2181  ti.cbSize = sizeof(ti);
2182  ti.hwnd = infoPtr->hwndSelf;
2183  ti.uId = button->idCommand;
2184  ti.rect = button->rect;
2185  SendMessageW(infoPtr->hwndToolTip, TTM_NEWTOOLRECTW, 0, (LPARAM)&ti);
2186  }
2187 }
2188 
2189 /* Creates the tooltip control */
2190 static void
2192 {
2193  int i;
2194  NMTOOLTIPSCREATED nmttc;
2195 
2198  infoPtr->hwndSelf, 0, 0, 0);
2199 
2200  if (!infoPtr->hwndToolTip)
2201  return;
2202 
2203  /* Send NM_TOOLTIPSCREATED notification */
2204  nmttc.hwndToolTips = infoPtr->hwndToolTip;
2205  TOOLBAR_SendNotify(&nmttc.hdr, infoPtr, NM_TOOLTIPSCREATED);
2206 
2207  for (i = 0; i < infoPtr->nNumButtons; i++)
2208  {
2209  TOOLBAR_TooltipAddTool(infoPtr, &infoPtr->buttons[i]);
2210  TOOLBAR_TooltipSetRect(infoPtr, &infoPtr->buttons[i]);
2211  }
2212 }
2213 
2214 /* keeps available button list box sorted by button id */
2216 {
2217  int i;
2218  int count;
2219  PCUSTOMBUTTON btnInfo;
2220  HWND hwndAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
2221 
2222  TRACE("button %s, idCommand %d\n", debugstr_w(btnInfoNew->text), btnInfoNew->btn.idCommand);
2223 
2224  count = SendMessageW(hwndAvail, LB_GETCOUNT, 0, 0);
2225 
2226  /* position 0 is always separator */
2227  for (i = 1; i < count; i++)
2228  {
2229  btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndAvail, LB_GETITEMDATA, i, 0);
2230  if (btnInfoNew->btn.idCommand < btnInfo->btn.idCommand)
2231  {
2232  i = SendMessageW(hwndAvail, LB_INSERTSTRING, i, 0);
2233  SendMessageW(hwndAvail, LB_SETITEMDATA, i, (LPARAM)btnInfoNew);
2234  return;
2235  }
2236  }
2237  /* id higher than all others add to end */
2238  i = SendMessageW(hwndAvail, LB_ADDSTRING, 0, 0);
2239  SendMessageW(hwndAvail, LB_SETITEMDATA, i, (LPARAM)btnInfoNew);
2240 }
2241 
2242 static void TOOLBAR_Cust_MoveButton(const CUSTDLG_INFO *custInfo, HWND hwnd, INT nIndexFrom, INT nIndexTo)
2243 {
2244  NMTOOLBARW nmtb;
2245 
2246  TRACE("index from %d, index to %d\n", nIndexFrom, nIndexTo);
2247 
2248  if (nIndexFrom == nIndexTo)
2249  return;
2250 
2251  /* MSDN states that iItem is the index of the button, rather than the
2252  * command ID as used by every other NMTOOLBAR notification */
2253  nmtb.iItem = nIndexFrom;
2254  if (TOOLBAR_SendNotify(&nmtb.hdr, custInfo->tbInfo, TBN_QUERYINSERT))
2255  {
2256  PCUSTOMBUTTON btnInfo;
2257  NMHDR hdr;
2258  HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);
2259  int count = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2260 
2261  btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndList, LB_GETITEMDATA, nIndexFrom, 0);
2262 
2263  SendMessageW(hwndList, LB_DELETESTRING, nIndexFrom, 0);
2264  SendMessageW(hwndList, LB_INSERTSTRING, nIndexTo, 0);
2265  SendMessageW(hwndList, LB_SETITEMDATA, nIndexTo, (LPARAM)btnInfo);
2266  SendMessageW(hwndList, LB_SETCURSEL, nIndexTo, 0);
2267 
2268  if (nIndexTo <= 0)
2270  else
2272 
2273  /* last item is always separator, so -2 instead of -1 */
2274  if (nIndexTo >= (count - 2))
2276  else
2278 
2279  SendMessageW(custInfo->tbHwnd, TB_DELETEBUTTON, nIndexFrom, 0);
2280  SendMessageW(custInfo->tbHwnd, TB_INSERTBUTTONW, nIndexTo, (LPARAM)&(btnInfo->btn));
2281 
2283  }
2284 }
2285 
2286 static void TOOLBAR_Cust_AddButton(const CUSTDLG_INFO *custInfo, HWND hwnd, INT nIndexAvail, INT nIndexTo)
2287 {
2288  NMTOOLBARW nmtb;
2289 
2290  TRACE("Add: nIndexAvail %d, nIndexTo %d\n", nIndexAvail, nIndexTo);
2291 
2292  /* MSDN states that iItem is the index of the button, rather than the
2293  * command ID as used by every other NMTOOLBAR notification */
2294  nmtb.iItem = nIndexAvail;
2295  if (TOOLBAR_SendNotify(&nmtb.hdr, custInfo->tbInfo, TBN_QUERYINSERT))
2296  {
2297  PCUSTOMBUTTON btnInfo;
2298  NMHDR hdr;
2299  HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);
2300  HWND hwndAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
2301  int count = SendMessageW(hwndAvail, LB_GETCOUNT, 0, 0);
2302 
2303  btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndAvail, LB_GETITEMDATA, nIndexAvail, 0);
2304 
2305  if (nIndexAvail != 0) /* index == 0 indicates separator */
2306  {
2307  /* remove from 'available buttons' list */
2308  SendMessageW(hwndAvail, LB_DELETESTRING, nIndexAvail, 0);
2309  if (nIndexAvail == count-1)
2310  SendMessageW(hwndAvail, LB_SETCURSEL, nIndexAvail-1 , 0);
2311  else
2312  SendMessageW(hwndAvail, LB_SETCURSEL, nIndexAvail , 0);
2313  }
2314  else
2315  {
2316  PCUSTOMBUTTON btnNew;
2317 
2318  /* duplicate 'separator' button */
2319  btnNew = Alloc(sizeof(CUSTOMBUTTON));
2320  *btnNew = *btnInfo;
2321  btnInfo = btnNew;
2322  }
2323 
2324  /* insert into 'toolbar button' list */
2325  SendMessageW(hwndList, LB_INSERTSTRING, nIndexTo, 0);
2326  SendMessageW(hwndList, LB_SETITEMDATA, nIndexTo, (LPARAM)btnInfo);
2327 
2328  SendMessageW(custInfo->tbHwnd, TB_INSERTBUTTONW, nIndexTo, (LPARAM)&(btnInfo->btn));
2329 
2331  }
2332 }
2333 
2335 {
2336  PCUSTOMBUTTON btnInfo;
2337  HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);
2338 
2339  TRACE("Remove: index %d\n", index);
2340 
2341  btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndList, LB_GETITEMDATA, index, 0);
2342 
2343  /* send TBN_QUERYDELETE notification */
2344  if (TOOLBAR_IsButtonRemovable(custInfo->tbInfo, index, btnInfo))
2345  {
2346  NMHDR hdr;
2347 
2348  SendMessageW(hwndList, LB_DELETESTRING, index, 0);
2349  SendMessageW(hwndList, LB_SETCURSEL, index , 0);
2350 
2351  SendMessageW(custInfo->tbHwnd, TB_DELETEBUTTON, index, 0);
2352 
2353  /* insert into 'available button' list */
2354  if (!(btnInfo->btn.fsStyle & BTNS_SEP))
2356  else
2357  Free(btnInfo);
2358 
2360  }
2361 }
2362 
2363 /* drag list notification function for toolbar buttons list box */
2365  const DRAGLISTINFO *pDLI)
2366 {
2367  HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);
2368  switch (pDLI->uNotification)
2369  {
2370  case DL_BEGINDRAG:
2371  {
2372  INT nCurrentItem = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2373  INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2374  /* no dragging for last item (separator) */
2375  if (nCurrentItem >= (nCount - 1)) return FALSE;
2376  return TRUE;
2377  }
2378  case DL_DRAGGING:
2379  {
2380  INT nCurrentItem = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2381  INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2382  /* no dragging past last item (separator) */
2383  if ((nCurrentItem >= 0) && (nCurrentItem < (nCount - 1)))
2384  {
2385  DrawInsert(hwnd, hwndList, nCurrentItem);
2386  /* FIXME: native uses "move button" cursor */
2387  return DL_COPYCURSOR;
2388  }
2389 
2390  /* not over toolbar buttons list */
2391  if (nCurrentItem < 0)
2392  {
2393  POINT ptWindow = pDLI->ptCursor;
2394  HWND hwndListAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
2395  MapWindowPoints(NULL, hwnd, &ptWindow, 1);
2396  /* over available buttons list? */
2397  if (ChildWindowFromPoint(hwnd, ptWindow) == hwndListAvail)
2398  /* FIXME: native uses "move button" cursor */
2399  return DL_COPYCURSOR;
2400  }
2401  /* clear drag arrow */
2402  DrawInsert(hwnd, hwndList, -1);
2403  return DL_STOPCURSOR;
2404  }
2405  case DL_DROPPED:
2406  {
2407  INT nIndexTo = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2408  INT nIndexFrom = SendMessageW(hwndList, LB_GETCURSEL, 0, 0);
2409  INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2410  if ((nIndexTo >= 0) && (nIndexTo < (nCount - 1)))
2411  {
2412  /* clear drag arrow */
2413  DrawInsert(hwnd, hwndList, -1);
2414  /* move item */
2415  TOOLBAR_Cust_MoveButton(custInfo, hwnd, nIndexFrom, nIndexTo);
2416  }
2417  /* not over toolbar buttons list */
2418  if (nIndexTo < 0)
2419  {
2420  POINT ptWindow = pDLI->ptCursor;
2421  HWND hwndListAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
2422  MapWindowPoints(NULL, hwnd, &ptWindow, 1);
2423  /* over available buttons list? */
2424  if (ChildWindowFromPoint(hwnd, ptWindow) == hwndListAvail)
2425  TOOLBAR_Cust_RemoveButton(custInfo, hwnd, nIndexFrom);
2426  }
2427  break;
2428  }
2429  case DL_CANCELDRAG:
2430  /* Clear drag arrow */
2431  DrawInsert(hwnd, hwndList, -1);
2432  break;
2433  }
2434 
2435  return 0;
2436 }
2437 
2438 /* drag list notification function for available buttons list box */
2440  const DRAGLISTINFO *pDLI)
2441 {
2442  HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);
2443  switch (pDLI->uNotification)
2444  {
2445  case DL_BEGINDRAG:
2446  return TRUE;
2447  case DL_DRAGGING:
2448  {
2449  INT nCurrentItem = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2450  INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2451  /* no dragging past last item (separator) */
2452  if ((nCurrentItem >= 0) && (nCurrentItem < nCount))
2453  {
2454  DrawInsert(hwnd, hwndList, nCurrentItem);
2455  /* FIXME: native uses "move button" cursor */
2456  return DL_COPYCURSOR;
2457  }
2458 
2459  /* not over toolbar buttons list */
2460  if (nCurrentItem < 0)
2461  {
2462  POINT ptWindow = pDLI->ptCursor;
2463  HWND hwndListAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
2464  MapWindowPoints(NULL, hwnd, &ptWindow, 1);
2465  /* over available buttons list? */
2466  if (ChildWindowFromPoint(hwnd, ptWindow) == hwndListAvail)
2467  /* FIXME: native uses "move button" cursor */
2468  return DL_COPYCURSOR;
2469  }
2470  /* clear drag arrow */
2471  DrawInsert(hwnd, hwndList, -1);
2472  return DL_STOPCURSOR;
2473  }
2474  case DL_DROPPED:
2475  {
2476  INT nIndexTo = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2477  INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2479  if ((nIndexTo >= 0) && (nIndexTo < nCount))
2480  {
2481  /* clear drag arrow */
2482  DrawInsert(hwnd, hwndList, -1);
2483  /* add item */
2484  TOOLBAR_Cust_AddButton(custInfo, hwnd, nIndexFrom, nIndexTo);
2485  }
2486  }
2487  case DL_CANCELDRAG:
2488  /* Clear drag arrow */
2489  DrawInsert(hwnd, hwndList, -1);
2490  break;
2491  }
2492  return 0;
2493 }
2494 
2496 
2497 /***********************************************************************
2498  * TOOLBAR_CustomizeDialogProc
2499  * This function implements the toolbar customization dialog.
2500  */
2501 static INT_PTR CALLBACK
2503 {
2505  PCUSTOMBUTTON btnInfo;
2506  NMTOOLBARA nmtb;
2507  TOOLBAR_INFO *infoPtr = custInfo ? custInfo->tbInfo : NULL;
2508 
2509  switch (uMsg)
2510  {
2511  case WM_INITDIALOG:
2512  custInfo = (PCUSTDLG_INFO)lParam;
2513  SetWindowLongPtrW (hwnd, DWLP_USER, (LONG_PTR)custInfo);
2514 
2515  if (custInfo)
2516  {
2517  WCHAR Buffer[256];
2518  int i = 0;
2519  int index;
2520  NMTBINITCUSTOMIZE nmtbic;
2521 
2522  infoPtr = custInfo->tbInfo;
2523 
2524  /* send TBN_QUERYINSERT notification */
2525  nmtb.iItem = custInfo->tbInfo->nNumButtons;
2526 
2527  if (!TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_QUERYINSERT))
2528  return FALSE;
2529 
2530  nmtbic.hwndDialog = hwnd;
2531  /* Send TBN_INITCUSTOMIZE notification */
2532  if (TOOLBAR_SendNotify (&nmtbic.hdr, infoPtr, TBN_INITCUSTOMIZE) ==
2534  {
2535  TRACE("TBNRF_HIDEHELP requested\n");
2537  }
2538 
2539  /* add items to 'toolbar buttons' list and check if removable */
2540  for (i = 0; i < custInfo->tbInfo->nNumButtons; i++)
2541  {
2542  btnInfo = Alloc(sizeof(CUSTOMBUTTON));
2543  memset (&btnInfo->btn, 0, sizeof(TBBUTTON));
2544  btnInfo->btn.fsStyle = BTNS_SEP;
2545  btnInfo->bVirtual = FALSE;
2547 
2548  /* send TBN_QUERYDELETE notification */
2549  btnInfo->bRemovable = TOOLBAR_IsButtonRemovable(infoPtr, i, btnInfo);
2550 
2553  }
2554 
2556 
2557  /* insert separator button into 'available buttons' list */
2558  btnInfo = Alloc(sizeof(CUSTOMBUTTON));
2559  memset (&btnInfo->btn, 0, sizeof(TBBUTTON));
2560  btnInfo->btn.fsStyle = BTNS_SEP;
2561  btnInfo->bVirtual = FALSE;
2562  btnInfo->bRemovable = TRUE;
2566 
2567  /* insert all buttons into dsa */
2568  for (i = 0;; i++)
2569  {
2570  /* send TBN_GETBUTTONINFO notification */
2571  NMTOOLBARW nmtb;
2572  nmtb.iItem = i;
2573  nmtb.pszText = Buffer;
2574  nmtb.cchText = 256;
2575 
2576  /* Clear previous button's text */
2577  ZeroMemory(nmtb.pszText, nmtb.cchText * sizeof(WCHAR));
2578 
2579  if (!TOOLBAR_GetButtonInfo(infoPtr, &nmtb))
2580  break;
2581 
2582  TRACE("WM_INITDIALOG style: %x iItem(%d) idCommand(%d) iString(%ld) %s\n",
2583  nmtb.tbButton.fsStyle, i,
2584  nmtb.tbButton.idCommand,
2585  nmtb.tbButton.iString,
2586  nmtb.tbButton.iString >= 0 ? debugstr_w(infoPtr->strings[nmtb.tbButton.iString])
2587  : "");
2588 
2589  /* insert button into the appropriate list */
2590  index = TOOLBAR_GetButtonIndex (custInfo->tbInfo, nmtb.tbButton.idCommand, FALSE);
2591  if (index == -1)
2592  {
2593  btnInfo = Alloc(sizeof(CUSTOMBUTTON));
2594  btnInfo->bVirtual = FALSE;
2595  btnInfo->bRemovable = TRUE;
2596  }
2597  else
2598  {
2599  btnInfo = (PCUSTOMBUTTON)SendDlgItemMessageW (hwnd,
2601  }
2602 
2603  btnInfo->btn = nmtb.tbButton;
2604  if (!(nmtb.tbButton.fsStyle & BTNS_SEP))
2605  {
2606  if (lstrlenW(nmtb.pszText))
2607  lstrcpyW(btnInfo->text, nmtb.pszText);
2608  else if (nmtb.tbButton.iString >= 0 &&
2609  nmtb.tbButton.iString < infoPtr->nNumStrings)
2610  {
2611  lstrcpyW(btnInfo->text,
2612  infoPtr->strings[nmtb.tbButton.iString]);
2613  }
2614  }
2615 
2616  if (index == -1)
2618  }
2619 
2621 
2622  /* select first item in the 'available' list */
2624 
2625  /* append 'virtual' separator button to the 'toolbar buttons' list */
2626  btnInfo = Alloc(sizeof(CUSTOMBUTTON));
2627  memset (&btnInfo->btn, 0, sizeof(TBBUTTON));
2628  btnInfo->btn.fsStyle = BTNS_SEP;
2629  btnInfo->bVirtual = TRUE;
2630  btnInfo->bRemovable = FALSE;
2631  LoadStringW (COMCTL32_hModule, IDS_SEPARATOR, btnInfo->text, 64);
2634 
2635  /* select last item in the 'toolbar' list */
2638 
2641 
2642  /* set focus and disable buttons */
2643  PostMessageW (hwnd, WM_USER, 0, 0);
2644  }
2645  return TRUE;
2646 
2647  case WM_USER:
2652  return TRUE;
2653 
2654  case WM_CLOSE:
2655  EndDialog(hwnd, FALSE);
2656  return TRUE;
2657 
2658  case WM_COMMAND:
2659  switch (LOWORD(wParam))
2660  {
2661  case IDC_TOOLBARBTN_LBOX:
2662  if (HIWORD(wParam) == LBN_SELCHANGE)
2663  {
2664  PCUSTOMBUTTON btnInfo;
2665  NMTOOLBARA nmtb;
2666  int count;
2667  int index;
2668 
2671 
2672  /* send TBN_QUERYINSERT notification */
2673  nmtb.iItem = index;
2674  TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_QUERYINSERT);
2675 
2676  /* get list box item */
2678 
2679  if (index == (count - 1))
2680  {
2681  /* last item (virtual separator) */
2684  }
2685  else if (index == (count - 2))
2686  {
2687  /* second last item (last non-virtual item) */
2690  }
2691  else if (index == 0)
2692  {
2693  /* first item */
2696  }
2697  else
2698  {
2701  }
2702 
2704  }
2705  break;
2706 
2707  case IDC_MOVEUP_BTN:
2708  {
2710  TOOLBAR_Cust_MoveButton(custInfo, hwnd, index, index-1);
2711  }
2712  break;
2713 
2714  case IDC_MOVEDN_BTN: /* move down */
2715  {
2717  TOOLBAR_Cust_MoveButton(custInfo, hwnd, index, index+1);
2718  }
2719  break;
2720 
2721  case IDC_REMOVE_BTN: /* remove button */
2722  {
2724 
2725  if (LB_ERR == index)
2726  break;
2727 
2728  TOOLBAR_Cust_RemoveButton(custInfo, hwnd, index);
2729  }
2730  break;
2731  case IDC_HELP_BTN:
2732  TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_CUSTHELP);
2733  break;
2734  case IDC_RESET_BTN:
2735  TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_RESET);
2736  break;
2737 
2738  case IDOK: /* Add button */
2739  {
2740  int index;
2741  int indexto;
2742 
2745 
2746  TOOLBAR_Cust_AddButton(custInfo, hwnd, index, indexto);
2747  }
2748  break;
2749 
2750  case IDCANCEL:
2751  EndDialog(hwnd, FALSE);
2752  break;
2753  }
2754  return TRUE;
2755 
2756  case WM_DESTROY:
2757  {
2758  int count;
2759  int i;
2760 
2761  /* delete items from 'toolbar buttons' listbox*/
2763  for (i = 0; i < count; i++)
2764  {
2766  Free(btnInfo);
2768  }
2770 
2771 
2772  /* delete items from 'available buttons' listbox*/
2774  for (i = 0; i < count; i++)
2775  {
2777  Free(btnInfo);
2779  }
2781  }
2782  return TRUE;
2783 
2784  case WM_DRAWITEM:
2786  {
2788  RECT rcButton;
2789  RECT rcText;
2790  HPEN hPen, hOldPen;
2791  HBRUSH hOldBrush;
2792  COLORREF oldText = 0;
2793  COLORREF oldBk = 0;
2794 
2795  /* get item data */
2797  if (btnInfo == NULL)
2798  {
2799  FIXME("btnInfo invalid\n");
2800  return TRUE;
2801  }
2802 
2803  /* set colors and select objects */
2805  if (btnInfo->bVirtual)
2806  oldText = SetTextColor (lpdis->hDC, comctl32_color.clrGrayText);
2807  else
2809  hPen = CreatePen( PS_SOLID, 1,
2811  hOldPen = SelectObject (lpdis->hDC, hPen );
2813 
2814  /* fill background rectangle */
2815  Rectangle (lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top,
2816  lpdis->rcItem.right, lpdis->rcItem.bottom);
2817 
2818  /* calculate button and text rectangles */
2819  rcButton = lpdis->rcItem;
2820  InflateRect (&rcButton, -1, -1);
2821  rcText = rcButton;
2822  rcButton.right = rcButton.left + custInfo->tbInfo->nBitmapWidth + 6;
2823  rcText.left = rcButton.right + 2;
2824 
2825  /* draw focus rectangle */
2826  if (lpdis->itemState & ODS_FOCUS)
2827  DrawFocusRect (lpdis->hDC, &lpdis->rcItem);
2828 
2829  /* draw button */
2830  if (!(infoPtr->dwStyle & TBSTYLE_FLAT))
2831  DrawEdge (lpdis->hDC, &rcButton, EDGE_RAISED, BF_RECT|BF_MIDDLE|BF_SOFT);
2832 
2833  /* draw image and text */
2834  if ((btnInfo->btn.fsStyle & BTNS_SEP) == 0) {
2835  HIMAGELIST himl = GETDEFIMAGELIST(infoPtr, GETHIMLID(infoPtr,
2836  btnInfo->btn.iBitmap));
2837  ImageList_Draw (himl, GETIBITMAP(infoPtr, btnInfo->btn.iBitmap),
2838  lpdis->hDC, rcButton.left+3, rcButton.top+3, ILD_NORMAL);
2839  }
2840  DrawTextW (lpdis->hDC, btnInfo->text, -1, &rcText,
2842 
2843  /* delete objects and reset colors */
2844  SelectObject (lpdis->hDC, hOldBrush);
2845  SelectObject (lpdis->hDC, hOldPen);
2846  SetBkColor (lpdis->hDC, oldBk);
2847  SetTextColor (lpdis->hDC, oldText);
2848  DeleteObject( hPen );
2849  return TRUE;
2850  }
2851  return FALSE;
2852 
2853  case WM_MEASUREITEM:
2855  {
2857 
2858  lpmis->itemHeight = 15 + 8; /* default height */
2859 
2860  return TRUE;
2861  }
2862  return FALSE;
2863 
2864  default:
2865  if (uDragListMessage && (uMsg == uDragListMessage))
2866  {
2867  if (wParam == IDC_TOOLBARBTN_LBOX)
2868  {
2870  custInfo, hwnd, (DRAGLISTINFO *)lParam);
2872  return TRUE;
2873  }
2874  else if (wParam == IDC_AVAILBTN_LBOX)
2875  {
2877  custInfo, hwnd, (DRAGLISTINFO *)lParam);
2879  return TRUE;
2880  }
2881  }
2882  return FALSE;
2883  }
2884 }
2885 
2886 static BOOL
2888 {
2889  HBITMAP hbmLoad;
2890  INT nCountBefore = ImageList_GetImageCount(himlDef);
2891  INT nCountAfter;
2892  INT cxIcon, cyIcon;
2893  INT nAdded;
2894  INT nIndex;
2895 
2896  TRACE("adding hInst=%p nID=%d nButtons=%d\n", bitmap->hInst, bitmap->nID, bitmap->nButtons);
2897  /* Add bitmaps to the default image list */
2898  if (bitmap->hInst == NULL) /* a handle was passed */
2899  hbmLoad = CopyImage(ULongToHandle(bitmap->nID), IMAGE_BITMAP, 0, 0, 0);
2900  else if (bitmap->hInst == COMCTL32_hModule)
2901  hbmLoad = LoadImageW( bitmap->hInst, MAKEINTRESOURCEW(bitmap->nID),
2903  else
2904  hbmLoad = CreateMappedBitmap(bitmap->hInst, bitmap->nID, 0, NULL, 0);
2905 
2906  /* enlarge the bitmap if needed */
2907  ImageList_GetIconSize(himlDef, &cxIcon, &cyIcon);
2908  if (bitmap->hInst != COMCTL32_hModule)
2909  COMCTL32_EnsureBitmapSize(&hbmLoad, cxIcon*(INT)bitmap->nButtons, cyIcon, comctl32_color.clrBtnFace);
2910 
2911  nIndex = ImageList_AddMasked(himlDef, hbmLoad, comctl32_color.clrBtnFace);
2912  DeleteObject(hbmLoad);
2913  if (nIndex == -1)
2914  return FALSE;
2915 
2916  nCountAfter = ImageList_GetImageCount(himlDef);
2917  nAdded = nCountAfter - nCountBefore;
2918  if (bitmap->nButtons == 0) /* wParam == 0 is special and means add only one image */
2919  {
2920  ImageList_SetImageCount(himlDef, nCountBefore + 1);
2921  } else if (nAdded > (INT)bitmap->nButtons) {
2922  TRACE("Added more images than wParam: Previous image number %i added %i while wParam %i. Images in list %i\n",
2923  nCountBefore, nAdded, bitmap->nButtons, nCountAfter);
2924  }
2925 
2926  infoPtr->nNumBitmaps += nAdded;
2927  return TRUE;
2928 }
2929 
2930 static void
2932 {
2933  HIMAGELIST himlDef;
2934  HIMAGELIST himlNew;
2935  INT cx, cy;
2936  INT i;
2937 
2938  himlDef = GETDEFIMAGELIST(infoPtr, 0);
2939  if (himlDef == NULL || himlDef != infoPtr->himlInt)
2940  return;
2941  if (!ImageList_GetIconSize(himlDef, &cx, &cy))
2942  return;
2943  if (cx == infoPtr->nBitmapWidth && cy == infoPtr->nBitmapHeight)
2944  return;
2945 
2946  TRACE("Update icon size: %dx%d -> %dx%d\n",
2947  cx, cy, infoPtr->nBitmapWidth, infoPtr->nBitmapHeight);
2948 
2949  himlNew = ImageList_Create(infoPtr->nBitmapWidth, infoPtr->nBitmapHeight,
2950  ILC_COLOR32|ILC_MASK, 8, 2);
2951  for (i = 0; i < infoPtr->nNumBitmapInfos; i++)
2952  TOOLBAR_AddBitmapToImageList(infoPtr, himlNew, &infoPtr->bitmaps[i]);
2953  TOOLBAR_InsertImageList(&infoPtr->himlDef, &infoPtr->cimlDef, himlNew, 0);
2954  infoPtr->himlInt = himlNew;
2955 
2956  infoPtr->nNumBitmaps -= ImageList_GetImageCount(himlDef);
2957  ImageList_Destroy(himlDef);
2958 }
2959 
2960 /***********************************************************************
2961  * TOOLBAR_AddBitmap: Add the bitmaps to the default image list.
2962  *
2963  */
2964 static LRESULT
2966 {
2968  INT iSumButtons, i;
2969  HIMAGELIST himlDef;
2970 
2971  TRACE("hwnd=%p count=%d lpAddBmp=%p\n", infoPtr->hwndSelf, count, lpAddBmp);
2972  if (!lpAddBmp)
2973  return -1;
2974 
2975  if (lpAddBmp->hInst == HINST_COMMCTRL)
2976  {
2977  info.hInst = COMCTL32_hModule;
2978  switch (lpAddBmp->nID)
2979  {
2980  case IDB_STD_SMALL_COLOR:
2981  case 2:
2982  info.nButtons = 15;
2983  info.nID = IDB_STD_SMALL;
2984  break;
2985  case IDB_STD_LARGE_COLOR:
2986  case 3:
2987  info.nButtons = 15;
2988  info.nID = IDB_STD_LARGE;
2989  break;
2990  case IDB_VIEW_SMALL_COLOR:
2991  case 6:
2992  info.nButtons = 12;
2993  info.nID = IDB_VIEW_SMALL;
2994  break;
2995  case IDB_VIEW_LARGE_COLOR:
2996  case 7:
2997  info.nButtons = 12;
2998  info.nID = IDB_VIEW_LARGE;
2999  break;
3000  case IDB_HIST_SMALL_COLOR:
3001  info.nButtons = 5;
3002  info.nID = IDB_HIST_SMALL;
3003  break;
3004  case IDB_HIST_LARGE_COLOR:
3005  info.nButtons = 5;
3006  info.nID = IDB_HIST_LARGE;
3007  break;
3008  default:
3009  WARN("unknown bitmap id, %ld\n", lpAddBmp->nID);
3010  return -1;
3011  }
3012 
3013  TRACE ("adding %d internal bitmaps\n", info.nButtons);
3014 
3015  /* Windows resize all the buttons to the size of a newly added standard image */
3016  if (lpAddBmp->nID & 1)
3017  {
3018  /* large icons: 24x24. Will make the button 31x30 */
3019  SendMessageW (infoPtr->hwndSelf, TB_SETBITMAPSIZE, 0, MAKELPARAM(24, 24));
3020  }
3021  else
3022  {
3023  /* small icons: 16x16. Will make the buttons 23x22 */
3024  SendMessageW (infoPtr->hwndSelf, TB_SETBITMAPSIZE, 0, MAKELPARAM(16, 16));
3025  }
3026 
3027  TOOLBAR_CalcToolbar (infoPtr);
3028  }
3029  else
3030  {
3031  info.nButtons = count;
3032  info.hInst = lpAddBmp->hInst;
3033  info.nID = lpAddBmp->nID;
3034  TRACE("adding %d bitmaps\n", info.nButtons);
3035  }
3036 
3037  /* check if the bitmap is already loaded and compute iSumButtons */
3038  iSumButtons = 0;
3039  for (i = 0; i < infoPtr->nNumBitmapInfos; i++)
3040  {
3041  if (infoPtr->bitmaps[i].hInst == info.hInst &&
3042  infoPtr->bitmaps[i].nID == info.nID)
3043  return iSumButtons;
3044  iSumButtons += infoPtr->bitmaps[i].nButtons;
3045  }
3046 
3047  if (!infoPtr->cimlDef) {
3048  /* create new default image list */
3049  TRACE ("creating default image list\n");
3050 
3051  himlDef = ImageList_Create (infoPtr->nBitmapWidth, infoPtr->nBitmapHeight,
3052  ILC_COLOR32 | ILC_MASK, info.nButtons, 2);
3053  TOOLBAR_InsertImageList(&infoPtr->himlDef, &infoPtr->cimlDef, himlDef, 0);
3054  infoPtr->himlInt = himlDef;
3055  }
3056  else {
3057  himlDef = GETDEFIMAGELIST(infoPtr, 0);
3058  }
3059 
3060  if (!himlDef) {
3061  WARN("No default image list available\n");
3062  return -1;
3063  }
3064 
3065  if (!TOOLBAR_AddBitmapToImageList(infoPtr, himlDef, &info))
3066  return -1;
3067 
3068  TRACE("Number of bitmap infos: %d\n", infoPtr->nNumBitmapInfos);
3069  infoPtr->bitmaps = ReAlloc(infoPtr->bitmaps, (infoPtr->nNumBitmapInfos + 1) * sizeof(TBITMAP_INFO));
3070  infoPtr->bitmaps[infoPtr->nNumBitmapInfos] = info;
3071  infoPtr->nNumBitmapInfos++;
3072  TRACE("Number of bitmap infos: %d\n", infoPtr->nNumBitmapInfos);
3073 
3074  InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
3075  return iSumButtons;
3076 }
3077 
3078 
3079 static LRESULT
3080 TOOLBAR_AddButtonsT(TOOLBAR_INFO *infoPtr, INT nAddButtons, const TBBUTTON* lpTbb, BOOL fUnicode)
3081 {
3082  TRACE("adding %d buttons (unicode=%d)\n", nAddButtons, fUnicode);
3083 
3084  return TOOLBAR_InternalInsertButtonsT(infoPtr, -1, nAddButtons, lpTbb, fUnicode);
3085 }
3086 
3087 
3088 static LRESULT
3090 {
3091 #define MAX_RESOURCE_STRING_LENGTH 512
3092  BOOL fFirstString = (infoPtr->nNumStrings == 0);
3093  INT nIndex = infoPtr->nNumStrings;
3094 
3095  TRACE("%p, %lx\n", hInstance, lParam);
3096 
3097  if (IS_INTRESOURCE(lParam)) {
3099  WCHAR delimiter;
3100  WCHAR *next_delim;
3101  HRSRC hrsrc;
3102  WCHAR *p;
3103  INT len;
3104 
3105  TRACE("adding string from resource\n");
3106 
3107  if (!hInstance) return -1;
3108 
3109  hrsrc = FindResourceW( hInstance, MAKEINTRESOURCEW((LOWORD(lParam) >> 4) + 1),
3110  (LPWSTR)RT_STRING );
3111  if (!hrsrc)
3112  {
3113  TRACE("string not found in resources\n");
3114  return -1;
3115  }
3116 
3118  szString, MAX_RESOURCE_STRING_LENGTH);
3119 
3120  TRACE("len=%d %s\n", len, debugstr_w(szString));
3121  if (len == 0 || len == 1)
3122  return nIndex;
3123 
3124  TRACE("delimiter: 0x%x\n", *szString);
3125  delimiter = *szString;
3126  p = szString + 1;
3127 
3128  while ((next_delim = strchrW(p, delimiter)) != NULL) {
3129  *next_delim = 0;
3130  if (next_delim + 1 >= szString + len)
3131  {
3132  /* this may happen if delimiter == '\0' or if the last char is a
3133  * delimiter (then it is ignored like the native does) */
3134  break;
3135  }
3136 
3137  infoPtr->strings = ReAlloc(infoPtr->strings, sizeof(LPWSTR)*(infoPtr->nNumStrings+1));
3138  Str_SetPtrW(&infoPtr->strings[infoPtr->nNumStrings], p);
3139  infoPtr->nNumStrings++;
3140 
3141  p = next_delim + 1;
3142  }
3143  }
3144  else {
3145  LPWSTR p = (LPWSTR)lParam;
3146  INT len;
3147 
3148  if (p == NULL)
3149  return -1;
3150  TRACE("adding string(s) from array\n");
3151  while (*p) {
3152  len = strlenW (p);
3153 
3154  TRACE("len=%d %s\n", len, debugstr_w(p));
3155  infoPtr->strings = ReAlloc(infoPtr->strings, sizeof(LPWSTR)*(infoPtr->nNumStrings+1));
3156  Str_SetPtrW (&infoPtr->strings[infoPtr->nNumStrings], p);
3157  infoPtr->nNumStrings++;
3158 
3159  p += (len+1);
3160  }
3161  }
3162 
3163  if (fFirstString)
3164  TOOLBAR_CalcToolbar(infoPtr);
3165  return nIndex;
3166 }
3167 
3168 
3169 static LRESULT
3171 {
3172  BOOL fFirstString = (infoPtr->nNumStrings == 0);
3173  LPSTR p;
3174  INT nIndex;
3175  INT len;
3176 
3177  TRACE("%p, %lx\n", hInstance, lParam);
3178 
3179  if (IS_INTRESOURCE(lParam)) /* load from resources */
3180  return TOOLBAR_AddStringW(infoPtr, hInstance, lParam);
3181 
3182  p = (LPSTR)lParam;
3183  if (p == NULL)
3184  return -1;
3185 
3186  TRACE("adding string(s) from array\n");
3187  nIndex = infoPtr->nNumStrings;
3188  while (*p) {
3189  len = strlen (p);
3190  TRACE("len=%d \"%s\"\n", len, p);
3191 
3192  infoPtr->strings = ReAlloc(infoPtr->strings, sizeof(LPWSTR)*(infoPtr->nNumStrings+1));
3193  Str_SetPtrAtoW(&infoPtr->strings[infoPtr->nNumStrings], p);
3194  infoPtr->nNumStrings++;
3195 
3196  p += (len+1);
3197  }
3198 
3199  if (fFirstString)
3200  TOOLBAR_CalcToolbar(infoPtr);
3201  return nIndex;
3202 }
3203 
3204 
3205 static LRESULT
3207 {
3208  TRACE("auto sizing, style=%#x\n", infoPtr->dwStyle);
3209  TRACE("nRows: %d, infoPtr->nButtonHeight: %d\n", infoPtr->nRows, infoPtr->nButtonHeight);
3210 
3211 #ifdef __REACTOS__ /* workaround CORE-16169 part 2 of 2 */
3212  if ((infoPtr->dwStyle & TBSTYLE_WRAPABLE) || (infoPtr->dwExStyle & TBSTYLE_EX_VERTICAL))
3213  {
3214  TOOLBAR_LayoutToolbar(infoPtr);
3215  InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
3216  }
3217 #endif
3218 
3219  if (!(infoPtr->dwStyle & CCS_NORESIZE))
3220  {
3221  RECT window_rect, parent_rect;
3222  UINT uPosFlags = SWP_NOZORDER | SWP_NOACTIVATE;
3223  HWND parent;
3224  INT x, y, cx, cy;
3225 
3226  parent = GetParent (infoPtr->hwndSelf);
3227 
3228  if (!parent || !infoPtr->bDoRedraw)
3229  return 0;
3230 
3231  GetClientRect(parent, &parent_rect);
3232 
3233  x = parent_rect.left;
3234  y = parent_rect.top;
3235 
3236  cy = TOP_BORDER + infoPtr->nRows * infoPtr->nButtonHeight + BOTTOM_BORDER;
3237  cx = parent_rect.right - parent_rect.left;
3238 
3239  if ((infoPtr->dwStyle & CCS_BOTTOM) == CCS_NOMOVEY)
3240  {
3241  GetWindowRect(infoPtr->hwndSelf, &window_rect);
3242  MapWindowPoints( 0, parent, (POINT *)&window_rect, 2 );
3243  y = window_rect.top;
3244  }
3245  if ((infoPtr->dwStyle & CCS_BOTTOM) == CCS_BOTTOM)
3246  {
3247  GetWindowRect(infoPtr->hwndSelf, &window_rect);
3248  y = parent_rect.bottom - ( window_rect.bottom - window_rect.top);
3249  }
3250 
3251  if (infoPtr->dwStyle & CCS_NOPARENTALIGN)
3252  uPosFlags |= SWP_NOMOVE;
3253 
3254  if (!(infoPtr->dwStyle & CCS_NODIVIDER))
3256 
3257  if (infoPtr->dwStyle & WS_BORDER)
3258  {
3261  }
3262 
3263  SetWindowPos(infoPtr->hwndSelf, NULL, x, y, cx, cy, uPosFlags);
3264  }
3265 
3266  if ((infoPtr->dwStyle & TBSTYLE_WRAPABLE) || (infoPtr->dwExStyle & TBSTYLE_EX_VERTICAL))
3267  {
3268  TOOLBAR_LayoutToolbar(infoPtr);
3269  InvalidateRect( infoPtr->hwndSelf, NULL, TRUE );
3270  }
3271 
3272  return 0;
3273 }
3274 
3275 
3276 static inline LRESULT
3278 {
3279  return infoPtr->nNumButtons;
3280 }
3281 
3282 
3283 static inline LRESULT
3285 {
3286  infoPtr->dwStructSize = Size;
3287 
3288  return 0;
3289 }
3290 
3291 
3292 static LRESULT
3294 {
3295  TBUTTON_INFO *btnPtr;
3296  INT nIndex;
3297 
3298  TRACE("button %d, iBitmap now %d\n", Id, Index);
3299 
3300  nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3301  if (nIndex == -1)
3302  return FALSE;
3303 
3304  btnPtr = &infoPtr->buttons[nIndex];
3305  btnPtr->iBitmap = Index;
3306 
3307  /* we HAVE to erase the background, the new bitmap could be */
3308  /* transparent */
3309  InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
3310 
3311  return TRUE;
3312 }
3313 
3314 
3315 static LRESULT
3317 {
3318  TBUTTON_INFO *btnPtr;
3319  INT nIndex;
3320  INT nOldIndex = -1;
3321  BOOL bChecked = FALSE;
3322 
3323  nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3324 
3325  TRACE("hwnd=%p, btn index=%d, lParam=0x%08lx\n", infoPtr->hwndSelf, nIndex, lParam);
3326 
3327  if (nIndex == -1)
3328  return FALSE;
3329 
3330  btnPtr = &infoPtr->buttons[nIndex];
3331 
3332  bChecked = (btnPtr->fsState & TBSTATE_CHECKED) != 0;
3333 
3334  if (!LOWORD(lParam))
3335  btnPtr->fsState &= ~TBSTATE_CHECKED;
3336  else {
3337  if (btnPtr->fsStyle & BTNS_GROUP) {
3338  nOldIndex =
3339  TOOLBAR_GetCheckedGroupButtonIndex (infoPtr, nIndex);
3340  if (nOldIndex == nIndex)
3341  return 0;
3342  if (nOldIndex != -1)
3343  infoPtr->buttons[nOldIndex].fsState &= ~TBSTATE_CHECKED;
3344  }
3345  btnPtr->fsState |= TBSTATE_CHECKED;
3346  }
3347 
3348  if( bChecked != LOWORD(lParam) )
3349  {
3350  if (nOldIndex != -1)
3351  InvalidateRect(infoPtr->hwndSelf, &infoPtr->buttons[nOldIndex].rect, TRUE);
3352  InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
3353  }
3354 
3355  /* FIXME: Send a WM_NOTIFY?? */
3356 
3357  return TRUE;
3358 }
3359 
3360 
3361 static LRESULT
3363 {
3364  return TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3365 }
3366 
3367 
3368 static LRESULT
3370 {
3371  CUSTDLG_INFO custInfo;
3372  LRESULT ret;
3373  NMHDR nmhdr;
3374 
3375  custInfo.tbInfo = infoPtr;
3376  custInfo.tbHwnd = infoPtr->hwndSelf;
3377 
3378  /* send TBN_BEGINADJUST notification */
3379  TOOLBAR_SendNotify (&nmhdr, infoPtr, TBN_BEGINADJUST);
3380 
3382  infoPtr->hwndSelf, TOOLBAR_CustomizeDialogProc, (LPARAM)&custInfo);
3383 
3384  /* send TBN_ENDADJUST notification */
3385  TOOLBAR_SendNotify (&nmhdr, infoPtr, TBN_ENDADJUST);
3386 
3387  return ret;
3388 }
3389 
3390 
3391 static LRESULT
3393 {
3394  NMTOOLBARW nmtb;
3395  TBUTTON_INFO *btnPtr = &infoPtr->buttons[nIndex];
3396 
3397  if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
3398  return FALSE;
3399 
3400  memset(&nmtb, 0, sizeof(nmtb));
3401  nmtb.iItem = btnPtr->idCommand;
3402  nmtb.tbButton.iBitmap = btnPtr->iBitmap;
3403  nmtb.tbButton.idCommand = btnPtr->idCommand;
3404  nmtb.tbButton.fsState = btnPtr->fsState;
3405  nmtb.tbButton.fsStyle = btnPtr->fsStyle;
3406  nmtb.tbButton.dwData = btnPtr->dwData;
3407  nmtb.tbButton.iString = btnPtr->iString;
3408  TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_DELETINGBUTTON);
3409 
3410  TOOLBAR_TooltipDelTool(infoPtr, &infoPtr->buttons[nIndex]);
3411 
3412  infoPtr->nHotItem = -1;
3413  if (infoPtr->nNumButtons == 1) {
3414  TRACE(" simple delete\n");
3415  free_string( infoPtr->buttons );
3416  Free (infoPtr->buttons);
3417  infoPtr->buttons = NULL;
3418  infoPtr->nNumButtons = 0;
3419  }
3420  else {
3421  TBUTTON_INFO *oldButtons = infoPtr->buttons;
3422  TRACE("complex delete [nIndex=%d]\n", nIndex);
3423 
3424  infoPtr->nNumButtons--;
3425  infoPtr->buttons = Alloc (sizeof (TBUTTON_INFO) * infoPtr->nNumButtons);
3426  if (nIndex > 0) {
3427  memcpy (&infoPtr->buttons[0], &oldButtons[0],
3428  nIndex * sizeof(TBUTTON_INFO));
3429  }
3430 
3431  if (nIndex < infoPtr->nNumButtons) {
3432  memcpy (&infoPtr->buttons[nIndex], &oldButtons[nIndex+1],
3433  (infoPtr->nNumButtons - nIndex) * sizeof(TBUTTON_INFO));
3434  }
3435 
3436  free_string( oldButtons + nIndex );
3437  Free (oldButtons);
3438  }
3439 
3440  TOOLBAR_LayoutToolbar(infoPtr);
3441 
3442  InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
3443 
3444  return TRUE;
3445 }
3446 
3447 
3448 static LRESULT
3450 {
3451  TBUTTON_INFO *btnPtr;
3452  INT nIndex;
3453  DWORD bState;
3454 
3455  nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3456 
3457  TRACE("hwnd=%p, btn id=%d, lParam=0x%08lx\n", infoPtr->hwndSelf, Id, lParam);
3458 
3459  if (nIndex == -1)
3460  return FALSE;
3461 
3462  btnPtr = &infoPtr->buttons[nIndex];
3463 
3464  bState = btnPtr->fsState & TBSTATE_ENABLED;
3465 
3466  /* update the toolbar button state */
3467  if(!LOWORD(lParam)) {
3468  btnPtr->fsState &= ~(TBSTATE_ENABLED | TBSTATE_PRESSED);
3469  } else {
3470  btnPtr->fsState |= TBSTATE_ENABLED;
3471  }
3472 
3473  /* redraw the button only if the state of the button changed */
3474  if(bState != (btnPtr->fsState & TBSTATE_ENABLED))
3475  InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
3476 
3477  return TRUE;
3478 }
3479 
3480 
3481 static inline LRESULT
3483 {
3484  return infoPtr->bAnchor;
3485 }
3486 
3487 
3488 static LRESULT
3490 {
3491  INT nIndex;
3492 
3493  nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3494  if (nIndex == -1)
3495  return -1;
3496 
3497  return infoPtr->buttons[nIndex].iBitmap;
3498 }
3499 
3500 
3501 static inline LRESULT
3503 {
3504  return (GetDeviceCaps (0, LOGPIXELSX) >= 120) ? TBBF_LARGE : 0;
3505 }
3506 
3507 
3508 static LRESULT
3509 TOOLBAR_GetButton (const TOOLBAR_INFO *infoPtr, INT nIndex, TBBUTTON *lpTbb)
3510 {
3511  TBUTTON_INFO *btnPtr;
3512 
3513  if (lpTbb == NULL)
3514  return FALSE;
3515 
3516  if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
3517  return FALSE;
3518 
3519  btnPtr = &infoPtr->buttons[nIndex];
3520  lpTbb->iBitmap = btnPtr->iBitmap;
3521  lpTbb->idCommand = btnPtr->idCommand;
3522  lpTbb->fsState = btnPtr->fsState;
3523  lpTbb->fsStyle = btnPtr->fsStyle;
3524  lpTbb->bReserved[0] = 0;
3525  lpTbb->bReserved[1] = 0;
3526  lpTbb->dwData = btnPtr->dwData;
3527  lpTbb->iString = btnPtr->iString;
3528 
3529  return TRUE;
3530 }
3531 
3532 
3533 static LRESULT
3534 TOOLBAR_GetButtonInfoT(const TOOLBAR_INFO *infoPtr, INT Id, LPTBBUTTONINFOW lpTbInfo, BOOL bUnicode)
3535 {
3536  /* TBBUTTONINFOW and TBBUTTONINFOA have the same layout*/
3537  TBUTTON_INFO *btnPtr;
3538  INT nIndex;
3539 
3540  if (lpTbInfo == NULL)
3541  return -1;
3542 
3543  /* MSDN documents an iImageLabel field added in Vista but it is not present in
3544  * the headers and tests shows that even with comctl 6 Vista accepts only the
3545  * original TBBUTTONINFO size
3546  */
3547  if (lpTbInfo->cbSize != sizeof(TBBUTTONINFOW))
3548  {
3549  WARN("Invalid button size\n");
3550  return -1;
3551  }
3552 
3553  nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, lpTbInfo->dwMask & TBIF_BYINDEX);
3554  if (nIndex == -1)
3555  return -1;
3556 
3557  btnPtr = &infoPtr->buttons[nIndex];
3558  if (lpTbInfo->dwMask & TBIF_COMMAND)
3559  lpTbInfo->idCommand = btnPtr->idCommand;
3560  if (lpTbInfo->dwMask & TBIF_IMAGE)
3561  lpTbInfo->iImage = btnPtr->iBitmap;
3562  if (lpTbInfo->dwMask & TBIF_LPARAM)
3563  lpTbInfo->lParam = btnPtr->dwData;
3564  if (lpTbInfo->dwMask & TBIF_SIZE)
3565  /* tests show that for separators TBIF_SIZE returns not calculated width,
3566  but cx property, that differs from 0 only if application have
3567  specifically set it */
3568  lpTbInfo->cx = (btnPtr->fsStyle & BTNS_SEP)
3569  ? btnPtr->cx : (WORD)(btnPtr->rect.right - btnPtr->rect.left);
3570  if (lpTbInfo->dwMask & TBIF_STATE)
3571  lpTbInfo->fsState = btnPtr->fsState;
3572  if (lpTbInfo->dwMask & TBIF_STYLE)
3573  lpTbInfo->fsStyle = btnPtr->fsStyle;
3574  if (lpTbInfo->dwMask & TBIF_TEXT) {
3575  /* TB_GETBUTTONINFO doesn't retrieve text from the string list, so we
3576  can't use TOOLBAR_GetText here */
3577  if (!IS_INTRESOURCE(btnPtr->iString) && (btnPtr->iString != -1)) {
3578  LPWSTR lpText = (LPWSTR)btnPtr->iString;
3579  if (bUnicode)
3580  Str_GetPtrW(lpText, lpTbInfo->pszText, lpTbInfo->cchText);
3581  else
3582  Str_GetPtrWtoA(lpText, (LPSTR)lpTbInfo->pszText, lpTbInfo->cchText);
3583  } else if (!bUnicode || lpTbInfo->pszText)
3584  lpTbInfo->pszText[0] = '\0';
3585  }
3586  return nIndex;
3587 }
3588 
3589 
3590 static inline LRESULT
3592 {
3593  return MAKELONG((WORD)infoPtr->nButtonWidth,
3594  (WORD)infoPtr->nButtonHeight);
3595 }
3596 
3597 
3598 static LRESULT
3600 {
3601  INT nIndex;
3602  LPWSTR lpText;
3603  LRESULT ret = 0;
3604 
3605  nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3606  if (nIndex == -1)
3607  return -1;
3608 
3609  lpText = TOOLBAR_GetText(infoPtr,&infoPtr->buttons[nIndex]);
3610 
3611  if (isW)
3612  {
3613  if (lpText)
3614  {
3615  ret = strlenW (lpText);
3616  if (lpStr) strcpyW (lpStr, lpText);
3617  }
3618  }
3619  else
3620  ret = WideCharToMultiByte( CP_ACP, 0, lpText, -1,
3621  (LPSTR)lpStr, lpStr ? 0x7fffffff : 0, NULL, NULL ) - 1;
3622  return ret;
3623 }
3624 
3625 
3626 static LRESULT
3628 {
3629  TRACE("hwnd=%p, wParam=%ld\n", infoPtr->hwndSelf, wParam);
3630  /* UNDOCUMENTED: wParam is actually the ID of the image list to return */
3631  return (LRESULT)GETDISIMAGELIST(infoPtr, wParam);
3632 }
3633 
3634 
3635 static inline LRESULT
3637 {
3638  TRACE("\n");
3639 
3640  return infoPtr->dwExStyle;
3641 }
3642 
3643 
3644 static LRESULT
3646 {
3647  TRACE("hwnd=%p, wParam=%ld\n", infoPtr->hwndSelf, wParam);
3648  /* UNDOCUMENTED: wParam is actually the ID of the image list to return */
3649  return (LRESULT)GETHOTIMAGELIST(infoPtr, wParam);
3650 }
3651 
3652 
3653 static LRESULT
3655 {
3656  if (!((infoPtr->dwStyle & TBSTYLE_FLAT) || GetWindowTheme (infoPtr->hwndSelf)))
3657  return -1;
3658 
3659  if (infoPtr->nHotItem < 0)
3660  return -1;
3661 
3662  return (LRESULT)infoPtr->nHotItem;
3663 }
3664 
3665 
3666 static LRESULT
3668 {
3669  TRACE("hwnd=%p, wParam=%ld\n", infoPtr->hwndSelf, wParam);
3670  /* UNDOCUMENTED: wParam is actually the ID of the image list to return */
3671  return (LRESULT) GETDEFIMAGELIST(infoPtr, wParam);
3672 }
3673 
3674 
3675 static LRESULT
3677 {
3678  TRACE("hwnd = %p, lptbim = %p\n", infoPtr->hwndSelf, lptbim);
3679 
3680  *lptbim = infoPtr->tbim;
3681 
3682  return 0;
3683 }
3684 
3685 
3686 static inline LRESULT
3688 {
3689  TRACE("hwnd = %p\n", infoPtr->hwndSelf);
3690 
3691  return (LRESULT)infoPtr->clrInsertMark;
3692 }
3693 
3694 
3695 static LRESULT
3696 TOOLBAR_GetItemRect (const TOOLBAR_INFO *infoPtr, INT nIndex, LPRECT lpRect)
3697 {
3698  TBUTTON_INFO *btnPtr;
3699 
3700  btnPtr = &infoPtr->buttons[nIndex];
3701  if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
3702  return FALSE;
3703 
3704  if (lpRect == NULL)
3705  return FALSE;
3706  if (btnPtr->fsState & TBSTATE_HIDDEN)
3707  return FALSE;
3708 
3709  lpRect->left = btnPtr->rect.left;
3710  lpRect->right = btnPtr->rect.right;
3711  lpRect->bottom = btnPtr->rect.bottom;
3712  lpRect->top = btnPtr->rect.top;
3713 
3714  return TRUE;
3715 }
3716 
3717 
3718 static LRESULT
3719 TOOLBAR_GetMaxSize (const TOOLBAR_INFO *infoPtr, LPSIZE lpSize)
3720 {
3721  if (lpSize == NULL)
3722  return FALSE;
3723 
3724  lpSize->cx = infoPtr->rcBound.right - infoPtr->rcBound.left;
3725  lpSize->cy = infoPtr->rcBound.bottom - infoPtr->rcBound.top;
3726 
3727  TRACE("maximum size %d x %d\n",
3728  infoPtr->rcBound.right - infoPtr->rcBound.left,
3729  infoPtr->rcBound.bottom - infoPtr->rcBound.top);
3730 
3731  return TRUE;
3732 }
3733 
3734 #ifdef __REACTOS__
3735 static LRESULT
3736 TOOLBAR_GetMetrics(const TOOLBAR_INFO *infoPtr, TBMETRICS *pMetrics)
3737 {
3738  if (pMetrics == NULL || pMetrics->cbSize != sizeof(TBMETRICS))
3739  return 0;
3740 
3741  if (pMetrics->dwMask & TBMF_PAD)
3742  {
3743  pMetrics->cxPad = infoPtr->szPadding.cx;
3744  pMetrics->cyPad = infoPtr->szPadding.cy;
3745  }
3746 
3747  if (pMetrics->dwMask & TBMF_BARPAD)
3748  {
3749  pMetrics->cxBarPad = infoPtr->szBarPadding.cx;
3750  pMetrics->cyBarPad = infoPtr->szBarPadding.cy;
3751  }
3752 
3753  if (pMetrics->dwMask & TBMF_BUTTONSPACING)
3754  {
3755  pMetrics->cxButtonSpacing = infoPtr->szSpacing.cx;
3756  pMetrics->cyButtonSpacing = infoPtr->szSpacing.cy;
3757  }
3758 
3759  return 0;
3760 }
3761 #endif
3762 
3763 /* << TOOLBAR_GetObject >> */
3764 
3765 
3766 static inline LRESULT
3768 {
3769  return MAKELONG(infoPtr->szPadding.cx, infoPtr->szPadding.cy);
3770 }
3771 
3772 
3773 static LRESULT
3774 TOOLBAR_GetRect (const TOOLBAR_INFO *infoPtr, INT Id, LPRECT lpRect)
3775 {
3776  TBUTTON_INFO *btnPtr;
3777  INT nIndex;
3778 
3779  nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3780  btnPtr = &infoPtr->buttons[nIndex];
3781  if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
3782  return FALSE;
3783 
3784  if (lpRect == NULL)
3785  return FALSE;
3786 
3787  lpRect->left = btnPtr->rect.left;
3788  lpRect->right = btnPtr->rect.right;
3789  lpRect->bottom = btnPtr->rect.bottom;
3790  lpRect->top = btnPtr->rect.top;
3791 
3792  return TRUE;
3793 }
3794 
3795 
3796 static inline LRESULT
3798 {
3799  return infoPtr->nRows;
3800 }
3801 
3802 
3803 static LRESULT
3805 {
3806  INT nIndex;
3807 
3808  nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3809  if (nIndex == -1)
3810  return -1;
3811 
3812  return infoPtr->buttons[nIndex].fsState;
3813 }
3814 
3815 
3816 static inline LRESULT
3818 {
3819  return infoPtr->dwStyle;
3820 }
3821 
3822 
3823 static inline LRESULT
3825 {
3826  return infoPtr->nMaxTextRows;
3827 }
3828 
3829 
3830 static LRESULT
3832 {
3833  if ((infoPtr->dwStyle & TBSTYLE_TOOLTIPS) && (infoPtr->hwndToolTip == NULL))
3835  return (LRESULT)infoPtr->hwndToolTip;
3836 }
3837 
3838 
3839 static LRESULT
3841 {
3842  TRACE("%s hwnd=%p\n",
3843  infoPtr->bUnicode ? "TRUE" : "FALSE", infoPtr->hwndSelf);
3844 
3845  return infoPtr->bUnicode;
3846 }
3847 
3848 
3849 static inline LRESULT
3851 {
3852  return infoPtr->iVersion;
3853 }
3854 
3855 
3856 static LRESULT
3858 {
3859  TBUTTON_INFO *btnPtr;
3860  BYTE oldState;
3861  INT nIndex;
3862 
3863  TRACE("\n");
3864 
3865  nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3866  if (nIndex == -1)
3867  return FALSE;
3868 
3869  btnPtr = &infoPtr->buttons[nIndex];
3870  oldState = btnPtr->fsState;
3871 
3872  if (fHide)
3873  btnPtr->fsState |= TBSTATE_HIDDEN;
3874  else
3875  btnPtr->fsState &= ~TBSTATE_HIDDEN;
3876 
3877  if (oldState != btnPtr->fsState) {
3878  TOOLBAR_LayoutToolbar (infoPtr);
3879  InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
3880  }
3881 
3882  return TRUE;
3883 }
3884 
3885 
3886 static inline LRESULT
3887 TOOLBAR_HitTest (const TOOLBAR_INFO *infoPtr, const POINT* lpPt)
3888 {
3889  return TOOLBAR_InternalHitTest (infoPtr, lpPt, NULL);
3890 }
3891 
3892 
3893 static LRESULT
3894 TOOLBAR_Indeterminate (const TOOLBAR_INFO *infoPtr, INT Id, BOOL fIndeterminate)
3895 {
3896  TBUTTON_INFO *btnPtr;
3897  INT nIndex;
3898  DWORD oldState;
3899 
3900  nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
3901  if (nIndex == -1)
3902  return FALSE;
3903 
3904  btnPtr = &infoPtr->buttons[nIndex];
3905  oldState = btnPtr->fsState;
3906 
3907  if (fIndeterminate)
3908  btnPtr->fsState |= TBSTATE_INDETERMINATE;
3909  else
3910  btnPtr->fsState &= ~TBSTATE_INDETERMINATE;
3911 
3912  if(oldState != btnPtr->fsState)
3913  InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
3914 
3915  return TRUE;
3916 }
3917 
3918 
3919 static LRESULT
3920 TOOLBAR_InsertButtonT(TOOLBAR_INFO *infoPtr, INT nIndex, const TBBUTTON *lpTbb, BOOL fUnicode)
3921 {
3922  if (lpTbb == NULL)
3923  return FALSE;
3924 
3925  if (nIndex == -1) {
3926  /* EPP: this seems to be an undocumented call (from my IE4)
3927  * I assume in that case that:
3928  * - index of insertion is at the end of existing buttons
3929  * I only see this happen with nIndex == -1, but it could have a special
3930  * meaning (like -nIndex (or ~nIndex) to get the real position of insertion).
3931  */
3932  nIndex = infoPtr->nNumButtons;
3933 
3934  } else if (nIndex < 0)
3935  return FALSE;
3936 
3937  TRACE("inserting button index=%d\n", nIndex);
3938  if (nIndex > infoPtr->nNumButtons) {
3939  nIndex = infoPtr->nNumButtons;
3940  TRACE("adjust index=%d\n", nIndex);
3941  }
3942 
3943  return TOOLBAR_InternalInsertButtonsT(infoPtr, nIndex, 1, lpTbb, fUnicode);
3944 }
3945 
3946 /* << TOOLBAR_InsertMarkHitTest >> */
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_CHECKED);
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_ENABLED);
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_HIDDEN);
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_MARKED);
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_INDETERMINATE);
4011 }
4012 
4013 
4014 static LRESULT
4016 {
4017  INT nIndex;
4018 
4019  nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
4020  if (nIndex == -1)
4021  return -1;
4022 
4023  return (infoPtr->buttons[nIndex].fsState & TBSTATE_PRESSED);
4024 }
4025 
4026 
4027 static LRESULT
4029 {
4030  TBADDBITMAP tbab;
4031  tbab.hInst = hInstance;
4032  tbab.nID = wParam;
4033 
4034  TRACE("hwnd = %p, hInst = %p, nID = %lu\n", infoPtr->hwndSelf, tbab.hInst, tbab.nID);
4035 
4036  return TOOLBAR_AddBitmap(infoPtr, 0, &tbab);
4037 }
4038 
4039 
4040 static LRESULT
4041 TOOLBAR_MapAccelerator (const TOOLBAR_INFO *infoPtr, WCHAR wAccel, UINT *pIDButton)
4042 {
4043  WCHAR wszAccel[] = {'&',wAccel,0};
4044  int i;
4045 
4046  TRACE("hwnd = %p, wAccel = %x(%s), pIDButton = %p\n",
4047  infoPtr->hwndSelf, wAccel, debugstr_wn(&wAccel,1), pIDButton);
4048 
4049  for (i = 0; i < infoPtr->nNumButtons; i++)
4050  {
4051  TBUTTON_INFO *btnPtr = infoPtr->buttons+i;
4052  if (!(btnPtr->fsStyle & BTNS_NOPREFIX) &&
4053  !(btnPtr->fsState & TBSTATE_HIDDEN))
4054  {
4055  int iLen = strlenW(wszAccel);
4056  LPCWSTR lpszStr = TOOLBAR_GetText(infoPtr, btnPtr);
4057 
4058  if (!lpszStr)
4059  continue;
4060 
4061  while (*lpszStr)
4062  {
4063  if ((lpszStr[0] == '&') && (lpszStr[1] == '&'))
4064  {
4065  lpszStr += 2;
4066  continue;
4067  }
4068  if (!strncmpiW(lpszStr, wszAccel, iLen))
4069  {
4070  *pIDButton = btnPtr->idCommand;
4071  return TRUE;
4072  }
4073  lpszStr++;
4074  }
4075  }
4076  }
4077  return FALSE;
4078 }
4079 
4080 
4081 static LRESULT
4082 TOOLBAR_MarkButton (const TOOLBAR_INFO *infoPtr, INT Id, BOOL fMark)
4083 {
4084  INT nIndex;
4085  DWORD oldState;
4086  TBUTTON_INFO *btnPtr;
4087 
4088  TRACE("hwnd = %p, Id = %d, fMark = 0%d\n", infoPtr->hwndSelf, Id, fMark);
4089 
4090  nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
4091  if (nIndex == -1)
4092  return FALSE;
4093 
4094  btnPtr = &infoPtr->buttons[nIndex];
4095  oldState = btnPtr->fsState;
4096 
4097  if (fMark)
4098  btnPtr->fsState |= TBSTATE_MARKED;
4099  else
4100  btnPtr->fsState &= ~TBSTATE_MARKED;
4101 
4102  if(oldState != btnPtr->fsState)
4103  InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
4104 
4105  return TRUE;
4106 }
4107 
4108 
4109 /* fixes up an index of a button affected by a move */
4110 static inline void TOOLBAR_MoveFixupIndex(INT* pIndex, INT nIndex, INT nMoveIndex, BOOL bMoveUp)
4111 {
4112  if (bMoveUp)
4113  {
4114  if (*pIndex > nIndex && *pIndex <= nMoveIndex)
4115  (*pIndex)--;
4116  else if (*pIndex == nIndex)
4117  *pIndex = nMoveIndex;
4118  }
4119  else
4120  {
4121  if (*pIndex >= nMoveIndex && *pIndex < nIndex)
4122  (*pIndex)++;
4123  else if (*pIndex == nIndex)
4124  *pIndex = nMoveIndex;
4125  }
4126 }
4127 
4128 
4129 static LRESULT
4130 TOOLBAR_MoveButton (TOOLBAR_INFO *infoPtr, INT Id, INT nMoveIndex)
4131 {
4132  INT nIndex;
4133  INT nCount;
4135 
4136  TRACE("hwnd=%p, Id=%d, nMoveIndex=%d\n", infoPtr->hwndSelf, Id, nMoveIndex);
4137 
4138  nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, TRUE);
4139  if ((nIndex == -1) || (nMoveIndex < 0))
4140  return FALSE;
4141 
4142  if (nMoveIndex > infoPtr->nNumButtons - 1)
4143  nMoveIndex = infoPtr->nNumButtons - 1;
4144 
4145  button = infoPtr->buttons[nIndex];
4146 
4147  /* move button right */
4148  if (nIndex < nMoveIndex)
4149  {
4150  nCount = nMoveIndex - nIndex;
4151  memmove(&infoPtr->buttons[nIndex], &infoPtr->buttons[nIndex+1], nCount*sizeof(TBUTTON_INFO));
4152  infoPtr->buttons[nMoveIndex] = button;
4153 
4154  TOOLBAR_MoveFixupIndex(&infoPtr->nButtonDown, nIndex, nMoveIndex, TRUE);
4155  TOOLBAR_MoveFixupIndex(&infoPtr->nButtonDrag, nIndex, nMoveIndex, TRUE);
4156  TOOLBAR_MoveFixupIndex(&infoPtr->nOldHit, nIndex, nMoveIndex, TRUE);
4157  TOOLBAR_MoveFixupIndex(&infoPtr->nHotItem, nIndex, nMoveIndex, TRUE);
4158  }
4159  else if (nIndex > nMoveIndex) /* move button left */
4160  {
4161  nCount = nIndex - nMoveIndex;
4162  memmove(&infoPtr->buttons[nMoveIndex+1], &infoPtr->buttons[nMoveIndex], nCount*sizeof(TBUTTON_INFO));
4163  infoPtr->buttons[nMoveIndex] = button;
4164 
4165  TOOLBAR_MoveFixupIndex(&infoPtr->nButtonDown, nIndex, nMoveIndex, FALSE);
4166  TOOLBAR_MoveFixupIndex(&infoPtr->nButtonDrag, nIndex, nMoveIndex, FALSE);
4167  TOOLBAR_MoveFixupIndex(&infoPtr->nOldHit, nIndex, nMoveIndex, FALSE);
4168  TOOLBAR_MoveFixupIndex(&infoPtr->nHotItem, nIndex, nMoveIndex, FALSE);
4169  }
4170 
4171  TOOLBAR_LayoutToolbar(infoPtr);
4172  TOOLBAR_AutoSize(infoPtr);
4173  InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
4174 
4175  return TRUE;
4176 }
4177 
4178 
4179 static LRESULT
4180 TOOLBAR_PressButton (const TOOLBAR_INFO *infoPtr, INT Id, BOOL fPress)
4181 {
4182  TBUTTON_INFO *btnPtr;
4183  INT nIndex;
4184  DWORD oldState;
4185 
4186  nIndex = TOOLBAR_GetButtonIndex (infoPtr, Id, FALSE);
4187  if (nIndex == -1)
4188  return FALSE;
4189 
4190  btnPtr = &infoPtr->buttons[nIndex];
4191  oldState = btnPtr->fsState;
4192 
4193  if (fPress)
4194  btnPtr->fsState |= TBSTATE_PRESSED;
4195  else
4196  btnPtr->fsState &= ~TBSTATE_PRESSED;
4197 
4198  if(oldState != btnPtr->fsState)
4199  InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
4200 
4201  return TRUE;
4202 }
4203 
4204 /* FIXME: there might still be some confusion her between number of buttons
4205  * and number of bitmaps */
4206 static LRESULT
4208 {
4209  HBITMAP hBitmap;
4210  int i = 0, nOldButtons = 0, pos = 0;
4211  int nOldBitmaps, nNewBitmaps = 0;
4212  HIMAGELIST himlDef = 0;
4213 
4214  TRACE("hInstOld %p nIDOld %lx hInstNew %p nIDNew %lx nButtons %x\n",
4215  lpReplace->hInstOld, lpReplace->nIDOld, lpReplace->hInstNew, lpReplace->nIDNew,
4216  lpReplace->nButtons);
4217 
4218  if (lpReplace->hInstOld == HINST_COMMCTRL)
4219  {
4220  FIXME("changing standard bitmaps not implemented\n");
4221  return FALSE;
4222  }
4223  else if (lpReplace->hInstOld != 0 && lpReplace->hInstOld != lpReplace->hInstNew)
4224  FIXME("resources not in the current module not implemented\n");
4225 
4226  TRACE("To be replaced hInstOld %p nIDOld %lx\n", lpReplace->hInstOld, lpReplace->nIDOld);
4227  for (i = 0; i < infoPtr->nNumBitmapInfos; i++) {
4228  TBITMAP_INFO *tbi = &infoPtr->bitmaps[i];
4229  TRACE("tbimapinfo %d hInstOld %p nIDOld %x\n", i, tbi->hInst, tbi->nID);
4230  if (tbi->hInst == lpReplace->hInstOld && tbi->nID == lpReplace->nIDOld)
4231  {
4232  TRACE("Found: nButtons %d hInst %p nID %x\n", tbi->nButtons, tbi->hInst, tbi->nID);
4233  nOldButtons = tbi->nButtons;
4234  tbi->nButtons = lpReplace->nButtons;
4235  tbi->hInst = lpReplace->hInstNew;
4236  tbi->nID = lpReplace->nIDNew;
4237  TRACE("tbimapinfo changed %d hInstOld %p nIDOld %x\n", i, tbi->hInst, tbi->nID);
4238  break;
4239  }
4240  pos += tbi->nButtons;
4241  }
4242 
4243  if (nOldButtons == 0)
4244  {
4245  WARN("No hinst/bitmap found! hInst %p nID %lx\n", lpReplace->hInstOld, lpReplace->nIDOld);
4246  return FALSE;
4247  }
4248 
4249  /* copy the bitmap before adding it as ImageList_AddMasked modifies the
4250  * bitmap
4251  */
4252  if (lpReplace->hInstNew)