~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/comctl32/toolbar.c

Version: ~ [ wine-1.0-rc1 ] ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  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_GETMETRICS
 37  *     - TB_GETOBJECT
 38  *     - TB_INSERTMARKHITTEST
 39  *     - TB_SAVERESTORE
 40  *     - TB_SETMETRICS
 41  *     - WM_WININICHANGE
 42  *   - Notifications:
 43  *     - NM_CHAR
 44  *     - TBN_GETOBJECT
 45  *     - TBN_SAVE
 46  *   - Button wrapping (under construction).
 47  *   - Fix TB_SETROWS and Separators.
 48  *   - iListGap custom draw support.
 49  *
 50  * Testing:
 51  *   - Run tests using Waite Group Windows95 API Bible Volume 2.
 52  *     The second cdrom contains executables addstr.exe, btncount.exe,
 53  *     btnstate.exe, butstrsz.exe, chkbtn.exe, chngbmp.exe, customiz.exe,
 54  *     enablebtn.exe, getbmp.exe, getbtn.exe, getflags.exe, hidebtn.exe,
 55  *     indetbtn.exe, insbtn.exe, pressbtn.exe, setbtnsz.exe, setcmdid.exe,
 56  *     setparnt.exe, setrows.exe, toolwnd.exe.
 57  *   - Microsoft's controlspy examples.
 58  *   - Charles Petzold's 'Programming Windows': gadgets.exe
 59  *
 60  *  Differences between MSDN and actual native control operation:
 61  *   1. MSDN says: "TBSTYLE_LIST: Creates a flat toolbar with button text
 62  *                  to the right of the bitmap. Otherwise, this style is
 63  *                  identical to TBSTYLE_FLAT."
 64  *      As implemented by both v4.71 and v5.80 of the native COMCTL32.DLL
 65  *      you can create a TBSTYLE_LIST without TBSTYLE_FLAT and the result
 66  *      is non-flat non-transparent buttons. Therefore TBSTYLE_LIST does
 67  *      *not* imply TBSTYLE_FLAT as documented.  (GA 8/2001)
 68  *
 69  */
 70 
 71 #include <stdarg.h>
 72 #include <string.h>
 73 
 74 #include "windef.h"
 75 #include "winbase.h"
 76 #include "winreg.h"
 77 #include "wingdi.h"
 78 #include "winuser.h"
 79 #include "wine/unicode.h"
 80 #include "winnls.h"
 81 #include "commctrl.h"
 82 #include "comctl32.h"
 83 #include "uxtheme.h"
 84 #include "tmschema.h"
 85 #include "wine/debug.h"
 86 
 87 WINE_DEFAULT_DEBUG_CHANNEL(toolbar);
 88 
 89 static HCURSOR hCursorDrag = NULL;
 90 
 91 typedef struct
 92 {
 93     INT iBitmap;
 94     INT idCommand;
 95     BYTE  fsState;
 96     BYTE  fsStyle;
 97     BYTE  bHot;
 98     BYTE  bDropDownPressed;
 99     DWORD_PTR dwData;
100     INT_PTR iString;
101     INT nRow;
102     RECT rect;
103     INT cx; /* manually set size */
104 } TBUTTON_INFO;
105 
106 typedef struct
107 {
108     UINT nButtons;
109     HINSTANCE hInst;
110     UINT nID;
111 } TBITMAP_INFO;
112 
113 typedef struct
114 {
115     HIMAGELIST himl;
116     INT id;
117 } IMLENTRY, *PIMLENTRY;
118 
119 typedef struct
120 {
121     DWORD    dwStructSize;    /* size of TBBUTTON struct */
122     INT      nWidth;          /* width of the toolbar */
123     RECT     client_rect;
124     RECT     rcBound;         /* bounding rectangle */
125     INT      nButtonHeight;
126     INT      nButtonWidth;
127     INT      nBitmapHeight;
128     INT      nBitmapWidth;
129     INT      nIndent;
130     INT      nRows;           /* number of button rows */
131     INT      nMaxTextRows;    /* maximum number of text rows */
132     INT      cxMin;           /* minimum button width */
133     INT      cxMax;           /* maximum button width */
134     INT      nNumButtons;     /* number of buttons */
135     INT      nNumBitmaps;     /* number of bitmaps */
136     INT      nNumStrings;     /* number of strings */
137     INT      nNumBitmapInfos;
138     INT      nButtonDown;     /* toolbar button being pressed or -1 if none */
139     INT      nButtonDrag;     /* toolbar button being dragged or -1 if none */
140     INT      nOldHit;
141     INT      nHotItem;        /* index of the "hot" item */
142     SIZE     szPadding;       /* padding values around button */
143     INT      iTopMargin;      /* the top margin */
144     INT      iListGap;        /* default gap between text and image for toolbar with list style */
145     HFONT    hDefaultFont;
146     HFONT    hFont;           /* text font */
147     HIMAGELIST himlInt;       /* image list created internally */
148     PIMLENTRY *himlDef;       /* default image list array */
149     INT       cimlDef;        /* default image list array count */
150     PIMLENTRY *himlHot;       /* hot image list array */
151     INT       cimlHot;        /* hot image list array count */
152     PIMLENTRY *himlDis;       /* disabled image list array */
153     INT       cimlDis;        /* disabled image list array count */
154     HWND     hwndToolTip;     /* handle to tool tip control */
155     HWND     hwndNotify;      /* handle to the window that gets notifications */
156     HWND     hwndSelf;        /* my own handle */
157     BOOL     bAnchor;         /* anchor highlight enabled */
158     BOOL     bDoRedraw;       /* Redraw status */
159     BOOL     bDragOutSent;    /* has TBN_DRAGOUT notification been sent for this drag? */
160     BOOL     bUnicode;        /* Notifications are ASCII (FALSE) or Unicode (TRUE)? */
161     BOOL     bCaptured;       /* mouse captured? */
162     DWORD      dwStyle;       /* regular toolbar style */
163     DWORD      dwExStyle;     /* extended toolbar style */
164     DWORD      dwDTFlags;     /* DrawText flags */
165 
166     COLORREF   clrInsertMark;   /* insert mark color */
167     COLORREF   clrBtnHighlight; /* color for Flat Separator */
168     COLORREF   clrBtnShadow;    /* color for Flag Separator */
169     INT      iVersion;
170     LPWSTR   pszTooltipText;    /* temporary store for a string > 80 characters
171                                  * for TTN_GETDISPINFOW notification */
172     TBINSERTMARK  tbim;         /* info on insertion mark */
173     TBUTTON_INFO *buttons;      /* pointer to button array */
174     LPWSTR       *strings;      /* pointer to string array */
175     TBITMAP_INFO *bitmaps;
176 } TOOLBAR_INFO, *PTOOLBAR_INFO;
177 
178 
179 /* used by customization dialog */
180 typedef struct
181 {
182     PTOOLBAR_INFO tbInfo;
183     HWND          tbHwnd;
184 } CUSTDLG_INFO, *PCUSTDLG_INFO;
185 
186 typedef struct
187 {
188     TBBUTTON btn;
189     BOOL     bVirtual;
190     BOOL     bRemovable;
191     WCHAR    text[64];
192 } CUSTOMBUTTON, *PCUSTOMBUTTON;
193 
194 typedef enum
195 {
196     IMAGE_LIST_DEFAULT,
197     IMAGE_LIST_HOT,
198     IMAGE_LIST_DISABLED
199 } IMAGE_LIST_TYPE;
200 
201 #define SEPARATOR_WIDTH    8
202 #define TOP_BORDER         2
203 #define BOTTOM_BORDER      2
204 #define DDARROW_WIDTH      11
205 #define ARROW_HEIGHT       3
206 #define INSERTMARK_WIDTH   2
207 
208 #define DEFPAD_CX 7
209 #define DEFPAD_CY 6
210 #define DEFLISTGAP 4
211 
212 /* vertical padding used in list mode when image is present */
213 #define LISTPAD_CY 9
214 
215 /* how wide to treat the bitmap if it isn't present */
216 #define NONLIST_NOTEXT_OFFSET 2
217 
218 #define TOOLBAR_NOWHERE (-1)
219 
220 #define TOOLBAR_GetInfoPtr(hwnd) ((TOOLBAR_INFO *)GetWindowLongPtrW(hwnd,0))
221 #define TOOLBAR_HasText(x, y) (TOOLBAR_GetText(x, y) ? TRUE : FALSE)
222 #define TOOLBAR_HasDropDownArrows(exStyle) ((exStyle & TBSTYLE_EX_DRAWDDARROWS) ? TRUE : FALSE)
223 
224 /* Used to find undocumented extended styles */
225 #define TBSTYLE_EX_ALL (TBSTYLE_EX_DRAWDDARROWS | \
226                         TBSTYLE_EX_UNDOC1 | \
227                         TBSTYLE_EX_MIXEDBUTTONS | \
228                         TBSTYLE_EX_HIDECLIPPEDBUTTONS)
229 
230 /* all of the CCS_ styles */
231 #define COMMON_STYLES (CCS_TOP|CCS_NOMOVEY|CCS_BOTTOM|CCS_NORESIZE| \
232                        CCS_NOPARENTALIGN|CCS_ADJUSTABLE|CCS_NODIVIDER|CCS_VERT)
233 
234 #define GETIBITMAP(infoPtr, i) (infoPtr->iVersion >= 5 ? LOWORD(i) : i)
235 #define GETHIMLID(infoPtr, i) (infoPtr->iVersion >= 5 ? HIWORD(i) : 0)
236 #define GETDEFIMAGELIST(infoPtr, id) TOOLBAR_GetImageList(infoPtr->himlDef, infoPtr->cimlDef, id)
237 #define GETHOTIMAGELIST(infoPtr, id) TOOLBAR_GetImageList(infoPtr->himlHot, infoPtr->cimlHot, id)
238 #define GETDISIMAGELIST(infoPtr, id) TOOLBAR_GetImageList(infoPtr->himlDis, infoPtr->cimlDis, id)
239 
240 static const WCHAR themeClass[] = { 'T','o','o','l','b','a','r',0 };
241 
242 static BOOL TOOLBAR_GetButtonInfo(const TOOLBAR_INFO *infoPtr, NMTOOLBARW *nmtb);
243 static BOOL TOOLBAR_IsButtonRemovable(const TOOLBAR_INFO *infoPtr, int iItem, PCUSTOMBUTTON btnInfo);
244 static HIMAGELIST TOOLBAR_GetImageList(const PIMLENTRY *pies, INT cies, INT id);
245 static PIMLENTRY TOOLBAR_GetImageListEntry(const PIMLENTRY *pies, INT cies, INT id);
246 static VOID TOOLBAR_DeleteImageList(PIMLENTRY **pies, INT *cies);
247 static HIMAGELIST TOOLBAR_InsertImageList(PIMLENTRY **pies, INT *cies, HIMAGELIST himl, INT id);
248 static LRESULT TOOLBAR_LButtonDown(HWND hwnd, WPARAM wParam, LPARAM lParam);
249 static void TOOLBAR_SetHotItemEx (TOOLBAR_INFO *infoPtr, INT nHit, DWORD dwReason);
250 static void TOOLBAR_LayoutToolbar(HWND hwnd);
251 static LRESULT TOOLBAR_AutoSize(HWND hwnd);
252 static void TOOLBAR_CheckImageListIconSize(TOOLBAR_INFO *infoPtr);
253 static void TOOLBAR_TooltipSetRect(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *button);
254 
255 static LRESULT
256 TOOLBAR_NotifyFormat(const TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam);
257 
258 static inline int default_top_margin(const TOOLBAR_INFO *infoPtr)
259 {
260     return (infoPtr->dwStyle & TBSTYLE_FLAT ? 0 : TOP_BORDER);
261 }
262 
263 static LPWSTR
264 TOOLBAR_GetText(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *btnPtr)
265 {
266     LPWSTR lpText = NULL;
267 
268     /* NOTE: iString == -1 is undocumented */
269     if ((HIWORD(btnPtr->iString) != 0) && (btnPtr->iString != -1))
270         lpText = (LPWSTR)btnPtr->iString;
271     else if ((btnPtr->iString >= 0) && (btnPtr->iString < infoPtr->nNumStrings))
272         lpText = infoPtr->strings[btnPtr->iString];
273 
274     return lpText;
275 }
276 
277 static void
278 TOOLBAR_DumpButton(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *bP, INT btn_num, BOOL internal)
279 {
280     if (TRACE_ON(toolbar)){
281         TRACE("button %d id %d, bitmap=%d, state=%02x, style=%02x, data=%08lx, stringid=0x%08lx\n",
282               btn_num, bP->idCommand, GETIBITMAP(infoPtr, bP->iBitmap), 
283               bP->fsState, bP->fsStyle, bP->dwData, bP->iString);
284         TRACE("string %s\n", debugstr_w(TOOLBAR_GetText(infoPtr,bP)));
285         if (internal)
286             TRACE("button %d id %d, hot=%s, row=%d, rect=(%s)\n",
287                   btn_num, bP->idCommand,
288                   (bP->bHot) ? "TRUE":"FALSE", bP->nRow,
289                   wine_dbgstr_rect(&bP->rect));
290     }
291 }
292 
293 
294 static void
295 TOOLBAR_DumpToolbar(const TOOLBAR_INFO *iP, INT line)
296 {
297     if (TRACE_ON(toolbar)) {
298         INT i;
299 
300         TRACE("toolbar %p at line %d, exStyle=%08x, buttons=%d, bitmaps=%d, strings=%d, style=%08x\n",
301               iP->hwndSelf, line,
302               iP->dwExStyle, iP->nNumButtons, iP->nNumBitmaps,
303               iP->nNumStrings, iP->dwStyle);
304         TRACE("toolbar %p at line %d, himlInt=%p, himlDef=%p, himlHot=%p, himlDis=%p, redrawable=%s\n",
305               iP->hwndSelf, line,
306               iP->himlInt, iP->himlDef, iP->himlHot, iP->himlDis,
307               (iP->bDoRedraw) ? "TRUE" : "FALSE");
308         for(i=0; i<iP->nNumButtons; i++) {
309             TOOLBAR_DumpButton(iP, &iP->buttons[i], i, TRUE);
310         }
311     }
312 }
313 
314 
315 /***********************************************************************
316 *               TOOLBAR_CheckStyle
317 *
318 * This function validates that the styles set are implemented and
319 * issues FIXME's warning of possible problems. In a perfect world this
320 * function should be null.
321 */
322 static void
323 TOOLBAR_CheckStyle (HWND hwnd, DWORD dwStyle)
324 {
325     if (dwStyle & TBSTYLE_REGISTERDROP)
326         FIXME("[%p] TBSTYLE_REGISTERDROP not implemented\n", hwnd);
327 }
328 
329 
330 static INT
331 TOOLBAR_SendNotify (NMHDR *nmhdr, const TOOLBAR_INFO *infoPtr, UINT code)
332 {
333         if(!IsWindow(infoPtr->hwndSelf))
334             return 0;   /* we have just been destroyed */
335 
336     nmhdr->idFrom = GetDlgCtrlID (infoPtr->hwndSelf);
337     nmhdr->hwndFrom = infoPtr->hwndSelf;
338     nmhdr->code = code;
339 
340     TRACE("to window %p, code=%08x, %s\n", infoPtr->hwndNotify, code,
341           (infoPtr->bUnicode) ? "via Unicode" : "via ANSI");
342 
343     return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmhdr->idFrom, (LPARAM)nmhdr);
344 }
345 
346 /***********************************************************************
347 *               TOOLBAR_GetBitmapIndex
348 *
349 * This function returns the bitmap index associated with a button.
350 * If the button specifies I_IMAGECALLBACK, then the TBN_GETDISPINFO
351 * is issued to retrieve the index.
352 */
353 static INT
354 TOOLBAR_GetBitmapIndex(const TOOLBAR_INFO *infoPtr, TBUTTON_INFO *btnPtr)
355 {
356     INT ret = btnPtr->iBitmap;
357 
358     if (ret == I_IMAGECALLBACK)
359     {
360         /* issue TBN_GETDISPINFO */
361         NMTBDISPINFOW nmgd;
362 
363         memset(&nmgd, 0, sizeof(nmgd));
364         nmgd.idCommand = btnPtr->idCommand;
365         nmgd.lParam = btnPtr->dwData;
366         nmgd.dwMask = TBNF_IMAGE;
367         nmgd.iImage = -1;
368         /* Windows also send TBN_GETDISPINFOW even if the control is ANSI */
369         TOOLBAR_SendNotify(&nmgd.hdr, infoPtr, TBN_GETDISPINFOW);
370         if (nmgd.dwMask & TBNF_DI_SETITEM)
371             btnPtr->iBitmap = nmgd.iImage;
372         ret = nmgd.iImage;
373         TRACE("TBN_GETDISPINFO returned bitmap id %d, mask=%08x, nNumBitmaps=%d\n",
374             ret, nmgd.dwMask, infoPtr->nNumBitmaps);
375     }
376 
377     if (ret != I_IMAGENONE)
378         ret = GETIBITMAP(infoPtr, ret);
379 
380     return ret;
381 }
382 
383 
384 static BOOL
385 TOOLBAR_IsValidBitmapIndex(const TOOLBAR_INFO *infoPtr, INT index)
386 {
387     HIMAGELIST himl;
388     INT id = GETHIMLID(infoPtr, index);
389     INT iBitmap = GETIBITMAP(infoPtr, index);
390 
391     if (((himl = GETDEFIMAGELIST(infoPtr, id)) &&
392         iBitmap >= 0 && iBitmap < ImageList_GetImageCount(himl)) ||
393         (index == I_IMAGECALLBACK))
394       return TRUE;
395     else
396       return FALSE;
397 }
398 
399 
400 static inline BOOL
401 TOOLBAR_IsValidImageList(const TOOLBAR_INFO *infoPtr, INT index)
402 {
403     HIMAGELIST himl = GETDEFIMAGELIST(infoPtr, GETHIMLID(infoPtr, index));
404     return (himl != NULL) && (ImageList_GetImageCount(himl) > 0);
405 }
406 
407 
408 /***********************************************************************
409 *               TOOLBAR_GetImageListForDrawing
410 *
411 * This function validates the bitmap index (including I_IMAGECALLBACK
412 * functionality) and returns the corresponding image list.
413 */
414 static HIMAGELIST
415 TOOLBAR_GetImageListForDrawing (const TOOLBAR_INFO *infoPtr, TBUTTON_INFO *btnPtr,
416                                 IMAGE_LIST_TYPE imagelist, INT * index)
417 {
418     HIMAGELIST himl;
419 
420     if (!TOOLBAR_IsValidBitmapIndex(infoPtr,btnPtr->iBitmap)) {
421         if (btnPtr->iBitmap == I_IMAGENONE) return NULL;
422         ERR("bitmap for ID %d, index %d is not valid, number of bitmaps in imagelist: %d\n",
423             HIWORD(btnPtr->iBitmap), LOWORD(btnPtr->iBitmap), infoPtr->nNumBitmaps);
424         return NULL;
425     }
426 
427     if ((*index = TOOLBAR_GetBitmapIndex(infoPtr, btnPtr)) < 0) {
428         if ((*index == I_IMAGECALLBACK) ||
429             (*index == I_IMAGENONE)) return NULL;
430         ERR("TBN_GETDISPINFO returned invalid index %d\n",
431             *index);
432         return NULL;
433     }
434 
435     switch(imagelist)
436     {
437     case IMAGE_LIST_DEFAULT:
438         himl = GETDEFIMAGELIST(infoPtr, GETHIMLID(infoPtr, btnPtr->iBitmap));
439         break;
440     case IMAGE_LIST_HOT:
441         himl = GETHOTIMAGELIST(infoPtr, GETHIMLID(infoPtr, btnPtr->iBitmap));
442         break;
443     case IMAGE_LIST_DISABLED:
444         himl = GETDISIMAGELIST(infoPtr, GETHIMLID(infoPtr, btnPtr->iBitmap));
445         break;
446     default:
447         himl = NULL;
448         FIXME("Shouldn't reach here\n");
449     }
450 
451     if (!himl)
452        TRACE("no image list\n");
453 
454     return himl;
455 }
456 
457 
458 static void
459 TOOLBAR_DrawFlatSeparator (const RECT *lpRect, HDC hdc, const TOOLBAR_INFO *infoPtr)
460 {
461     RECT myrect;
462     COLORREF oldcolor, newcolor;
463 
464     myrect.left = (lpRect->left + lpRect->right) / 2 - 1;
465     myrect.right = myrect.left + 1;
466     myrect.top = lpRect->top + 2;
467     myrect.bottom = lpRect->bottom - 2;
468 
469     newcolor = (infoPtr->clrBtnShadow == CLR_DEFAULT) ?
470                 comctl32_color.clrBtnShadow : infoPtr->clrBtnShadow;
471     oldcolor = SetBkColor (hdc, newcolor);
472     ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
473 
474     myrect.left = myrect.right;
475     myrect.right = myrect.left + 1;
476 
477     newcolor = (infoPtr->clrBtnHighlight == CLR_DEFAULT) ?
478                 comctl32_color.clrBtnHighlight : infoPtr->clrBtnHighlight;
479     SetBkColor (hdc, newcolor);
480     ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
481 
482     SetBkColor (hdc, oldcolor);
483 }
484 
485 
486 /***********************************************************************
487 *               TOOLBAR_DrawDDFlatSeparator
488 *
489 * This function draws the separator that was flagged as BTNS_DROPDOWN.
490 * In this case, the separator is a pixel high line of COLOR_BTNSHADOW,
491 * followed by a pixel high line of COLOR_BTNHIGHLIGHT. These separators
492 * are horizontal as opposed to the vertical separators for not dropdown
493 * type.
494 *
495 * FIXME: It is possible that the height of each line is really SM_CYBORDER.
496 */
497 static void
498 TOOLBAR_DrawDDFlatSeparator (const RECT *lpRect, HDC hdc, const TBUTTON_INFO *btnPtr,
499                              const TOOLBAR_INFO *infoPtr)
500 {
501     RECT myrect;
502     COLORREF oldcolor, newcolor;
503 
504     myrect.left = lpRect->left;
505     myrect.right = lpRect->right;
506     myrect.top = lpRect->top + (lpRect->bottom - lpRect->top - 2)/2;
507     myrect.bottom = myrect.top + 1;
508 
509     InflateRect (&myrect, -2, 0);
510 
511     TRACE("rect=(%s)\n", wine_dbgstr_rect(&myrect));
512 
513     newcolor = (infoPtr->clrBtnShadow == CLR_DEFAULT) ?
514                 comctl32_color.clrBtnShadow : infoPtr->clrBtnShadow;
515     oldcolor = SetBkColor (hdc, newcolor);
516     ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
517 
518     myrect.top = myrect.bottom;
519     myrect.bottom = myrect.top + 1;
520 
521     newcolor = (infoPtr->clrBtnHighlight == CLR_DEFAULT) ?
522                 comctl32_color.clrBtnHighlight : infoPtr->clrBtnHighlight;
523     SetBkColor (hdc, newcolor);
524     ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
525 
526     SetBkColor (hdc, oldcolor);
527 }
528 
529 
530 static void
531 TOOLBAR_DrawArrow (HDC hdc, INT left, INT top, COLORREF clr)
532 {
533     INT x, y;
534     HPEN hPen, hOldPen;
535 
536     if (!(hPen = CreatePen( PS_SOLID, 1, clr))) return;
537     hOldPen = SelectObject ( hdc, hPen );
538     x = left + 2;
539     y = top;
540     MoveToEx (hdc, x, y, NULL);
541     LineTo (hdc, x+5, y++); x++;
542     MoveToEx (hdc, x, y, NULL);
543     LineTo (hdc, x+3, y++); x++;
544     MoveToEx (hdc, x, y, NULL);
545     LineTo (hdc, x+1, y++);
546     SelectObject( hdc, hOldPen );
547     DeleteObject( hPen );
548 }
549 
550 /*
551  * Draw the text string for this button.
552  * note: infoPtr->himlDis *SHOULD* be non-zero when infoPtr->himlDef
553  *      is non-zero, so we can simply check himlDef to see if we have
554  *      an image list
555  */
556 static void
557 TOOLBAR_DrawString (const TOOLBAR_INFO *infoPtr, RECT *rcText, LPCWSTR lpText,
558                     const NMTBCUSTOMDRAW *tbcd, DWORD dwItemCDFlag)
559 {
560     HDC hdc = tbcd->nmcd.hdc;
561     HFONT  hOldFont = 0;
562     COLORREF clrOld = 0;
563     COLORREF clrOldBk = 0;
564     int oldBkMode = 0;
565     UINT state = tbcd->nmcd.uItemState;
566 
567     /* draw text */
568     if (lpText) {
569         TRACE("string=%s rect=(%s)\n", debugstr_w(lpText),
570               wine_dbgstr_rect(rcText));
571 
572         hOldFont = SelectObject (hdc, infoPtr->hFont);
573         if ((state & CDIS_HOT) && (dwItemCDFlag & TBCDRF_HILITEHOTTRACK )) {
574             clrOld = SetTextColor (hdc, tbcd->clrTextHighlight);
575         }
576         else if (state & CDIS_DISABLED) {
577             clrOld = SetTextColor (hdc, tbcd->clrBtnHighlight);
578             OffsetRect (rcText, 1, 1);
579             DrawTextW (hdc, lpText, -1, rcText, infoPtr->dwDTFlags);
580             SetTextColor (hdc, comctl32_color.clr3dShadow);
581             OffsetRect (rcText, -1, -1);
582         }
583         else if (state & CDIS_INDETERMINATE) {
584             clrOld = SetTextColor (hdc, comctl32_color.clr3dShadow);
585         }
586         else if ((state & CDIS_MARKED) && !(dwItemCDFlag & TBCDRF_NOMARK)) {
587             clrOld = SetTextColor (hdc, tbcd->clrTextHighlight);
588             clrOldBk = SetBkColor (hdc, tbcd->clrMark);
589             oldBkMode = SetBkMode (hdc, tbcd->nHLStringBkMode);
590         }
591         else {
592             clrOld = SetTextColor (hdc, tbcd->clrText);
593         }
594 
595         DrawTextW (hdc, lpText, -1, rcText, infoPtr->dwDTFlags);
596         SetTextColor (hdc, clrOld);
597         if ((state & CDIS_MARKED) && !(dwItemCDFlag & TBCDRF_NOMARK))
598         {
599             SetBkColor (hdc, clrOldBk);
600             SetBkMode (hdc, oldBkMode);
601         }
602         SelectObject (hdc, hOldFont);
603     }
604 }
605 
606 
607 static void
608 TOOLBAR_DrawPattern (const RECT *lpRect, const NMTBCUSTOMDRAW *tbcd)
609 {
610     HDC hdc = tbcd->nmcd.hdc;
611     HBRUSH hbr = SelectObject (hdc, tbcd->hbrMonoDither);
612     COLORREF clrTextOld;
613     COLORREF clrBkOld;
614     INT cx = lpRect->right - lpRect->left;
615     INT cy = lpRect->bottom - lpRect->top;
616     INT cxEdge = GetSystemMetrics(SM_CXEDGE);
617     INT cyEdge = GetSystemMetrics(SM_CYEDGE);
618     clrTextOld = SetTextColor(hdc, tbcd->clrBtnHighlight);
619     clrBkOld = SetBkColor(hdc, tbcd->clrBtnFace);
620     PatBlt (hdc, lpRect->left + cxEdge, lpRect->top + cyEdge,
621             cx - (2 * cxEdge), cy - (2 * cyEdge), PATCOPY);
622     SetBkColor(hdc, clrBkOld);
623     SetTextColor(hdc, clrTextOld);
624     SelectObject (hdc, hbr);
625 }
626 
627 
628 static void TOOLBAR_DrawMasked(HIMAGELIST himl, int index, HDC hdc, INT x, INT y, UINT draw_flags)
629 {
630     INT cx, cy;
631     HBITMAP hbmMask, hbmImage;
632     HDC hdcMask, hdcImage;
633 
634     ImageList_GetIconSize(himl, &cx, &cy);
635 
636     /* Create src image */
637     hdcImage = CreateCompatibleDC(hdc);
638     hbmImage = CreateCompatibleBitmap(hdc, cx, cy);
639     SelectObject(hdcImage, hbmImage);
640     ImageList_DrawEx(himl, index, hdcImage, 0, 0, cx, cy,
641                      RGB(0xff, 0xff, 0xff), RGB(0,0,0), draw_flags);
642 
643     /* Create Mask */
644     hdcMask = CreateCompatibleDC(0);
645     hbmMask = CreateBitmap(cx, cy, 1, 1, NULL);
646     SelectObject(hdcMask, hbmMask);
647 
648     /* Remove the background and all white pixels */
649     ImageList_DrawEx(himl, index, hdcMask, 0, 0, cx, cy,
650                      RGB(0xff, 0xff, 0xff), RGB(0,0,0), ILD_MASK);
651     SetBkColor(hdcImage, RGB(0xff, 0xff, 0xff));
652     BitBlt(hdcMask, 0, 0, cx, cy, hdcImage, 0, 0, NOTSRCERASE);
653 
654     /* draw the new mask 'etched' to hdc */
655     SetBkColor(hdc, RGB(255, 255, 255));
656     SelectObject(hdc, GetSysColorBrush(COLOR_3DHILIGHT));
657     /* E20746 op code is (Dst ^ (Src & (Pat ^ Dst))) */
658     BitBlt(hdc, x + 1, y + 1, cx, cy, hdcMask, 0, 0, 0xE20746);
659     SelectObject(hdc, GetSysColorBrush(COLOR_3DSHADOW));
660     BitBlt(hdc, x, y, cx, cy, hdcMask, 0, 0, 0xE20746);
661 
662     /* Cleanup */
663     DeleteObject(hbmImage);
664     DeleteDC(hdcImage);
665     DeleteObject (hbmMask);
666     DeleteDC(hdcMask);
667 }
668 
669 
670 static UINT
671 TOOLBAR_TranslateState(const TBUTTON_INFO *btnPtr)
672 {
673     UINT retstate = 0;
674 
675     retstate |= (btnPtr->fsState & TBSTATE_CHECKED) ? CDIS_CHECKED  : 0;
676     retstate |= (btnPtr->fsState & TBSTATE_PRESSED) ? CDIS_SELECTED : 0;
677     retstate |= (btnPtr->fsState & TBSTATE_ENABLED) ? 0 : CDIS_DISABLED;
678     retstate |= (btnPtr->fsState & TBSTATE_MARKED ) ? CDIS_MARKED   : 0;
679     retstate |= (btnPtr->bHot                     ) ? CDIS_HOT      : 0;
680     retstate |= ((btnPtr->fsState & (TBSTATE_ENABLED|TBSTATE_INDETERMINATE)) == (TBSTATE_ENABLED|TBSTATE_INDETERMINATE)) ? CDIS_INDETERMINATE : 0;
681     /* NOTE: we don't set CDIS_GRAYED, CDIS_FOCUS, CDIS_DEFAULT */
682     return retstate;
683 }
684 
685 /* draws the image on a toolbar button */
686 static void
687 TOOLBAR_DrawImage(const TOOLBAR_INFO *infoPtr, TBUTTON_INFO *btnPtr, INT left, INT top,
688                   const NMTBCUSTOMDRAW *tbcd, DWORD dwItemCDFlag)
689 {
690     HIMAGELIST himl = NULL;
691     BOOL draw_masked = FALSE;
692     INT index;
693     INT offset = 0;
694     UINT draw_flags = ILD_TRANSPARENT;
695 
696     if (tbcd->nmcd.uItemState & (CDIS_DISABLED | CDIS_INDETERMINATE))
697     {
698         himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_DISABLED, &index);
699         if (!himl)
700         {
701             himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_DEFAULT, &index);
702             draw_masked = TRUE;
703         }
704     }
705     else if (tbcd->nmcd.uItemState & CDIS_CHECKED ||
706       ((tbcd->nmcd.uItemState & CDIS_HOT) 
707       && ((infoPtr->dwStyle & TBSTYLE_FLAT) || GetWindowTheme (infoPtr->hwndSelf))))
708     {
709         /* if hot, attempt to draw with hot image list, if fails, 
710            use default image list */
711         himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_HOT, &index);
712         if (!himl)
713             himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_DEFAULT, &index);
714         }
715     else
716         himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_DEFAULT, &index);
717 
718     if (!himl)
719         return;
720 
721     if (!(dwItemCDFlag & TBCDRF_NOOFFSET) && 
722         (tbcd->nmcd.uItemState & (CDIS_SELECTED | CDIS_CHECKED)))
723         offset = 1;
724 
725     if (!(dwItemCDFlag & TBCDRF_NOMARK) &&
726         (tbcd->nmcd.uItemState & CDIS_MARKED))
727         draw_flags |= ILD_BLEND50;
728 
729     TRACE("drawing index=%d, himl=%p, left=%d, top=%d, offset=%d\n",
730       index, himl, left, top, offset);
731 
732     if (draw_masked)
733         TOOLBAR_DrawMasked (himl, index, tbcd->nmcd.hdc, left + offset, top + offset, draw_flags);
734     else
735         ImageList_Draw (himl, index, tbcd->nmcd.hdc, left + offset, top + offset, draw_flags);
736 }
737 
738 /* draws a blank frame for a toolbar button */
739 static void
740 TOOLBAR_DrawFrame(const TOOLBAR_INFO *infoPtr, const NMTBCUSTOMDRAW *tbcd, DWORD dwItemCDFlag)
741 {
742     HDC hdc = tbcd->nmcd.hdc;
743     RECT rc = tbcd->nmcd.rc;
744     /* if the state is disabled or indeterminate then the button
745      * cannot have an interactive look like pressed or hot */
746     BOOL non_interactive_state = (tbcd->nmcd.uItemState & CDIS_DISABLED) ||
747                                  (tbcd->nmcd.uItemState & CDIS_INDETERMINATE);
748     BOOL pressed_look = !non_interactive_state &&
749                         ((tbcd->nmcd.uItemState & CDIS_SELECTED) || 
750                          (tbcd->nmcd.uItemState & CDIS_CHECKED));
751 
752     /* app don't want us to draw any edges */
753     if (dwItemCDFlag & TBCDRF_NOEDGES)
754         return;
755 
756     if (infoPtr->dwStyle & TBSTYLE_FLAT)
757     {
758         if (pressed_look)
759             DrawEdge (hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
760         else if ((tbcd->nmcd.uItemState & CDIS_HOT) && !non_interactive_state)
761             DrawEdge (hdc, &rc, BDR_RAISEDINNER, BF_RECT);
762     }
763     else
764     {
765         if (pressed_look)
766             DrawEdge (hdc, &rc, EDGE_SUNKEN, BF_RECT | BF_MIDDLE);
767         else
768             DrawEdge (hdc, &rc, EDGE_RAISED,
769               BF_SOFT | BF_RECT | BF_MIDDLE);
770     }
771 }
772 
773 static void
774 TOOLBAR_DrawSepDDArrow(const TOOLBAR_INFO *infoPtr, const NMTBCUSTOMDRAW *tbcd, RECT *rcArrow, BOOL bDropDownPressed, DWORD dwItemCDFlag)
775 {
776     HDC hdc = tbcd->nmcd.hdc;
777     int offset = 0;
778     BOOL pressed = bDropDownPressed ||
779         (tbcd->nmcd.uItemState & (CDIS_SELECTED | CDIS_CHECKED));
780 
781     if (infoPtr->dwStyle & TBSTYLE_FLAT)
782     {
783         if (pressed)
784             DrawEdge (hdc, rcArrow, BDR_SUNKENOUTER, BF_RECT);
785         else if ( (tbcd->nmcd.uItemState & CDIS_HOT) &&
786                  !(tbcd->nmcd.uItemState & CDIS_DISABLED) &&
787                  !(tbcd->nmcd.uItemState & CDIS_INDETERMINATE))
788             DrawEdge (hdc, rcArrow, BDR_RAISEDINNER, BF_RECT);
789     }
790     else
791     {
792         if (pressed)
793             DrawEdge (hdc, rcArrow, EDGE_SUNKEN, BF_RECT | BF_MIDDLE);
794         else
795             DrawEdge (hdc, rcArrow, EDGE_RAISED,
796               BF_SOFT | BF_RECT | BF_MIDDLE);
797     }
798 
799     if (pressed)
800         offset = (dwItemCDFlag & TBCDRF_NOOFFSET) ? 0 : 1;
801 
802     if (tbcd->nmcd.uItemState & (CDIS_DISABLED | CDIS_INDETERMINATE))
803     {
804         TOOLBAR_DrawArrow(hdc, rcArrow->left+1, rcArrow->top+1 + (rcArrow->bottom - rcArrow->top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnHighlight);
805         TOOLBAR_DrawArrow(hdc, rcArrow->left, rcArrow->top + (rcArrow->bottom - rcArrow->top - ARROW_HEIGHT) / 2, comctl32_color.clr3dShadow);
806     }
807     else
808         TOOLBAR_DrawArrow(hdc, rcArrow->left + offset, rcArrow->top + offset + (rcArrow->bottom - rcArrow->top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnText);
809 }
810 
811 /* draws a complete toolbar button */
812 static void
813 TOOLBAR_DrawButton (HWND hwnd, TBUTTON_INFO *btnPtr, HDC hdc, DWORD dwBaseCustDraw)
814 {
815     TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
816     DWORD dwStyle = infoPtr->dwStyle;
817     BOOL hasDropDownArrow = (TOOLBAR_HasDropDownArrows(infoPtr->dwExStyle) &&
818                             (btnPtr->fsStyle & BTNS_DROPDOWN)) ||
819                             (btnPtr->fsStyle & BTNS_WHOLEDROPDOWN);
820     BOOL drawSepDropDownArrow = hasDropDownArrow && 
821                                 (~btnPtr->fsStyle & BTNS_WHOLEDROPDOWN);
822     RECT rc, rcArrow, rcBitmap, rcText;
823     LPWSTR lpText = NULL;
824     NMTBCUSTOMDRAW tbcd;
825     DWORD ntfret;
826     INT offset;
827     INT oldBkMode;
828     DWORD dwItemCustDraw;
829     DWORD dwItemCDFlag;
830     HTHEME theme = GetWindowTheme (hwnd);
831 
832     rc = btnPtr->rect;
833     CopyRect (&rcArrow, &rc);
834 
835     /* separator - doesn't send NM_CUSTOMDRAW */
836     if (btnPtr->fsStyle & BTNS_SEP) {
837         if (theme)
838         {
839             DrawThemeBackground (theme, hdc, 
840                 (dwStyle & CCS_VERT) ? TP_SEPARATORVERT : TP_SEPARATOR, 0, 
841                 &rc, NULL);
842         }
843         else
844         /* with the FLAT style, iBitmap is the width and has already */
845         /* been taken into consideration in calculating the width    */
846         /* so now we need to draw the vertical separator             */
847         /* empirical tests show that iBitmap can/will be non-zero    */
848         /* when drawing the vertical bar...      */
849         if ((dwStyle & TBSTYLE_FLAT) /* && (btnPtr->iBitmap == 0) */) {
850             if (btnPtr->fsStyle & BTNS_DROPDOWN)
851                 TOOLBAR_DrawDDFlatSeparator (&rc, hdc, btnPtr, infoPtr);
852             else
853                 TOOLBAR_DrawFlatSeparator (&rc, hdc, infoPtr);
854         }
855         else if (btnPtr->fsStyle != BTNS_SEP) {
856             FIXME("Draw some kind of separator: fsStyle=%x\n",
857                   btnPtr->fsStyle);
858         }
859         return;
860     }
861 
862     /* get a pointer to the text */
863     lpText = TOOLBAR_GetText(infoPtr, btnPtr);
864 
865     if (hasDropDownArrow)
866     {
867         int right;
868 
869         if (dwStyle & TBSTYLE_FLAT)
870             right = max(rc.left, rc.right - DDARROW_WIDTH);
871         else
872             right = max(rc.left, rc.right - DDARROW_WIDTH - 2);
873 
874         if (drawSepDropDownArrow)
875            rc.right = right;
876 
877         rcArrow.left = right;
878     }
879 
880     /* copy text & bitmap rects after adjusting for drop-down arrow
881      * so that text & bitmap is centered in the rectangle not containing
882      * the arrow */
883     CopyRect(&rcText, &rc);
884     CopyRect(&rcBitmap, &rc);
885 
886     /* Center the bitmap horizontally and vertically */
887     if (dwStyle & TBSTYLE_LIST)
888     {
889         if (lpText &&
890             infoPtr->nMaxTextRows > 0 &&
891             (!(infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) ||
892             (btnPtr->fsStyle & BTNS_SHOWTEXT)) )
893             rcBitmap.left += GetSystemMetrics(SM_CXEDGE) + infoPtr->szPadding.cx / 2;
894         else
895             rcBitmap.left += GetSystemMet