toolbar.c 213 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3
/*
 * Toolbar control
 *
4
 * Copyright 1998,1999 Eric Kohl
5
 * Copyright 2000 Eric Kohl for CodeWeavers
6
 * Copyright 2004 Robert Shearman
Alexandre Julliard's avatar
Alexandre Julliard committed
7
 *
8 9 10 11 12 13 14 15 16 17 18 19
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 23 24 25 26 27 28 29 30
 * NOTES
 *
 * This code was audited for completeness against the documented features
 * of Comctl32.dll version 6.0 on Mar. 14, 2004, by Robert Shearman.
 * 
 * Unless otherwise noted, we believe this code to be complete, as per
 * the specification mentioned above.
 * If you discover missing features or bugs please note them below.
 * 
Alexandre Julliard's avatar
Alexandre Julliard committed
31
 * TODO:
32 33 34
 *   - Styles:
 *     - TBSTYLE_REGISTERDROP
 *     - TBSTYLE_EX_DOUBLEBUFFER
35 36 37 38
 *   - Messages:
 *     - TB_GETMETRICS
 *     - TB_GETOBJECT
 *     - TB_INSERTMARKHITTEST
39
 *     - TB_SAVERESTORE
40
 *     - TB_SETMETRICS
41
 *     - WM_WININICHANGE
42
 *   - Notifications:
43 44 45
 *     - NM_CHAR
 *     - TBN_GETOBJECT
 *     - TBN_SAVE
46
 *   - Button wrapping (under construction).
47
 *   - Fix TB_SETROWS and Separators.
48
 *   - iListGap custom draw support.
Alexandre Julliard's avatar
Alexandre Julliard committed
49 50 51 52 53 54 55 56
 *
 * Testing:
 *   - Run tests using Waite Group Windows95 API Bible Volume 2.
 *     The second cdrom contains executables addstr.exe, btncount.exe,
 *     btnstate.exe, butstrsz.exe, chkbtn.exe, chngbmp.exe, customiz.exe,
 *     enablebtn.exe, getbmp.exe, getbtn.exe, getflags.exe, hidebtn.exe,
 *     indetbtn.exe, insbtn.exe, pressbtn.exe, setbtnsz.exe, setcmdid.exe,
 *     setparnt.exe, setrows.exe, toolwnd.exe.
57
 *   - Microsoft's controlspy examples.
58
 *   - Charles Petzold's 'Programming Windows': gadgets.exe
59 60 61 62 63 64 65 66 67 68
 *
 *  Differences between MSDN and actual native control operation:
 *   1. MSDN says: "TBSTYLE_LIST: Creates a flat toolbar with button text
 *                  to the right of the bitmap. Otherwise, this style is
 *                  identical to TBSTYLE_FLAT."
 *      As implemented by both v4.71 and v5.80 of the native COMCTL32.DLL
 *      you can create a TBSTYLE_LIST without TBSTYLE_FLAT and the result
 *      is non-flat non-transparent buttons. Therefore TBSTYLE_LIST does
 *      *not* imply TBSTYLE_FLAT as documented.  (GA 8/2001)
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
69 70
 */

71
#include <stdarg.h>
72 73
#include <string.h>

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"
Alexandre Julliard's avatar
Alexandre Julliard committed
81
#include "commctrl.h"
82
#include "comctl32.h"
83 84
#include "uxtheme.h"
#include "tmschema.h"
85
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
86

87
WINE_DEFAULT_DEBUG_CHANNEL(toolbar);
88

Robert Shearman's avatar
Robert Shearman committed
89 90
static HCURSOR hCursorDrag = NULL;

91 92 93 94 95 96
typedef struct
{
    INT iBitmap;
    INT idCommand;
    BYTE  fsState;
    BYTE  fsStyle;
97 98
    BYTE  bHot;
    BYTE  bDropDownPressed;
99
    DWORD_PTR dwData;
Frank Richter's avatar
Frank Richter committed
100
    INT_PTR iString;
101 102
    INT nRow;
    RECT rect;
Robert Shearman's avatar
Robert Shearman committed
103
    INT cx; /* manually set size */
104
} TBUTTON_INFO;
105

106 107 108 109 110 111 112
typedef struct
{
    UINT nButtons;
    HINSTANCE hInst;
    UINT nID;
} TBITMAP_INFO;

113 114 115 116 117 118
typedef struct
{
    HIMAGELIST himl;
    INT id;
} IMLENTRY, *PIMLENTRY;

119 120
typedef struct
{
121 122
    DWORD    dwStructSize;    /* size of TBBUTTON struct */
    INT      nWidth;          /* width of the toolbar */
123
    RECT     client_rect;
124
    RECT     rcBound;         /* bounding rectangle */
125 126 127 128 129 130 131 132 133 134 135 136
    INT      nButtonHeight;
    INT      nButtonWidth;
    INT      nBitmapHeight;
    INT      nBitmapWidth;
    INT      nIndent;
    INT      nRows;           /* number of button rows */
    INT      nMaxTextRows;    /* maximum number of text rows */
    INT      cxMin;           /* minimum button width */
    INT      cxMax;           /* maximum button width */
    INT      nNumButtons;     /* number of buttons */
    INT      nNumBitmaps;     /* number of bitmaps */
    INT      nNumStrings;     /* number of strings */
137
    INT      nNumBitmapInfos;
Robert Shearman's avatar
Robert Shearman committed
138 139
    INT      nButtonDown;     /* toolbar button being pressed or -1 if none */
    INT      nButtonDrag;     /* toolbar button being dragged or -1 if none */
140 141
    INT      nOldHit;
    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 149 150 151 152 153
    PIMLENTRY *himlDef;       /* default image list array */
    INT       cimlDef;        /* default image list array count */
    PIMLENTRY *himlHot;       /* hot image list array */
    INT       cimlHot;        /* hot image list array count */
    PIMLENTRY *himlDis;       /* disabled image list array */
    INT       cimlDis;        /* disabled image list array count */
154 155
    HWND     hwndToolTip;     /* handle to tool tip control */
    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 163 164
    DWORD      dwStyle;       /* regular toolbar style */
    DWORD      dwExStyle;     /* extended toolbar style */
    DWORD      dwDTFlags;     /* DrawText flags */
165 166

    COLORREF   clrInsertMark;   /* insert mark color */
167 168
    COLORREF   clrBtnHighlight; /* color for Flat Separator */
    COLORREF   clrBtnShadow;    /* color for Flag Separator */
169
    INT      iVersion;
170 171
    LPWSTR   pszTooltipText;    /* temporary store for a string > 80 characters
                                 * for TTN_GETDISPINFOW notification */
172
    TBINSERTMARK  tbim;         /* info on insertion mark */
173 174
    TBUTTON_INFO *buttons;      /* pointer to button array */
    LPWSTR       *strings;      /* pointer to string array */
175
    TBITMAP_INFO *bitmaps;
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
} TOOLBAR_INFO, *PTOOLBAR_INFO;


/* used by customization dialog */
typedef struct
{
    PTOOLBAR_INFO tbInfo;
    HWND          tbHwnd;
} CUSTDLG_INFO, *PCUSTDLG_INFO;

typedef struct
{
    TBBUTTON btn;
    BOOL     bVirtual;
    BOOL     bRemovable;
191
    WCHAR    text[64];
192 193
} CUSTOMBUTTON, *PCUSTOMBUTTON;

194 195 196 197 198 199
typedef enum
{
    IMAGE_LIST_DEFAULT,
    IMAGE_LIST_HOT,
    IMAGE_LIST_DISABLED
} IMAGE_LIST_TYPE;
200

Alexandre Julliard's avatar
Alexandre Julliard committed
201 202 203
#define SEPARATOR_WIDTH    8
#define TOP_BORDER         2
#define BOTTOM_BORDER      2
204
#define DDARROW_WIDTH      11
205
#define ARROW_HEIGHT       3
206
#define INSERTMARK_WIDTH   2
Alexandre Julliard's avatar
Alexandre Julliard committed
207

208 209
#define DEFPAD_CX 7
#define DEFPAD_CY 6
210 211 212 213
#define DEFLISTGAP 4

/* vertical padding used in list mode when image is present */
#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 219
#define TOOLBAR_NOWHERE (-1)

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 225 226 227 228 229
/* Used to find undocumented extended styles */
#define TBSTYLE_EX_ALL (TBSTYLE_EX_DRAWDDARROWS | \
                        TBSTYLE_EX_UNDOC1 | \
                        TBSTYLE_EX_MIXEDBUTTONS | \
                        TBSTYLE_EX_HIDECLIPPEDBUTTONS)

230 231 232 233
/* all of the CCS_ styles */
#define COMMON_STYLES (CCS_TOP|CCS_NOMOVEY|CCS_BOTTOM|CCS_NORESIZE| \
                       CCS_NOPARENTALIGN|CCS_ADJUSTABLE|CCS_NODIVIDER|CCS_VERT)

234 235 236 237 238 239
#define GETIBITMAP(infoPtr, i) (infoPtr->iVersion >= 5 ? LOWORD(i) : i)
#define GETHIMLID(infoPtr, i) (infoPtr->iVersion >= 5 ? HIWORD(i) : 0)
#define GETDEFIMAGELIST(infoPtr, id) TOOLBAR_GetImageList(infoPtr->himlDef, infoPtr->cimlDef, id)
#define GETHOTIMAGELIST(infoPtr, id) TOOLBAR_GetImageList(infoPtr->himlHot, infoPtr->cimlHot, id)
#define GETDISIMAGELIST(infoPtr, id) TOOLBAR_GetImageList(infoPtr->himlDis, infoPtr->cimlDis, id)

240 241
static const WCHAR themeClass[] = { 'T','o','o','l','b','a','r',0 };

242 243 244 245
static BOOL TOOLBAR_GetButtonInfo(const TOOLBAR_INFO *infoPtr, NMTOOLBARW *nmtb);
static BOOL TOOLBAR_IsButtonRemovable(const TOOLBAR_INFO *infoPtr, int iItem, PCUSTOMBUTTON btnInfo);
static HIMAGELIST TOOLBAR_GetImageList(const PIMLENTRY *pies, INT cies, INT id);
static PIMLENTRY TOOLBAR_GetImageListEntry(const PIMLENTRY *pies, INT cies, INT id);
246 247
static VOID TOOLBAR_DeleteImageList(PIMLENTRY **pies, INT *cies);
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 261
{
    return (infoPtr->dwStyle & TBSTYLE_FLAT ? 0 : TOP_BORDER);
}
262

263
static LPWSTR
264
TOOLBAR_GetText(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *btnPtr)
265 266 267
{
    LPWSTR lpText = NULL;

Robert Shearman's avatar
Robert Shearman committed
268
    /* NOTE: iString == -1 is undocumented */
269 270 271 272
    if ((HIWORD(btnPtr->iString) != 0) && (btnPtr->iString != -1))
        lpText = (LPWSTR)btnPtr->iString;
    else if ((btnPtr->iString >= 0) && (btnPtr->iString < infoPtr->nNumStrings))
        lpText = infoPtr->strings[btnPtr->iString];
273

274 275 276
    return lpText;
}

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 283
              btn_num, bP->idCommand, GETIBITMAP(infoPtr, bP->iBitmap), 
              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=(%d,%d)-(%d,%d)\n",
287 288
		  btn_num, bP->idCommand,
		  (bP->bHot) ? "TRUE":"FALSE", bP->nRow,
289
		  bP->rect.left, bP->rect.top,
290 291 292 293 294 295
		  bP->rect.right, bP->rect.bottom);
    }
}


static void
296
TOOLBAR_DumpToolbar(const TOOLBAR_INFO *iP, INT line)
297 298 299 300
{
    if (TRACE_ON(toolbar)) {
	INT i;

301
	TRACE("toolbar %p at line %d, exStyle=%08x, buttons=%d, bitmaps=%d, strings=%d, style=%08x\n",
302 303
	      iP->hwndSelf, line,
	      iP->dwExStyle, iP->nNumButtons, iP->nNumBitmaps,
304
	      iP->nNumStrings, iP->dwStyle);
305
	TRACE("toolbar %p at line %d, himlInt=%p, himlDef=%p, himlHot=%p, himlDis=%p, redrawable=%s\n",
306
	      iP->hwndSelf, line,
307 308
	      iP->himlInt, iP->himlDef, iP->himlHot, iP->himlDis,
	      (iP->bDoRedraw) ? "TRUE" : "FALSE");
309 310 311 312 313 314 315
 	for(i=0; i<iP->nNumButtons; i++) {
	    TOOLBAR_DumpButton(iP, &iP->buttons[i], i, TRUE);
	}
    }
}


316 317 318 319 320 321 322 323 324 325 326
/***********************************************************************
* 		TOOLBAR_CheckStyle
*
* This function validates that the styles set are implemented and
* issues FIXME's warning of possible problems. In a perfect world this
* function should be null.
*/
static void
TOOLBAR_CheckStyle (HWND hwnd, DWORD dwStyle)
{
    if (dwStyle & TBSTYLE_REGISTERDROP)
327
	FIXME("[%p] TBSTYLE_REGISTERDROP not implemented\n", hwnd);
328 329 330
}


331
static INT
332
TOOLBAR_SendNotify (NMHDR *nmhdr, const TOOLBAR_INFO *infoPtr, UINT code)
333
{
334 335
	if(!IsWindow(infoPtr->hwndSelf))
	    return 0;   /* we have just been destroyed */
336

337 338 339 340
    nmhdr->idFrom = GetDlgCtrlID (infoPtr->hwndSelf);
    nmhdr->hwndFrom = infoPtr->hwndSelf;
    nmhdr->code = code;

341
    TRACE("to window %p, code=%08x, %s\n", infoPtr->hwndNotify, code,
342
	  (infoPtr->bUnicode) ? "via Unicode" : "via ANSI");
343

344 345
    return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, 
        (WPARAM)nmhdr->idFrom, (LPARAM)nmhdr);
346 347 348 349 350 351 352 353 354
}

/***********************************************************************
* 		TOOLBAR_GetBitmapIndex
*
* This function returns the bitmap index associated with a button.
* If the button specifies I_IMAGECALLBACK, then the TBN_GETDISPINFO
* is issued to retrieve the index.
*/
355
static INT
356
TOOLBAR_GetBitmapIndex(const TOOLBAR_INFO *infoPtr, TBUTTON_INFO *btnPtr)
357 358 359
{
    INT ret = btnPtr->iBitmap;

360 361 362
    if (ret == I_IMAGECALLBACK)
    {
        /* issue TBN_GETDISPINFO */
363
        NMTBDISPINFOW nmgd;
364 365 366 367 368

        memset(&nmgd, 0, sizeof(nmgd));
        nmgd.idCommand = btnPtr->idCommand;
        nmgd.lParam = btnPtr->dwData;
        nmgd.dwMask = TBNF_IMAGE;
369
        nmgd.iImage = -1;
370 371
        /* Windows also send TBN_GETDISPINFOW even if the control is ANSI */
        TOOLBAR_SendNotify(&nmgd.hdr, infoPtr, TBN_GETDISPINFOW);
372 373 374
        if (nmgd.dwMask & TBNF_DI_SETITEM)
            btnPtr->iBitmap = nmgd.iImage;
        ret = nmgd.iImage;
375
        TRACE("TBN_GETDISPINFO returned bitmap id %d, mask=%08x, nNumBitmaps=%d\n",
376
            ret, nmgd.dwMask, infoPtr->nNumBitmaps);
377
    }
378 379 380 381

    if (ret != I_IMAGENONE)
        ret = GETIBITMAP(infoPtr, ret);

382 383 384 385
    return ret;
}


386
static BOOL
387
TOOLBAR_IsValidBitmapIndex(const TOOLBAR_INFO *infoPtr, INT index)
388
{
389 390 391 392 393 394 395
    HIMAGELIST himl;
    INT id = GETHIMLID(infoPtr, index);
    INT iBitmap = GETIBITMAP(infoPtr, index);

    if (((himl = GETDEFIMAGELIST(infoPtr, id)) &&
        iBitmap >= 0 && iBitmap < ImageList_GetImageCount(himl)) ||
        (index == I_IMAGECALLBACK))
396 397 398 399 400
      return TRUE;
    else
      return FALSE;
}

401

402
static inline BOOL
403
TOOLBAR_IsValidImageList(const TOOLBAR_INFO *infoPtr, INT index)
404 405 406 407 408 409
{
    HIMAGELIST himl = GETDEFIMAGELIST(infoPtr, GETHIMLID(infoPtr, index));
    return (himl != NULL) && (ImageList_GetImageCount(himl) > 0);
}


410
/***********************************************************************
Robert Shearman's avatar
Robert Shearman committed
411
* 		TOOLBAR_GetImageListForDrawing
412 413
*
* This function validates the bitmap index (including I_IMAGECALLBACK
Robert Shearman's avatar
Robert Shearman committed
414
* functionality) and returns the corresponding image list.
415
*/
Robert Shearman's avatar
Robert Shearman committed
416
static HIMAGELIST
417 418
TOOLBAR_GetImageListForDrawing (const TOOLBAR_INFO *infoPtr, TBUTTON_INFO *btnPtr,
                                IMAGE_LIST_TYPE imagelist, INT * index)
419
{
420
    HIMAGELIST himl;
421 422

    if (!TOOLBAR_IsValidBitmapIndex(infoPtr,btnPtr->iBitmap)) {
Robert Shearman's avatar
Robert Shearman committed
423
	if (btnPtr->iBitmap == I_IMAGENONE) return NULL;
424
	ERR("bitmap for ID %d, index %d is not valid, number of bitmaps in imagelist: %d\n",
Robert Shearman's avatar
Robert Shearman committed
425
	    HIWORD(btnPtr->iBitmap), LOWORD(btnPtr->iBitmap), infoPtr->nNumBitmaps);
Robert Shearman's avatar
Robert Shearman committed
426
	return NULL;
427 428
    }

Robert Shearman's avatar
Robert Shearman committed
429 430 431
    if ((*index = TOOLBAR_GetBitmapIndex(infoPtr, btnPtr)) < 0) {
	if ((*index == I_IMAGECALLBACK) ||
	    (*index == I_IMAGENONE)) return NULL;
432
	ERR("TBN_GETDISPINFO returned invalid index %d\n",
Robert Shearman's avatar
Robert Shearman committed
433 434
	    *index);
	return NULL;
435
    }
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453

    switch(imagelist)
    {
    case IMAGE_LIST_DEFAULT:
        himl = GETDEFIMAGELIST(infoPtr, GETHIMLID(infoPtr, btnPtr->iBitmap));
        break;
    case IMAGE_LIST_HOT:
        himl = GETHOTIMAGELIST(infoPtr, GETHIMLID(infoPtr, btnPtr->iBitmap));
        break;
    case IMAGE_LIST_DISABLED:
        himl = GETDISIMAGELIST(infoPtr, GETHIMLID(infoPtr, btnPtr->iBitmap));
        break;
    default:
        himl = NULL;
        FIXME("Shouldn't reach here\n");
    }

    if (!himl)
Robert Shearman's avatar
Robert Shearman committed
454
       TRACE("no image list\n");
455

Robert Shearman's avatar
Robert Shearman committed
456
    return himl;
457 458 459
}


Alexandre Julliard's avatar
Alexandre Julliard committed
460
static void
461
TOOLBAR_DrawFlatSeparator (const RECT *lpRect, HDC hdc, const TOOLBAR_INFO *infoPtr)
Alexandre Julliard's avatar
Alexandre Julliard committed
462
{
463 464
    RECT myrect;
    COLORREF oldcolor, newcolor;
Alexandre Julliard's avatar
Alexandre Julliard committed
465

466 467 468 469 470
    myrect.left = (lpRect->left + lpRect->right) / 2 - 1;
    myrect.right = myrect.left + 1;
    myrect.top = lpRect->top + 2;
    myrect.bottom = lpRect->bottom - 2;

471
    newcolor = (infoPtr->clrBtnShadow == CLR_DEFAULT) ?
472
	        comctl32_color.clrBtnShadow : infoPtr->clrBtnShadow;
473
    oldcolor = SetBkColor (hdc, newcolor);
474
    ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
475 476 477 478

    myrect.left = myrect.right;
    myrect.right = myrect.left + 1;

479
    newcolor = (infoPtr->clrBtnHighlight == CLR_DEFAULT) ?
480
	        comctl32_color.clrBtnHighlight : infoPtr->clrBtnHighlight;
481
    SetBkColor (hdc, newcolor);
482
    ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
483 484

    SetBkColor (hdc, oldcolor);
Alexandre Julliard's avatar
Alexandre Julliard committed
485 486
}

487 488 489 490

/***********************************************************************
* 		TOOLBAR_DrawDDFlatSeparator
*
491
* This function draws the separator that was flagged as BTNS_DROPDOWN.
492 493 494 495 496 497 498 499
* In this case, the separator is a pixel high line of COLOR_BTNSHADOW,
* followed by a pixel high line of COLOR_BTNHIGHLIGHT. These separators
* are horizontal as opposed to the vertical separators for not dropdown
* type.
*
* FIXME: It is possible that the height of each line is really SM_CYBORDER.
*/
static void
500 501
TOOLBAR_DrawDDFlatSeparator (const RECT *lpRect, HDC hdc, const TBUTTON_INFO *btnPtr,
                             const TOOLBAR_INFO *infoPtr)
502 503 504 505 506 507 508 509 510 511 512
{
    RECT myrect;
    COLORREF oldcolor, newcolor;

    myrect.left = lpRect->left;
    myrect.right = lpRect->right;
    myrect.top = lpRect->top + (lpRect->bottom - lpRect->top - 2)/2;
    myrect.bottom = myrect.top + 1;

    InflateRect (&myrect, -2, 0);

513
    TRACE("rect=(%d,%d)-(%d,%d)\n",
514 515
	  myrect.left, myrect.top, myrect.right, myrect.bottom);

516
    newcolor = (infoPtr->clrBtnShadow == CLR_DEFAULT) ?
517
	        comctl32_color.clrBtnShadow : infoPtr->clrBtnShadow;
518
    oldcolor = SetBkColor (hdc, newcolor);
519
    ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
520 521 522 523

    myrect.top = myrect.bottom;
    myrect.bottom = myrect.top + 1;

524
    newcolor = (infoPtr->clrBtnHighlight == CLR_DEFAULT) ?
525
	        comctl32_color.clrBtnHighlight : infoPtr->clrBtnHighlight;
526
    SetBkColor (hdc, newcolor);
527
    ExtTextOutW (hdc, 0, 0, ETO_OPAQUE, &myrect, 0, 0, 0);
528 529 530 531 532

    SetBkColor (hdc, oldcolor);
}


533
static void
Robert Shearman's avatar
Robert Shearman committed
534
TOOLBAR_DrawArrow (HDC hdc, INT left, INT top, COLORREF clr)
535 536
{
    INT x, y;
537 538
    HPEN hPen, hOldPen;

Robert Shearman's avatar
Robert Shearman committed
539
    if (!(hPen = CreatePen( PS_SOLID, 1, clr))) return;
540
    hOldPen = SelectObject ( hdc, hPen );
541
    x = left + 2;
542
    y = top;
543 544 545 546 547 548
    MoveToEx (hdc, x, y, NULL);
    LineTo (hdc, x+5, y++); x++;
    MoveToEx (hdc, x, y, NULL);
    LineTo (hdc, x+3, y++); x++;
    MoveToEx (hdc, x, y, NULL);
    LineTo (hdc, x+1, y++);
549 550
    SelectObject( hdc, hOldPen );
    DeleteObject( hPen );
551 552
}

553 554
/*
 * Draw the text string for this button.
555 556 557
 * note: infoPtr->himlDis *SHOULD* be non-zero when infoPtr->himlDef
 * 	is non-zero, so we can simply check himlDef to see if we have
 *      an image list
558
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
559
static void
560 561
TOOLBAR_DrawString (const TOOLBAR_INFO *infoPtr, RECT *rcText, LPCWSTR lpText,
                    const NMTBCUSTOMDRAW *tbcd, DWORD dwItemCDFlag)
Alexandre Julliard's avatar
Alexandre Julliard committed
562
{
Robert Shearman's avatar
Robert Shearman committed
563
    HDC hdc = tbcd->nmcd.hdc;
564 565
    HFONT  hOldFont = 0;
    COLORREF clrOld = 0;
Robert Shearman's avatar
Robert Shearman committed
566
    COLORREF clrOldBk = 0;
567
    int oldBkMode = 0;
568
    UINT state = tbcd->nmcd.uItemState;
Alexandre Julliard's avatar
Alexandre Julliard committed
569 570

    /* draw text */
571
    if (lpText) {
572
	TRACE("string=%s rect=(%d,%d)-(%d,%d)\n", debugstr_w(lpText),
573
	      rcText->left, rcText->top, rcText->right, rcText->bottom);
574

575
	hOldFont = SelectObject (hdc, infoPtr->hFont);
576
	if ((state & CDIS_HOT) && (dwItemCDFlag & TBCDRF_HILITEHOTTRACK )) {
577 578 579
	    clrOld = SetTextColor (hdc, tbcd->clrTextHighlight);
	}
	else if (state & CDIS_DISABLED) {
580 581 582
	    clrOld = SetTextColor (hdc, tbcd->clrBtnHighlight);
	    OffsetRect (rcText, 1, 1);
	    DrawTextW (hdc, lpText, -1, rcText, infoPtr->dwDTFlags);
583
	    SetTextColor (hdc, comctl32_color.clr3dShadow);
584
	    OffsetRect (rcText, -1, -1);
Alexandre Julliard's avatar
Alexandre Julliard committed
585
	}
586
	else if (state & CDIS_INDETERMINATE) {
587 588
	    clrOld = SetTextColor (hdc, comctl32_color.clr3dShadow);
	}
589
	else if ((state & CDIS_MARKED) && !(dwItemCDFlag & TBCDRF_NOMARK)) {
590
	    clrOld = SetTextColor (hdc, tbcd->clrTextHighlight);
Robert Shearman's avatar
Robert Shearman committed
591
	    clrOldBk = SetBkColor (hdc, tbcd->clrMark);
592
	    oldBkMode = SetBkMode (hdc, tbcd->nHLStringBkMode);
Robert Shearman's avatar
Robert Shearman committed
593
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
594
	else {
595
	    clrOld = SetTextColor (hdc, tbcd->clrText);
Alexandre Julliard's avatar
Alexandre Julliard committed
596 597
	}

598
	DrawTextW (hdc, lpText, -1, rcText, infoPtr->dwDTFlags);
599
	SetTextColor (hdc, clrOld);
600
	if ((state & CDIS_MARKED) && !(dwItemCDFlag & TBCDRF_NOMARK))
601
	{
Robert Shearman's avatar
Robert Shearman committed
602
	    SetBkColor (hdc, clrOldBk);
603 604
	    SetBkMode (hdc, oldBkMode);
	}
605
	SelectObject (hdc, hOldFont);
Alexandre Julliard's avatar
Alexandre Julliard committed
606 607 608 609
    }
}


Alexandre Julliard's avatar
Alexandre Julliard committed
610
static void
611
TOOLBAR_DrawPattern (const RECT *lpRect, const NMTBCUSTOMDRAW *tbcd)
Alexandre Julliard's avatar
Alexandre Julliard committed
612
{
613
    HDC hdc = tbcd->nmcd.hdc;
Robert Shearman's avatar
Robert Shearman committed
614 615 616
    HBRUSH hbr = SelectObject (hdc, tbcd->hbrMonoDither);
    COLORREF clrTextOld;
    COLORREF clrBkOld;
617 618
    INT cx = lpRect->right - lpRect->left;
    INT cy = lpRect->bottom - lpRect->top;
619 620
    INT cxEdge = GetSystemMetrics(SM_CXEDGE);
    INT cyEdge = GetSystemMetrics(SM_CYEDGE);
Robert Shearman's avatar
Robert Shearman committed
621 622
    clrTextOld = SetTextColor(hdc, tbcd->clrBtnHighlight);
    clrBkOld = SetBkColor(hdc, tbcd->clrBtnFace);
623 624
    PatBlt (hdc, lpRect->left + cxEdge, lpRect->top + cyEdge,
            cx - (2 * cxEdge), cy - (2 * cyEdge), PATCOPY);
Robert Shearman's avatar
Robert Shearman committed
625 626
    SetBkColor(hdc, clrBkOld);
    SetTextColor(hdc, clrTextOld);
627
    SelectObject (hdc, hbr);
Alexandre Julliard's avatar
Alexandre Julliard committed
628 629 630
}


631
static void TOOLBAR_DrawMasked(HIMAGELIST himl, int index, HDC hdc, INT x, INT y, UINT draw_flags)
Alexandre Julliard's avatar
Alexandre Julliard committed
632
{
633 634 635
    INT cx, cy;
    HBITMAP hbmMask, hbmImage;
    HDC hdcMask, hdcImage;
636

637 638 639 640
    ImageList_GetIconSize(himl, &cx, &cy);

    /* Create src image */
    hdcImage = CreateCompatibleDC(hdc);
641
    hbmImage = CreateCompatibleBitmap(hdc, cx, cy);
642 643 644 645 646 647 648 649 650 651
    SelectObject(hdcImage, hbmImage);
    ImageList_DrawEx(himl, index, hdcImage, 0, 0, cx, cy,
                     RGB(0xff, 0xff, 0xff), RGB(0,0,0), draw_flags);

    /* Create Mask */
    hdcMask = CreateCompatibleDC(0);
    hbmMask = CreateBitmap(cx, cy, 1, 1, NULL);
    SelectObject(hdcMask, hbmMask);

    /* Remove the background and all white pixels */
652 653
    ImageList_DrawEx(himl, index, hdcMask, 0, 0, cx, cy,
                     RGB(0xff, 0xff, 0xff), RGB(0,0,0), ILD_MASK);
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
    SetBkColor(hdcImage, RGB(0xff, 0xff, 0xff));
    BitBlt(hdcMask, 0, 0, cx, cy, hdcImage, 0, 0, NOTSRCERASE);

    /* draw the new mask 'etched' to hdc */
    SetBkColor(hdc, RGB(255, 255, 255));
    SelectObject(hdc, GetSysColorBrush(COLOR_3DHILIGHT));
    /* E20746 op code is (Dst ^ (Src & (Pat ^ Dst))) */
    BitBlt(hdc, x + 1, y + 1, cx, cy, hdcMask, 0, 0, 0xE20746);
    SelectObject(hdc, GetSysColorBrush(COLOR_3DSHADOW));
    BitBlt(hdc, x, y, cx, cy, hdcMask, 0, 0, 0xE20746);

    /* Cleanup */
    DeleteObject(hbmImage);
    DeleteDC(hdcImage);
    DeleteObject (hbmMask);
    DeleteDC(hdcMask);
Alexandre Julliard's avatar
Alexandre Julliard committed
670 671 672
}


673
static UINT
674
TOOLBAR_TranslateState(const TBUTTON_INFO *btnPtr)
675 676 677 678 679 680 681 682
{
    UINT retstate = 0;

    retstate |= (btnPtr->fsState & TBSTATE_CHECKED) ? CDIS_CHECKED  : 0;
    retstate |= (btnPtr->fsState & TBSTATE_PRESSED) ? CDIS_SELECTED : 0;
    retstate |= (btnPtr->fsState & TBSTATE_ENABLED) ? 0 : CDIS_DISABLED;
    retstate |= (btnPtr->fsState & TBSTATE_MARKED ) ? CDIS_MARKED   : 0;
    retstate |= (btnPtr->bHot                     ) ? CDIS_HOT      : 0;
683
    retstate |= ((btnPtr->fsState & (TBSTATE_ENABLED|TBSTATE_INDETERMINATE)) == (TBSTATE_ENABLED|TBSTATE_INDETERMINATE)) ? CDIS_INDETERMINATE : 0;
684
    /* NOTE: we don't set CDIS_GRAYED, CDIS_FOCUS, CDIS_DEFAULT */
685 686 687
    return retstate;
}

Robert Shearman's avatar
Robert Shearman committed
688 689
/* draws the image on a toolbar button */
static void
690 691
TOOLBAR_DrawImage(const TOOLBAR_INFO *infoPtr, TBUTTON_INFO *btnPtr, INT left, INT top,
                  const NMTBCUSTOMDRAW *tbcd, DWORD dwItemCDFlag)
Robert Shearman's avatar
Robert Shearman committed
692 693 694 695 696
{
    HIMAGELIST himl = NULL;
    BOOL draw_masked = FALSE;
    INT index;
    INT offset = 0;
697
    UINT draw_flags = ILD_TRANSPARENT;
Robert Shearman's avatar
Robert Shearman committed
698

699
    if (tbcd->nmcd.uItemState & (CDIS_DISABLED | CDIS_INDETERMINATE))
Robert Shearman's avatar
Robert Shearman committed
700 701 702
    {
        himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_DISABLED, &index);
        if (!himl)
703 704 705 706
        {
            himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_DEFAULT, &index);
            draw_masked = TRUE;
        }
Robert Shearman's avatar
Robert Shearman committed
707
    }
708 709 710
    else if (tbcd->nmcd.uItemState & CDIS_CHECKED ||
      ((tbcd->nmcd.uItemState & CDIS_HOT) 
      && ((infoPtr->dwStyle & TBSTYLE_FLAT) || GetWindowTheme (infoPtr->hwndSelf))))
Robert Shearman's avatar
Robert Shearman committed
711 712 713 714 715 716 717 718 719 720
    {
        /* if hot, attempt to draw with hot image list, if fails, 
           use default image list */
        himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_HOT, &index);
        if (!himl)
            himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_DEFAULT, &index);
	}
    else
        himl = TOOLBAR_GetImageListForDrawing(infoPtr, btnPtr, IMAGE_LIST_DEFAULT, &index);

721 722 723
    if (!himl)
        return;

724
    if (!(dwItemCDFlag & TBCDRF_NOOFFSET) && 
Robert Shearman's avatar
Robert Shearman committed
725 726 727
        (tbcd->nmcd.uItemState & (CDIS_SELECTED | CDIS_CHECKED)))
        offset = 1;

728
    if (!(dwItemCDFlag & TBCDRF_NOMARK) &&
Robert Shearman's avatar
Robert Shearman committed
729 730 731 732 733 734 735
        (tbcd->nmcd.uItemState & CDIS_MARKED))
        draw_flags |= ILD_BLEND50;

    TRACE("drawing index=%d, himl=%p, left=%d, top=%d, offset=%d\n",
      index, himl, left, top, offset);

    if (draw_masked)
736 737
        TOOLBAR_DrawMasked (himl, index, tbcd->nmcd.hdc, left + offset, top + offset, draw_flags);
    else
Robert Shearman's avatar
Robert Shearman committed
738 739 740 741 742
        ImageList_Draw (himl, index, tbcd->nmcd.hdc, left + offset, top + offset, draw_flags);
}

/* draws a blank frame for a toolbar button */
static void
743
TOOLBAR_DrawFrame(const TOOLBAR_INFO *infoPtr, const NMTBCUSTOMDRAW *tbcd, DWORD dwItemCDFlag)
Robert Shearman's avatar
Robert Shearman committed
744 745 746 747 748 749 750 751 752 753 754 755
{
    HDC hdc = tbcd->nmcd.hdc;
    RECT rc = tbcd->nmcd.rc;
    /* if the state is disabled or indeterminate then the button
     * cannot have an interactive look like pressed or hot */
    BOOL non_interactive_state = (tbcd->nmcd.uItemState & CDIS_DISABLED) ||
                                 (tbcd->nmcd.uItemState & CDIS_INDETERMINATE);
    BOOL pressed_look = !non_interactive_state &&
                        ((tbcd->nmcd.uItemState & CDIS_SELECTED) || 
                         (tbcd->nmcd.uItemState & CDIS_CHECKED));

    /* app don't want us to draw any edges */
756
    if (dwItemCDFlag & TBCDRF_NOEDGES)
Robert Shearman's avatar
Robert Shearman committed
757 758
        return;

759
    if (infoPtr->dwStyle & TBSTYLE_FLAT)
Robert Shearman's avatar
Robert Shearman committed
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
    {
        if (pressed_look)
            DrawEdge (hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
        else if ((tbcd->nmcd.uItemState & CDIS_HOT) && !non_interactive_state)
            DrawEdge (hdc, &rc, BDR_RAISEDINNER, BF_RECT);
    }
    else
    {
        if (pressed_look)
            DrawEdge (hdc, &rc, EDGE_SUNKEN, BF_RECT | BF_MIDDLE);
        else
            DrawEdge (hdc, &rc, EDGE_RAISED,
              BF_SOFT | BF_RECT | BF_MIDDLE);
    }
}

static void
777
TOOLBAR_DrawSepDDArrow(const TOOLBAR_INFO *infoPtr, const NMTBCUSTOMDRAW *tbcd, RECT *rcArrow, BOOL bDropDownPressed, DWORD dwItemCDFlag)
Robert Shearman's avatar
Robert Shearman committed
778 779 780
{
    HDC hdc = tbcd->nmcd.hdc;
    int offset = 0;
781 782
    BOOL pressed = bDropDownPressed ||
        (tbcd->nmcd.uItemState & (CDIS_SELECTED | CDIS_CHECKED));
Robert Shearman's avatar
Robert Shearman committed
783

784
    if (infoPtr->dwStyle & TBSTYLE_FLAT)
Robert Shearman's avatar
Robert Shearman committed
785
    {
786
        if (pressed)
787
            DrawEdge (hdc, rcArrow, BDR_SUNKENOUTER, BF_RECT);
Robert Shearman's avatar
Robert Shearman committed
788 789 790 791 792 793 794
        else if ( (tbcd->nmcd.uItemState & CDIS_HOT) &&
                 !(tbcd->nmcd.uItemState & CDIS_DISABLED) &&
                 !(tbcd->nmcd.uItemState & CDIS_INDETERMINATE))
            DrawEdge (hdc, rcArrow, BDR_RAISEDINNER, BF_RECT);
    }
    else
    {
795
        if (pressed)
796
            DrawEdge (hdc, rcArrow, EDGE_SUNKEN, BF_RECT | BF_MIDDLE);
Robert Shearman's avatar
Robert Shearman committed
797 798
        else
            DrawEdge (hdc, rcArrow, EDGE_RAISED,
799
              BF_SOFT | BF_RECT | BF_MIDDLE);
Robert Shearman's avatar
Robert Shearman committed
800 801
    }

802
    if (pressed)
803
        offset = (dwItemCDFlag & TBCDRF_NOOFFSET) ? 0 : 1;
Robert Shearman's avatar
Robert Shearman committed
804 805 806 807 808 809 810 811 812

    if (tbcd->nmcd.uItemState & (CDIS_DISABLED | CDIS_INDETERMINATE))
    {
        TOOLBAR_DrawArrow(hdc, rcArrow->left+1, rcArrow->top+1 + (rcArrow->bottom - rcArrow->top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnHighlight);
        TOOLBAR_DrawArrow(hdc, rcArrow->left, rcArrow->top + (rcArrow->bottom - rcArrow->top - ARROW_HEIGHT) / 2, comctl32_color.clr3dShadow);
    }
    else
        TOOLBAR_DrawArrow(hdc, rcArrow->left + offset, rcArrow->top + offset + (rcArrow->bottom - rcArrow->top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnText);
}
813

Robert Shearman's avatar
Robert Shearman committed
814
/* draws a complete toolbar button */
Alexandre Julliard's avatar
Alexandre Julliard committed
815
static void
816
TOOLBAR_DrawButton (HWND hwnd, TBUTTON_INFO *btnPtr, HDC hdc, DWORD dwBaseCustDraw)
Alexandre Julliard's avatar
Alexandre Julliard committed
817
{
818
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
819
    DWORD dwStyle = infoPtr->dwStyle;
820 821 822 823
    BOOL hasDropDownArrow = (TOOLBAR_HasDropDownArrows(infoPtr->dwExStyle) &&
                            (btnPtr->fsStyle & BTNS_DROPDOWN)) ||
                            (btnPtr->fsStyle & BTNS_WHOLEDROPDOWN);
    BOOL drawSepDropDownArrow = hasDropDownArrow && 
Robert Shearman's avatar
Robert Shearman committed
824 825
                                (~btnPtr->fsStyle & BTNS_WHOLEDROPDOWN);
    RECT rc, rcArrow, rcBitmap, rcText;
826 827 828 829
    LPWSTR lpText = NULL;
    NMTBCUSTOMDRAW tbcd;
    DWORD ntfret;
    INT offset;
830
    INT oldBkMode;
831
    DWORD dwItemCustDraw;
832
    DWORD dwItemCDFlag;
833
    HTHEME theme = GetWindowTheme (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
834 835

    rc = btnPtr->rect;
836 837
    CopyRect (&rcArrow, &rc);

838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
    /* separator - doesn't send NM_CUSTOMDRAW */
    if (btnPtr->fsStyle & BTNS_SEP) {
        if (theme)
        {
            DrawThemeBackground (theme, hdc, 
                (dwStyle & CCS_VERT) ? TP_SEPARATORVERT : TP_SEPARATOR, 0, 
                &rc, NULL);
        }
        else
        /* with the FLAT style, iBitmap is the width and has already */
        /* been taken into consideration in calculating the width    */
        /* so now we need to draw the vertical separator             */
        /* empirical tests show that iBitmap can/will be non-zero    */
        /* when drawing the vertical bar...      */
        if ((dwStyle & TBSTYLE_FLAT) /* && (btnPtr->iBitmap == 0) */) {
	    if (btnPtr->fsStyle & BTNS_DROPDOWN)
		TOOLBAR_DrawDDFlatSeparator (&rc, hdc, btnPtr, infoPtr);
	    else
		TOOLBAR_DrawFlatSeparator (&rc, hdc, infoPtr);
	}
	else if (btnPtr->fsStyle != BTNS_SEP) {
	    FIXME("Draw some kind of separator: fsStyle=%x\n",
		  btnPtr->fsStyle);
	}
	return;
    }

865 866
    /* get a pointer to the text */
    lpText = TOOLBAR_GetText(infoPtr, btnPtr);
867 868 869

    if (hasDropDownArrow)
    {
870 871
        int right;

Robert Shearman's avatar
Robert Shearman committed
872
        if (dwStyle & TBSTYLE_FLAT)
873
            right = max(rc.left, rc.right - DDARROW_WIDTH);
Robert Shearman's avatar
Robert Shearman committed
874
        else
875 876 877 878 879
            right = max(rc.left, rc.right - DDARROW_WIDTH - 2);

        if (drawSepDropDownArrow)
           rc.right = right;

Robert Shearman's avatar
Robert Shearman committed
880
        rcArrow.left = right;
881
    }
882

883 884
    /* copy text & bitmap rects after adjusting for drop-down arrow
     * so that text & bitmap is centred in the rectangle not containing
885 886
     * the arrow */
    CopyRect(&rcText, &rc);
887
    CopyRect(&rcBitmap, &rc);
888

889
    /* Center the bitmap horizontally and vertically */
890
    if (dwStyle & TBSTYLE_LIST)
891 892 893 894 895 896 897 898 899
    {
        if (lpText &&
            infoPtr->nMaxTextRows > 0 &&
            (!(infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) ||
            (btnPtr->fsStyle & BTNS_SHOWTEXT)) )
            rcBitmap.left += GetSystemMetrics(SM_CXEDGE) + infoPtr->szPadding.cx / 2;
        else
            rcBitmap.left += GetSystemMetrics(SM_CXEDGE) + infoPtr->iListGap / 2;
    }
900
    else
901
        rcBitmap.left += ((rc.right - rc.left) - infoPtr->nBitmapWidth) / 2;
902

903
    rcBitmap.top += infoPtr->szPadding.cy / 2;
904

905
    TRACE("iBitmap=%d, start=(%d,%d) w=%d, h=%d\n",
Robert Shearman's avatar
Robert Shearman committed
906 907
      btnPtr->iBitmap, rcBitmap.left, rcBitmap.top,
      infoPtr->nBitmapWidth, infoPtr->nBitmapHeight);
908
    TRACE("Text=%s\n", debugstr_w(lpText));
909
    TRACE("iListGap=%d, padding = { %d, %d }\n", infoPtr->iListGap, infoPtr->szPadding.cx, infoPtr->szPadding.cy);
910

911 912 913 914 915 916
    /* calculate text position */
    if (lpText)
    {
        rcText.left += GetSystemMetrics(SM_CXEDGE);
        rcText.right -= GetSystemMetrics(SM_CXEDGE);
        if (dwStyle & TBSTYLE_LIST)
Robert Shearman's avatar
Robert Shearman committed
917
        {
918 919
            if (TOOLBAR_IsValidBitmapIndex(infoPtr,btnPtr->iBitmap))
                rcText.left += infoPtr->nBitmapWidth + infoPtr->iListGap + 2;
Robert Shearman's avatar
Robert Shearman committed
920 921
        }
        else
922 923 924 925 926 927
        {
            if (ImageList_GetImageCount(GETDEFIMAGELIST(infoPtr, 0)) > 0)
                rcText.top += infoPtr->szPadding.cy/2 + infoPtr->nBitmapHeight + 1;
            else
                rcText.top += infoPtr->szPadding.cy/2 + 2;
        }
928 929
    }

Robert Shearman's avatar
Robert Shearman committed
930 931 932
    /* Initialize fields in all cases, because we use these later
     * NOTE: applications can and do alter these to customize their
     * toolbars */
933 934 935 936 937 938 939
    ZeroMemory (&tbcd, sizeof(NMTBCUSTOMDRAW));
    tbcd.clrText = comctl32_color.clrBtnText;
    tbcd.clrTextHighlight = comctl32_color.clrHighlightText;
    tbcd.clrBtnFace = comctl32_color.clrBtnFace;
    tbcd.clrBtnHighlight = comctl32_color.clrBtnHighlight;
    tbcd.clrMark = comctl32_color.clrHighlight;
    tbcd.clrHighlightHotTrack = 0;
940 941
    tbcd.nStringBkMode = TRANSPARENT;
    tbcd.nHLStringBkMode = OPAQUE;
Robert Shearman's avatar
Robert Shearman committed
942 943 944 945 946
    /* MSDN says that this is the text rectangle.
     * But (why always a but) tracing of v5.7 of native shows
     * that this is really a *relative* rectangle based on the
     * the nmcd.rc. Also the left and top are always 0 ignoring
     * any bitmap that might be present. */
947 948 949 950
    tbcd.rcText.left = 0;
    tbcd.rcText.top = 0;
    tbcd.rcText.right = rcText.right - rc.left;
    tbcd.rcText.bottom = rcText.bottom - rc.top;
951
    tbcd.nmcd.uItemState = TOOLBAR_TranslateState(btnPtr);
952
    tbcd.nmcd.hdc = hdc;
Robert Shearman's avatar
Robert Shearman committed
953
    tbcd.nmcd.rc = rc;
954
    tbcd.hbrMonoDither = COMCTL32_hPattern55AABrush;
955

Robert Shearman's avatar
Robert Shearman committed
956
    /* FIXME: what are these used for? */
957 958 959
    tbcd.hbrLines = 0;
    tbcd.hpenLines = 0;

960
    /* Issue Item Prepaint notify */
961
    dwItemCustDraw = 0;
962 963
    dwItemCDFlag = 0;
    if (dwBaseCustDraw & CDRF_NOTIFYITEMDRAW)
964 965 966 967
    {
	tbcd.nmcd.dwDrawStage = CDDS_ITEMPREPAINT;
	tbcd.nmcd.dwItemSpec = btnPtr->idCommand;
	tbcd.nmcd.lItemlParam = btnPtr->dwData;
968
	ntfret = TOOLBAR_SendNotify(&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
Robert Shearman's avatar
Robert Shearman committed
969 970 971 972
        /* reset these fields so the user can't alter the behaviour like native */
        tbcd.nmcd.hdc = hdc;
        tbcd.nmcd.rc = rc;

973
	dwItemCustDraw = ntfret & 0xffff;
974
	dwItemCDFlag = ntfret & 0xffff0000;
975
	if (dwItemCustDraw & CDRF_SKIPDEFAULT)
976 977 978 979 980 981
	    return;
	/* save the only part of the rect that the user can change */
	rcText.right = tbcd.rcText.right + rc.left;
	rcText.bottom = tbcd.rcText.bottom + rc.top;
    }

982
    if (!(dwItemCDFlag & TBCDRF_NOOFFSET) &&
983 984 985
        (btnPtr->fsState & (TBSTATE_PRESSED | TBSTATE_CHECKED)))
        OffsetRect(&rcText, 1, 1);

Robert Shearman's avatar
Robert Shearman committed
986 987 988 989
    if (!(tbcd.nmcd.uItemState & CDIS_HOT) && 
        ((tbcd.nmcd.uItemState & CDIS_CHECKED) || (tbcd.nmcd.uItemState & CDIS_INDETERMINATE)))
        TOOLBAR_DrawPattern (&rc, &tbcd);

990 991
    if (((infoPtr->dwStyle & TBSTYLE_FLAT) || GetWindowTheme (infoPtr->hwndSelf)) 
        && (tbcd.nmcd.uItemState & CDIS_HOT))
992
    {
993
        if ( dwItemCDFlag & TBCDRF_HILITEHOTTRACK )
Robert Shearman's avatar
Robert Shearman committed
994
        {
995 996 997
            COLORREF oldclr;

            oldclr = SetBkColor(hdc, tbcd.clrHighlightHotTrack);
998
            ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, 0);
999
            if (hasDropDownArrow)
1000
                ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rcArrow, NULL, 0, 0);
1001 1002 1003
            SetBkColor(hdc, oldclr);
        }
    }
1004

1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
    if (theme)
    {
        int partId = drawSepDropDownArrow ? TP_SPLITBUTTON : TP_BUTTON;
        int stateId = TS_NORMAL;
        
        if (tbcd.nmcd.uItemState & CDIS_DISABLED)
            stateId = TS_DISABLED;
        else if (tbcd.nmcd.uItemState & CDIS_SELECTED)
            stateId = TS_PRESSED;
        else if (tbcd.nmcd.uItemState & CDIS_CHECKED)
            stateId = (tbcd.nmcd.uItemState & CDIS_HOT) ? TS_HOTCHECKED : TS_HOT;
        else if ((tbcd.nmcd.uItemState & CDIS_HOT)
            || (drawSepDropDownArrow && btnPtr->bDropDownPressed))
            stateId = TS_HOT;
            
        DrawThemeBackground (theme, hdc, partId, stateId, &tbcd.nmcd.rc, NULL);
    }
    else
1023
        TOOLBAR_DrawFrame(infoPtr, &tbcd, dwItemCDFlag);
1024

Robert Shearman's avatar
Robert Shearman committed
1025
    if (drawSepDropDownArrow)
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
    {
        if (theme)
        {
            int stateId = TS_NORMAL;
            
            if (tbcd.nmcd.uItemState & CDIS_DISABLED)
                stateId = TS_DISABLED;
            else if (btnPtr->bDropDownPressed || (tbcd.nmcd.uItemState & CDIS_SELECTED))
                stateId = TS_PRESSED;
            else if (tbcd.nmcd.uItemState & CDIS_CHECKED)
                stateId = (tbcd.nmcd.uItemState & CDIS_HOT) ? TS_HOTCHECKED : TS_HOT;
            else if (tbcd.nmcd.uItemState & CDIS_HOT)
                stateId = TS_HOT;
                
            DrawThemeBackground (theme, hdc, TP_DROPDOWNBUTTON, stateId, &rcArrow, NULL);
            DrawThemeBackground (theme, hdc, TP_SPLITBUTTONDROPDOWN, stateId, &rcArrow, NULL);
        }
        else
1044
            TOOLBAR_DrawSepDDArrow(infoPtr, &tbcd, &rcArrow, btnPtr->bDropDownPressed, dwItemCDFlag);
1045
    }
1046

1047
    oldBkMode = SetBkMode (hdc, tbcd.nStringBkMode);
Robert Shearman's avatar
Robert Shearman committed
1048
    if (!(infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) || (btnPtr->fsStyle & BTNS_SHOWTEXT))
1049
        TOOLBAR_DrawString (infoPtr, &rcText, lpText, &tbcd, dwItemCDFlag);
1050
    SetBkMode (hdc, oldBkMode);
Alexandre Julliard's avatar
Alexandre Julliard committed
1051

1052
    TOOLBAR_DrawImage(infoPtr, btnPtr, rcBitmap.left, rcBitmap.top, &tbcd, dwItemCDFlag);
Alexandre Julliard's avatar
Alexandre Julliard committed
1053

Robert Shearman's avatar
Robert Shearman committed
1054
    if (hasDropDownArrow && !drawSepDropDownArrow)
1055
    {
Robert Shearman's avatar
Robert Shearman committed
1056 1057 1058 1059 1060 1061 1062
        if (tbcd.nmcd.uItemState & (CDIS_DISABLED | CDIS_INDETERMINATE))
        {
            TOOLBAR_DrawArrow(hdc, rcArrow.left+1, rcArrow.top+1 + (rcArrow.bottom - rcArrow.top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnHighlight);
            TOOLBAR_DrawArrow(hdc, rcArrow.left, rcArrow.top + (rcArrow.bottom - rcArrow.top - ARROW_HEIGHT) / 2, comctl32_color.clr3dShadow);
        }
        else if (tbcd.nmcd.uItemState & (CDIS_SELECTED | CDIS_CHECKED))
        {
1063
            offset = (dwItemCDFlag & TBCDRF_NOOFFSET) ? 0 : 1;
Robert Shearman's avatar
Robert Shearman committed
1064 1065 1066 1067
            TOOLBAR_DrawArrow(hdc, rcArrow.left + offset, rcArrow.top + offset + (rcArrow.bottom - rcArrow.top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnText);
        }
        else
            TOOLBAR_DrawArrow(hdc, rcArrow.left, rcArrow.top + (rcArrow.bottom - rcArrow.top - ARROW_HEIGHT) / 2, comctl32_color.clrBtnText);
1068
    }
1069

1070
    if (dwItemCustDraw & CDRF_NOTIFYPOSTPAINT)
1071
    {
1072 1073
        tbcd.nmcd.dwDrawStage = CDDS_ITEMPOSTPAINT;
        TOOLBAR_SendNotify(&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
1074
    }
1075

Alexandre Julliard's avatar
Alexandre Julliard committed
1076 1077 1078 1079
}


static void
1080
TOOLBAR_Refresh (HWND hwnd, HDC hdc, const PAINTSTRUCT *ps)
Alexandre Julliard's avatar
Alexandre Julliard committed
1081
{
1082
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
1083
    TBUTTON_INFO *btnPtr;
1084
    INT i;
1085
    RECT rcTemp, rcClient;
1086 1087
    NMTBCUSTOMDRAW tbcd;
    DWORD ntfret;
1088
    DWORD dwBaseCustDraw;
Alexandre Julliard's avatar
Alexandre Julliard committed
1089

Robert Shearman's avatar
Robert Shearman committed
1090 1091 1092 1093
    /* the app has told us not to redraw the toolbar */
    if (!infoPtr->bDoRedraw)
        return;

1094 1095
    /* if imagelist belongs to the app, it can be changed
       by the app after setting it */
1096 1097 1098 1099 1100 1101
    if (GETDEFIMAGELIST(infoPtr, 0) != infoPtr->himlInt)
    {
        infoPtr->nNumBitmaps = 0;
        for (i = 0; i < infoPtr->cimlDef; i++)
            infoPtr->nNumBitmaps += ImageList_GetImageCount(infoPtr->himlDef[i]->himl);
    }
1102 1103 1104

    TOOLBAR_DumpToolbar (infoPtr, __LINE__);

1105 1106 1107
    /* change the imagelist icon size if we manage the list and it is necessary */
    TOOLBAR_CheckImageListIconSize(infoPtr);

1108 1109 1110 1111 1112
    /* Send initial notify */
    ZeroMemory (&tbcd, sizeof(NMTBCUSTOMDRAW));
    tbcd.nmcd.dwDrawStage = CDDS_PREPAINT;
    tbcd.nmcd.hdc = hdc;
    tbcd.nmcd.rc = ps->rcPaint;
1113
    ntfret = TOOLBAR_SendNotify(&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
1114
    dwBaseCustDraw = ntfret & 0xffff;
1115

1116 1117
    GetClientRect(hwnd, &rcClient);

1118
    /* redraw necessary buttons */
Alexandre Julliard's avatar
Alexandre Julliard committed
1119
    btnPtr = infoPtr->buttons;
Alexandre Julliard's avatar
Alexandre Julliard committed
1120
    for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++)
1121
    {
1122
        BOOL bDraw;
1123 1124
        if (!RectVisible(hdc, &btnPtr->rect))
            continue;
1125 1126 1127 1128 1129 1130 1131 1132
        if (infoPtr->dwExStyle & TBSTYLE_EX_HIDECLIPPEDBUTTONS)
        {
            IntersectRect(&rcTemp, &rcClient, &btnPtr->rect);
            bDraw = EqualRect(&rcTemp, &btnPtr->rect);
        }
        else
            bDraw = TRUE;
        bDraw &= IntersectRect(&rcTemp, &(ps->rcPaint), &(btnPtr->rect));
Robert Shearman's avatar
Robert Shearman committed
1133
        bDraw = (btnPtr->fsState & TBSTATE_HIDDEN) ? FALSE : bDraw;
1134
        if (bDraw)
1135
            TOOLBAR_DrawButton(hwnd, btnPtr, hdc, dwBaseCustDraw);
1136
    }
1137

1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
    /* draw insert mark if required */
    if (infoPtr->tbim.iButton != -1)
    {
        RECT rcButton = infoPtr->buttons[infoPtr->tbim.iButton].rect;
        RECT rcInsertMark;
        rcInsertMark.top = rcButton.top;
        rcInsertMark.bottom = rcButton.bottom;
        if (infoPtr->tbim.dwFlags & TBIMHT_AFTER)
            rcInsertMark.left = rcInsertMark.right = rcButton.right;
        else
            rcInsertMark.left = rcInsertMark.right = rcButton.left - INSERTMARK_WIDTH;
        COMCTL32_DrawInsertMark(hdc, &rcInsertMark, infoPtr->clrInsertMark, FALSE);
    }

1152
    if (dwBaseCustDraw & CDRF_NOTIFYPOSTPAINT)
1153 1154 1155 1156 1157
    {
	ZeroMemory (&tbcd, sizeof(NMTBCUSTOMDRAW));
	tbcd.nmcd.dwDrawStage = CDDS_POSTPAINT;
	tbcd.nmcd.hdc = hdc;
	tbcd.nmcd.rc = ps->rcPaint;
1158
	ntfret = TOOLBAR_SendNotify(&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
1159
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1160 1161
}

1162 1163 1164 1165 1166 1167 1168 1169 1170 1171
/***********************************************************************
* 		TOOLBAR_MeasureString
*
* This function gets the width and height of a string in pixels. This
* is done first by using GetTextExtentPoint to get the basic width
* and height. The DrawText is called with DT_CALCRECT to get the exact
* width. The reason is because the text may have more than one "&" (or
* prefix characters as M$ likes to call them). The prefix character
* indicates where the underline goes, except for the string "&&" which
* is reduced to a single "&". GetTextExtentPoint does not process these
1172
* only DrawText does. Note that the BTNS_NOPREFIX is handled here.
1173
*/
Alexandre Julliard's avatar
Alexandre Julliard committed
1174
static void
1175
TOOLBAR_MeasureString(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *btnPtr,
1176
		      HDC hdc, LPSIZE lpSize)
Alexandre Julliard's avatar
Alexandre Julliard committed
1177
{
1178
    RECT myrect;
1179

Alexandre Julliard's avatar
Alexandre Julliard committed
1180 1181
    lpSize->cx = 0;
    lpSize->cy = 0;
1182

1183 1184
    if (infoPtr->nMaxTextRows > 0 &&
        !(btnPtr->fsState & TBSTATE_HIDDEN) &&
1185 1186
        (!(infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) ||
        (btnPtr->fsStyle & BTNS_SHOWTEXT)) )
1187
    {
1188
        LPWSTR lpText = TOOLBAR_GetText(infoPtr, btnPtr);
1189

1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200
	if(lpText != NULL) {
	    /* first get size of all the text */
	    GetTextExtentPoint32W (hdc, lpText, strlenW (lpText), lpSize);

	    /* feed above size into the rectangle for DrawText */
	    myrect.left = myrect.top = 0;
	    myrect.right = lpSize->cx;
	    myrect.bottom = lpSize->cy;

	    /* Use DrawText to get true size as drawn (less pesky "&") */
	    DrawTextW (hdc, lpText, -1, &myrect, DT_VCENTER | DT_SINGLELINE |
1201
	    	   DT_CALCRECT | ((btnPtr->fsStyle & BTNS_NOPREFIX) ?
1202 1203
				  DT_NOPREFIX : 0));

1204 1205 1206 1207
	    /* feed back to caller  */
	    lpSize->cx = myrect.right;
	    lpSize->cy = myrect.bottom;
	}
1208
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1209

1210
    TRACE("string size %d x %d!\n", lpSize->cx, lpSize->cy);
Alexandre Julliard's avatar
Alexandre Julliard committed
1211 1212
}

1213 1214 1215 1216 1217 1218
/***********************************************************************
* 		TOOLBAR_CalcStrings
*
* This function walks through each string and measures it and returns
* the largest height and width to caller.
*/
1219 1220 1221 1222 1223 1224 1225
static void
TOOLBAR_CalcStrings (HWND hwnd, LPSIZE lpSize)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    TBUTTON_INFO *btnPtr;
    INT i;
    SIZE sz;
1226 1227
    HDC hdc;
    HFONT hOldFont;
1228 1229 1230 1231

    lpSize->cx = 0;
    lpSize->cy = 0;

1232
    if (infoPtr->nMaxTextRows == 0)
Huw Davies's avatar
Huw Davies committed
1233 1234
        return;

1235 1236 1237
    hdc = GetDC (hwnd);
    hOldFont = SelectObject (hdc, infoPtr->hFont);

1238
    if (infoPtr->nNumButtons == 0 && infoPtr->nNumStrings > 0)
1239 1240 1241 1242 1243 1244 1245
    {
        TEXTMETRICW tm;

        GetTextMetricsW(hdc, &tm);
        lpSize->cy = tm.tmHeight;
    }

1246 1247
    btnPtr = infoPtr->buttons;
    for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++) {
1248 1249
        if(TOOLBAR_HasText(infoPtr, btnPtr))
        {
1250
            TOOLBAR_MeasureString(infoPtr, btnPtr, hdc, &sz);
1251 1252 1253 1254 1255
            if (sz.cx > lpSize->cx)
                lpSize->cx = sz.cx;
            if (sz.cy > lpSize->cy)
                lpSize->cy = sz.cy;
        }
1256 1257
    }

1258 1259 1260
    SelectObject (hdc, hOldFont);
    ReleaseDC (hwnd, hdc);

1261
    TRACE("max string size %d x %d!\n", lpSize->cx, lpSize->cy);
1262 1263
}

1264 1265 1266
/***********************************************************************
* 		TOOLBAR_WrapToolbar
*
Francois Gouget's avatar
Francois Gouget committed
1267
* This function walks through the buttons and separators in the
1268 1269 1270 1271 1272
* toolbar, and sets the TBSTATE_WRAP flag only on those items where
* wrapping should occur based on the width of the toolbar window.
* It does *not* calculate button placement itself.  That task
* takes place in TOOLBAR_CalcToolbar. If the program wants to manage
* the toolbar wrapping on its own, it can use the TBSTYLE_WRAPABLE
1273
* flag, and set the TBSTATE_WRAP flags manually on the appropriate items.
1274
*
1275 1276 1277
* Note: TBSTYLE_WRAPABLE or TBSTYLE_EX_UNDOC1 can be used also to allow
* vertical toolbar lists.
*/
Alexandre Julliard's avatar
Alexandre Julliard committed
1278

Alexandre Julliard's avatar
Alexandre Julliard committed
1279
static void
1280
TOOLBAR_WrapToolbar( HWND hwnd, DWORD dwStyle )
Alexandre Julliard's avatar
Alexandre Julliard committed
1281
{
1282
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
1283 1284 1285 1286 1287
    TBUTTON_INFO *btnPtr;
    INT x, cx, i, j;
    RECT rc;
    BOOL bWrap, bButtonWrap;

1288
    /* 	When the toolbar window style is not TBSTYLE_WRAPABLE,	*/
1289 1290
    /*	no layout is necessary. Applications may use this style */
    /*	to perform their own layout on the toolbar. 		*/
1291
    if( !(dwStyle & TBSTYLE_WRAPABLE) &&
1292
	!(infoPtr->dwExStyle & TBSTYLE_EX_UNDOC1) )  return;
1293 1294 1295 1296

    btnPtr = infoPtr->buttons;
    x  = infoPtr->nIndent;

1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311
    if (GetParent(hwnd))
    {
        /* this can get the parents width, to know how far we can extend
         * this toolbar.  We cannot use its height, as there may be multiple
         * toolbars in a rebar control
         */
        GetClientRect( GetParent(hwnd), &rc );
        infoPtr->nWidth = rc.right - rc.left;
    }
    else
    {
        GetWindowRect( hwnd, &rc );
        infoPtr->nWidth = rc.right - rc.left;
    }

1312 1313
    bButtonWrap = FALSE;

1314 1315 1316 1317
    TRACE("start ButtonWidth=%d, BitmapWidth=%d, nWidth=%d, nIndent=%d\n",
	  infoPtr->nButtonWidth, infoPtr->nBitmapWidth, infoPtr->nWidth,
	  infoPtr->nIndent);

1318 1319 1320 1321
    for (i = 0; i < infoPtr->nNumButtons; i++ )
    {
	bWrap = FALSE;
	btnPtr[i].fsState &= ~TBSTATE_WRAP;
1322

1323 1324 1325 1326 1327 1328
	if (btnPtr[i].fsState & TBSTATE_HIDDEN)
	    continue;

	/* UNDOCUMENTED: If a separator has a non zero bitmap index, */
	/* it is the actual width of the separator. This is used for */
	/* custom controls in toolbars.                              */
1329
	/*                                                           */
1330
	/* BTNS_DROPDOWN separators are treated as buttons for    */
1331
	/* width.  - GA 8/01                                         */
1332 1333
	if ((btnPtr[i].fsStyle & BTNS_SEP) &&
	    !(btnPtr[i].fsStyle & BTNS_DROPDOWN))
1334
	    cx = (btnPtr[i].iBitmap > 0) ?
1335 1336 1337 1338
			btnPtr[i].iBitmap : SEPARATOR_WIDTH;
	else
	    cx = infoPtr->nButtonWidth;

1339
	/* Two or more adjacent separators form a separator group.   */
1340 1341 1342
	/* The first separator in a group should be wrapped to the   */
	/* next row if the previous wrapping is on a button.	     */
	if( bButtonWrap &&
1343
		(btnPtr[i].fsStyle & BTNS_SEP) &&
1344
		(i + 1 < infoPtr->nNumButtons ) &&
1345
		(btnPtr[i + 1].fsStyle & BTNS_SEP) )
1346
	{
1347
	    TRACE("wrap point 1 btn %d style %02x\n", i, btnPtr[i].fsStyle);
1348 1349 1350 1351 1352 1353 1354 1355
	    btnPtr[i].fsState |= TBSTATE_WRAP;
	    x = infoPtr->nIndent;
	    i++;
	    bButtonWrap = FALSE;
	    continue;
	}

	/* The layout makes sure the bitmap is visible, but not the button. */
1356 1357
	/* Test added to also wrap after a button that starts a row but     */
	/* is bigger than the area.  - GA  8/01                             */
1358
	if (( x + cx - (infoPtr->nButtonWidth - infoPtr->nBitmapWidth) / 2
1359 1360
	   > infoPtr->nWidth ) ||
	    ((x == infoPtr->nIndent) && (cx > infoPtr->nWidth)))
1361 1362 1363
	{
	    BOOL bFound = FALSE;

1364
	    /* 	If the current button is a separator and not hidden,  */
1365 1366
	    /*	go to the next until it reaches a non separator.      */
	    /*	Wrap the last separator if it is before a button.     */
1367 1368
	    while( ( ((btnPtr[i].fsStyle & BTNS_SEP) &&
		      !(btnPtr[i].fsStyle & BTNS_DROPDOWN)) ||
1369
		     (btnPtr[i].fsState & TBSTATE_HIDDEN) ) &&
1370 1371 1372 1373 1374
			i < infoPtr->nNumButtons )
	    {
		i++;
		bFound = TRUE;
	    }
1375

1376 1377 1378
	    if( bFound && i < infoPtr->nNumButtons )
	    {
		i--;
1379
		TRACE("wrap point 2 btn %d style %02x, x=%d, cx=%d\n",
1380
		      i, btnPtr[i].fsStyle, x, cx);
1381 1382 1383 1384 1385 1386 1387 1388
		btnPtr[i].fsState |= TBSTATE_WRAP;
		x = infoPtr->nIndent;
		bButtonWrap = FALSE;
		continue;
	    }
	    else if ( i >= infoPtr->nNumButtons)
		break;

1389
	    /* 	If the current button is not a separator, find the last  */
1390 1391 1392
	    /*	separator and wrap it.   				 */
	    for ( j = i - 1; j >= 0  &&  !(btnPtr[j].fsState & TBSTATE_WRAP); j--)
	    {
1393
		if ((btnPtr[j].fsStyle & BTNS_SEP) &&
1394 1395
			!(btnPtr[j].fsState & TBSTATE_HIDDEN))
		{
1396 1397 1398
		    bFound = TRUE;
		    i = j;
		    TRACE("wrap point 3 btn %d style %02x, x=%d, cx=%d\n",
1399
			  i, btnPtr[i].fsStyle, x, cx);
1400 1401
		    x = infoPtr->nIndent;
		    btnPtr[j].fsState |= TBSTATE_WRAP;
1402
		    bButtonWrap = FALSE;
1403 1404 1405 1406 1407 1408 1409 1410
		    break;
		}
	    }

	    /* 	If no separator available for wrapping, wrap one of 	*/
	    /*  non-hidden previous button.  			     	*/
	    if (!bFound)
	    {
1411
		for ( j = i - 1;
1412 1413
			j >= 0 && !(btnPtr[j].fsState & TBSTATE_WRAP); j--)
		{
1414
		    if (btnPtr[j].fsState & TBSTATE_HIDDEN)
1415 1416
			continue;

1417 1418 1419
		    bFound = TRUE;
		    i = j;
		    TRACE("wrap point 4 btn %d style %02x, x=%d, cx=%d\n",
1420
			  i, btnPtr[i].fsStyle, x, cx);
1421 1422 1423 1424 1425 1426 1427 1428
		    x = infoPtr->nIndent;
		    btnPtr[j].fsState |= TBSTATE_WRAP;
		    bButtonWrap = TRUE;
		    break;
		}
	    }

	    /* If all above failed, wrap the current button. */
1429
	    if (!bFound)
1430
	    {
1431 1432
		TRACE("wrap point 5 btn %d style %02x, x=%d, cx=%d\n",
		      i, btnPtr[i].fsStyle, x, cx);
1433 1434 1435
		btnPtr[i].fsState |= TBSTATE_WRAP;
		bFound = TRUE;
		x = infoPtr->nIndent;
1436
		if (btnPtr[i].fsStyle & BTNS_SEP )
1437 1438 1439
		    bButtonWrap = FALSE;
		else
		    bButtonWrap = TRUE;
1440
	    }
1441
	}
1442
	else {
1443
	    TRACE("wrap point 6 btn %d style %02x, x=%d, cx=%d\n",
1444
		  i, btnPtr[i].fsStyle, x, cx);
1445
	    x += cx;
1446
	}
1447 1448
    }
}
1449 1450


1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558
/***********************************************************************
* 		TOOLBAR_MeasureButton
*
* Calculates the width and height required for a button. Used in
* TOOLBAR_CalcToolbar to set the all-button width and height and also for
* the width of buttons that are autosized.
*
* Note that it would have been rather elegant to use one piece of code for
* both the laying out of the toolbar and for controlling where button parts
* are drawn, but the native control has inconsistencies between the two that
* prevent this from being effectively. These inconsistencies can be seen as
* artefacts where parts of the button appear outside of the bounding button
* rectangle.
*
* There are several cases for the calculation of the button dimensions and
* button part positioning:
*
* List
* ====
*
* With Bitmap:
*
* +--------------------------------------------------------+ ^
* |                    ^                     ^             | |
* |                    | pad.cy / 2          | centred     | |
* | pad.cx/2 + cxedge +--------------+     +------------+  | | DEFPAD_CY +
* |<----------------->| nBitmapWidth |     | Text       |  | | max(nBitmapHeight, szText.cy)
* |                   |<------------>|     |            |  | |
* |                   +--------------+     +------------+  | |
* |<-------------------------------------->|               | |
* |  cxedge + iListGap + nBitmapWidth + 2  |<----------->  | |
* |                                           szText.cx    | |
* +--------------------------------------------------------+ -
* <-------------------------------------------------------->
*  2*cxedge + nBitmapWidth + iListGap + szText.cx + pad.cx
*
* Without Bitmap (I_IMAGENONE):
*
* +-----------------------------------+ ^
* |                     ^             | |
* |                     | centred     | | LISTPAD_CY +
* |                   +------------+  | | szText.cy
* |                   | Text       |  | |
* |                   |            |  | |
* |                   +------------+  | |
* |<----------------->|               | |
* |      cxedge       |<----------->  | |
* |                      szText.cx    | |
* +-----------------------------------+ -
* <----------------------------------->
*          szText.cx + pad.cx
*
* Without text:
*
* +--------------------------------------+ ^
* |                       ^              | |
* |                       | padding.cy/2 | | DEFPAD_CY +
* |                     +------------+   | | nBitmapHeight
* |                     | Bitmap     |   | |
* |                     |            |   | |
* |                     +------------+   | |
* |<------------------->|                | |
* | cxedge + iListGap/2 |<----------->   | |
* |                       nBitmapWidth   | |
* +--------------------------------------+ -
* <-------------------------------------->
*     2*cxedge + nBitmapWidth + iListGap
*
* Non-List
* ========
*
* With bitmap:
*
* +-----------------------------------+ ^
* |                     ^             | |
* |                     | pad.cy / 2  | | nBitmapHeight +
* |                     -             | | szText.cy +
* |                   +------------+  | | DEFPAD_CY + 1
* |    centred        |   Bitmap   |  | |
* |<----------------->|            |  | |
* |                   +------------+  | |
* |                         ^         | |
* |                       1 |         | |
* |                         -         | |
* |     centred     +---------------+ | |
* |<--------------->|      Text     | | |
* |                 +---------------+ | |
* +-----------------------------------+ -
* <----------------------------------->
* pad.cx + max(nBitmapWidth, szText.cx)
*
* Without bitmaps (NULL imagelist or ImageList_GetImageCount() = 0):
*
* +---------------------------------------+ ^
* |                     ^                 | |
* |                     | 2 + pad.cy / 2  | |
* |                     -                 | | szText.cy +
* |    centred      +-----------------+   | | pad.cy + 2
* |<--------------->|   Text          |   | |
* |                 +-----------------+   | |
* |                                       | |
* +---------------------------------------+ -
* <--------------------------------------->
*          2*cxedge + pad.cx + szText.cx
*
* Without text:
*   As for with bitmaps, but with szText.cx zero.
*/
1559 1560
static inline SIZE TOOLBAR_MeasureButton(const TOOLBAR_INFO *infoPtr, SIZE sizeString,
                                         BOOL bHasBitmap, BOOL bValidImageList)
1561 1562 1563 1564 1565
{
    SIZE sizeButton;
    if (infoPtr->dwStyle & TBSTYLE_LIST)
    {
        /* set button height from bitmap / text height... */
1566
        sizeButton.cy = max((bHasBitmap ? infoPtr->nBitmapHeight : 0),
1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594
            sizeString.cy);

        /* ... add on the necessary padding */
        if (bValidImageList)
        {
            if (bHasBitmap)
                sizeButton.cy += DEFPAD_CY;
            else
                sizeButton.cy += LISTPAD_CY;
        }
        else
            sizeButton.cy += infoPtr->szPadding.cy;

        /* calculate button width */
        if (bHasBitmap)
        {
            sizeButton.cx = 2*GetSystemMetrics(SM_CXEDGE) +
                infoPtr->nBitmapWidth + infoPtr->iListGap;
            if (sizeString.cx > 0)
                sizeButton.cx += sizeString.cx + infoPtr->szPadding.cx;
        }
        else
            sizeButton.cx = sizeString.cx + infoPtr->szPadding.cx;
    }
    else
    {
        if (bHasBitmap)
        {
1595
            sizeButton.cy = infoPtr->nBitmapHeight + DEFPAD_CY;
1596 1597
            if (sizeString.cy > 0)
                sizeButton.cy += 1 + sizeString.cy;
1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612
            sizeButton.cx = infoPtr->szPadding.cx +
                max(sizeString.cx, infoPtr->nBitmapWidth);
        }
        else
        {
            sizeButton.cy = sizeString.cy + infoPtr->szPadding.cy +
                NONLIST_NOTEXT_OFFSET;
            sizeButton.cx = 2*GetSystemMetrics(SM_CXEDGE) +
                infoPtr->szPadding.cx + sizeString.cx;
        }
    }
    return sizeButton;
}


1613 1614 1615
/***********************************************************************
* 		TOOLBAR_CalcToolbar
*
1616 1617 1618
* This function calculates button and separator placement. It first
* calculates the button sizes, gets the toolbar window width and then
* calls TOOLBAR_WrapToolbar to determine which buttons we need to wrap
1619
* on. It assigns a new location to each item and sends this location to
1620 1621 1622
* the tooltip window if appropriate. Finally, it updates the rcBound
* rect and calculates the new required toolbar window height.
*/
1623 1624 1625 1626
static void
TOOLBAR_CalcToolbar (HWND hwnd)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr(hwnd);
1627 1628
    SIZE  sizeString, sizeButton;
    BOOL validImageList = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1629

1630
    TOOLBAR_CalcStrings (hwnd, &sizeString);
Alexandre Julliard's avatar
Alexandre Julliard committed
1631

1632 1633
    TOOLBAR_DumpToolbar (infoPtr, __LINE__);

1634 1635
    if (TOOLBAR_IsValidImageList(infoPtr, 0))
        validImageList = TRUE;
1636
    sizeButton = TOOLBAR_MeasureButton(infoPtr, sizeString, TRUE, validImageList);
1637 1638
    infoPtr->nButtonWidth = sizeButton.cx;
    infoPtr->nButtonHeight = sizeButton.cy;
1639
    infoPtr->iTopMargin = default_top_margin(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1640

1641 1642
    if ( infoPtr->cxMin >= 0 && infoPtr->nButtonWidth < infoPtr->cxMin )
        infoPtr->nButtonWidth = infoPtr->cxMin;
1643
    if ( infoPtr->cxMax > 0 && infoPtr->nButtonWidth > infoPtr->cxMax )
1644 1645
        infoPtr->nButtonWidth = infoPtr->cxMax;

1646 1647
    TOOLBAR_LayoutToolbar(hwnd);
}
1648

1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661
static void
TOOLBAR_LayoutToolbar(HWND hwnd)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr(hwnd);
    TBUTTON_INFO *btnPtr;
    SIZE sizeButton;
    INT i, nRows, nSepRows;
    INT x, y, cx, cy;
    BOOL bWrap;
    BOOL validImageList = TOOLBAR_IsValidImageList(infoPtr, 0);
    BOOL hasDropDownArrows = TOOLBAR_HasDropDownArrows(infoPtr->dwExStyle);

    TOOLBAR_WrapToolbar(hwnd, infoPtr->dwStyle);
1662

1663
    x  = infoPtr->nIndent;
1664
    y  = infoPtr->iTopMargin;
Alexandre Julliard's avatar
Alexandre Julliard committed
1665
    cx = infoPtr->nButtonWidth;
1666
    cy = infoPtr->nButtonHeight;
1667

1668
    nRows = nSepRows = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1669 1670 1671 1672 1673

    infoPtr->rcBound.top = y;
    infoPtr->rcBound.left = x;
    infoPtr->rcBound.bottom = y + cy;
    infoPtr->rcBound.right = x;
Alexandre Julliard's avatar
Alexandre Julliard committed
1674 1675

    btnPtr = infoPtr->buttons;
1676

1677 1678
    TRACE("cy=%d\n", cy);

1679 1680 1681 1682 1683
    for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++ )
    {
	bWrap = FALSE;
	if (btnPtr->fsState & TBSTATE_HIDDEN)
	{
1684
	    SetRectEmpty (&btnPtr->rect);
Alexandre Julliard's avatar
Alexandre Julliard committed
1685
	    continue;
1686 1687
	}

1688
	cy = infoPtr->nButtonHeight;
1689 1690 1691 1692

	/* UNDOCUMENTED: If a separator has a non zero bitmap index, */
	/* it is the actual width of the separator. This is used for */
	/* custom controls in toolbars.                              */
1693 1694
	if (btnPtr->fsStyle & BTNS_SEP) {
	    if (btnPtr->fsStyle & BTNS_DROPDOWN) {
1695 1696 1697 1698 1699
		cy = (btnPtr->iBitmap > 0) ?
		     btnPtr->iBitmap : SEPARATOR_WIDTH;
		cx = infoPtr->nButtonWidth;
	    }
	    else
1700 1701
		cx = (btnPtr->iBitmap > 0) ?
		     btnPtr->iBitmap : SEPARATOR_WIDTH;
1702
	}
1703 1704
	else
	{
Robert Shearman's avatar
Robert Shearman committed
1705 1706 1707
            if (btnPtr->cx)
              cx = btnPtr->cx;
            else if ((infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) || 
1708
                (btnPtr->fsStyle & BTNS_AUTOSIZE))
1709 1710
            {
              SIZE sz;
1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721
	      HDC hdc;
	      HFONT hOldFont;

	      hdc = GetDC (hwnd);
	      hOldFont = SelectObject (hdc, infoPtr->hFont);

              TOOLBAR_MeasureString(infoPtr, btnPtr, hdc, &sz);

	      SelectObject (hdc, hOldFont);
	      ReleaseDC (hwnd, hdc);

1722 1723 1724 1725
              sizeButton = TOOLBAR_MeasureButton(infoPtr, sz,
                  TOOLBAR_IsValidBitmapIndex(infoPtr, infoPtr->buttons[i].iBitmap),
                  validImageList);
              cx = sizeButton.cx;
1726 1727 1728
            }
            else
	      cx = infoPtr->nButtonWidth;
1729

Robert Shearman's avatar
Robert Shearman committed
1730 1731 1732 1733
            /* if size has been set manually then don't add on extra space
             * for the drop down arrow */
	    if (!btnPtr->cx && hasDropDownArrows && 
                ((btnPtr->fsStyle & BTNS_DROPDOWN) || (btnPtr->fsStyle & BTNS_WHOLEDROPDOWN)))
1734
	      cx += DDARROW_WIDTH;
1735
	}
1736
	if (btnPtr->fsState & TBSTATE_WRAP )
1737 1738
		    bWrap = TRUE;

1739
	SetRect (&btnPtr->rect, x, y, x + cx, y + cy);
Alexandre Julliard's avatar
Alexandre Julliard committed
1740

Alexandre Julliard's avatar
Alexandre Julliard committed
1741 1742 1743 1744 1745 1746
	if (infoPtr->rcBound.left > x)
	    infoPtr->rcBound.left = x;
	if (infoPtr->rcBound.right < x + cx)
	    infoPtr->rcBound.right = x + cx;
	if (infoPtr->rcBound.bottom < y + cy)
	    infoPtr->rcBound.bottom = y + cy;
Alexandre Julliard's avatar
Alexandre Julliard committed
1747

1748
        TOOLBAR_TooltipSetRect(infoPtr, btnPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1749

1750 1751 1752
	/* btnPtr->nRow is zero based. The space between the rows is 	*/
	/* also considered as a row. 					*/
	btnPtr->nRow = nRows + nSepRows;
1753

1754 1755 1756
	TRACE("button %d style=%x, bWrap=%d, nRows=%d, nSepRows=%d, btnrow=%d, (%d,%d)-(%d,%d)\n",
	      i, btnPtr->fsStyle, bWrap, nRows, nSepRows, btnPtr->nRow,
	      x, y, x+cx, y+cy);
1757

1758 1759
	if( bWrap )
	{
1760
	    if ( !(btnPtr->fsStyle & BTNS_SEP) )
1761
	        y += cy;
1762 1763
	    else
	    {
1764 1765 1766
		/* UNDOCUMENTED: If a separator has a non zero bitmap index, */
		/* it is the actual width of the separator. This is used for */
		/* custom controls in toolbars. 			     */
1767
		if ( !(btnPtr->fsStyle & BTNS_DROPDOWN))
1768
		    y += cy + ( (btnPtr->iBitmap > 0 ) ?
1769
				btnPtr->iBitmap : SEPARATOR_WIDTH) * 2 /3;
1770 1771
		else
		    y += cy;
1772

1773
		/* nSepRows is used to calculate the extra height follwoing  */
1774 1775 1776 1777
		/* the last row.					     */
		nSepRows++;
	    }
	    x = infoPtr->nIndent;
1778 1779 1780 1781

	    /* Increment row number unless this is the last button    */
	    /* and it has Wrap set.                                   */
	    if (i != infoPtr->nNumButtons-1)
Alexandre Julliard's avatar
Alexandre Julliard committed
1782 1783
		nRows++;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1784
	else
Alexandre Julliard's avatar
Alexandre Julliard committed
1785
	    x += cx;
Alexandre Julliard's avatar
Alexandre Julliard committed
1786
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1787

1788 1789 1790
    /* infoPtr->nRows is the number of rows on the toolbar */
    infoPtr->nRows = nRows + nSepRows + 1;

1791
    TRACE("toolbar button width %d\n", infoPtr->nButtonWidth);
Alexandre Julliard's avatar
Alexandre Julliard committed
1792 1793 1794
}


1795
static INT
1796
TOOLBAR_InternalHitTest (HWND hwnd, const POINT *lpPt)
Alexandre Julliard's avatar
Alexandre Julliard committed
1797
{
1798
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
1799
    TBUTTON_INFO *btnPtr;
1800
    INT i;
1801

Alexandre Julliard's avatar
Alexandre Julliard committed
1802
    btnPtr = infoPtr->buttons;
Alexandre Julliard's avatar
Alexandre Julliard committed
1803
    for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++) {
Alexandre Julliard's avatar
Alexandre Julliard committed
1804 1805 1806
	if (btnPtr->fsState & TBSTATE_HIDDEN)
	    continue;

1807
	if (btnPtr->fsStyle & BTNS_SEP) {
1808
	    if (PtInRect (&btnPtr->rect, *lpPt)) {
1809
		TRACE(" ON SEPARATOR %d!\n", i);
Alexandre Julliard's avatar
Alexandre Julliard committed
1810 1811 1812 1813
		return -i;
	    }
	}
	else {
1814
	    if (PtInRect (&btnPtr->rect, *lpPt)) {
1815
		TRACE(" ON BUTTON %d!\n", i);
Alexandre Julliard's avatar
Alexandre Julliard committed
1816 1817 1818 1819 1820
		return i;
	    }
	}
    }

1821
    TRACE(" NOWHERE!\n");
1822
    return TOOLBAR_NOWHERE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1823 1824 1825
}


1826
static INT
1827
TOOLBAR_GetButtonIndex (const TOOLBAR_INFO *infoPtr, INT idCommand, BOOL CommandIsIndex)
Alexandre Julliard's avatar
Alexandre Julliard committed
1828 1829
{
    TBUTTON_INFO *btnPtr;
1830
    INT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
1831

1832 1833
    if (CommandIsIndex) {
	TRACE("command is really index command=%d\n", idCommand);
1834
	if (idCommand >= infoPtr->nNumButtons) return -1;
1835 1836
	return idCommand;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1837
    btnPtr = infoPtr->buttons;
Alexandre Julliard's avatar
Alexandre Julliard committed
1838
    for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++) {
Alexandre Julliard's avatar
Alexandre Julliard committed
1839
	if (btnPtr->idCommand == idCommand) {
1840
	    TRACE("command=%d index=%d\n", idCommand, i);
Alexandre Julliard's avatar
Alexandre Julliard committed
1841
	    return i;
Alexandre Julliard's avatar
Alexandre Julliard committed
1842
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1843
    }
1844
    TRACE("no index found for command=%d\n", idCommand);
Alexandre Julliard's avatar
Alexandre Julliard committed
1845 1846 1847 1848
    return -1;
}


1849
static INT
1850
TOOLBAR_GetCheckedGroupButtonIndex (const TOOLBAR_INFO *infoPtr, INT nIndex)
Alexandre Julliard's avatar
Alexandre Julliard committed
1851 1852
{
    TBUTTON_INFO *btnPtr;
1853
    INT nRunIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
1854 1855 1856 1857 1858 1859

    if ((nIndex < 0) || (nIndex > infoPtr->nNumButtons))
	return -1;

    /* check index button */
    btnPtr = &infoPtr->buttons[nIndex];
1860
    if ((btnPtr->fsStyle & BTNS_CHECKGROUP) == BTNS_CHECKGROUP) {
Alexandre Julliard's avatar
Alexandre Julliard committed
1861 1862 1863 1864 1865 1866 1867 1868
	if (btnPtr->fsState & TBSTATE_CHECKED)
	    return nIndex;
    }

    /* check previous buttons */
    nRunIndex = nIndex - 1;
    while (nRunIndex >= 0) {
	btnPtr = &infoPtr->buttons[nRunIndex];
1869
	if ((btnPtr->fsStyle & BTNS_GROUP) == BTNS_GROUP) {
Alexandre Julliard's avatar
Alexandre Julliard committed
1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880
	    if (btnPtr->fsState & TBSTATE_CHECKED)
		return nRunIndex;
	}
	else
	    break;
	nRunIndex--;
    }

    /* check next buttons */
    nRunIndex = nIndex + 1;
    while (nRunIndex < infoPtr->nNumButtons) {
1881
	btnPtr = &infoPtr->buttons[nRunIndex];
1882
	if ((btnPtr->fsStyle & BTNS_GROUP) == BTNS_GROUP) {
Alexandre Julliard's avatar
Alexandre Julliard committed
1883 1884 1885 1886 1887 1888 1889 1890
	    if (btnPtr->fsState & TBSTATE_CHECKED)
		return nRunIndex;
	}
	else
	    break;
	nRunIndex++;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1891 1892 1893 1894
    return -1;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1895
static VOID
1896 1897
TOOLBAR_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
		    WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
1898
{
1899
    MSG msg;
Alexandre Julliard's avatar
Alexandre Julliard committed
1900 1901 1902 1903 1904 1905

    msg.hwnd = hwndMsg;
    msg.message = uMsg;
    msg.wParam = wParam;
    msg.lParam = lParam;
    msg.time = GetMessageTime ();
1906 1907
    msg.pt.x = (short)LOWORD(GetMessagePos ());
    msg.pt.y = (short)HIWORD(GetMessagePos ());
Alexandre Julliard's avatar
Alexandre Julliard committed
1908

1909
    SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
Alexandre Julliard's avatar
Alexandre Julliard committed
1910 1911
}

1912
static void
1913
TOOLBAR_TooltipAddTool(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *button)
1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931
{
    if (infoPtr->hwndToolTip && !(button->fsStyle & BTNS_SEP)) {
        TTTOOLINFOW ti;

        ZeroMemory(&ti, sizeof(TTTOOLINFOW));
        ti.cbSize   = sizeof (TTTOOLINFOW);
        ti.hwnd     = infoPtr->hwndSelf;
        ti.uId      = button->idCommand;
        ti.hinst    = 0;
        ti.lpszText = LPSTR_TEXTCALLBACKW;
        /* ti.lParam = random value from the stack? */

        SendMessageW(infoPtr->hwndToolTip, TTM_ADDTOOLW,
            0, (LPARAM)&ti);
    }
}

static void
1932
TOOLBAR_TooltipDelTool(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *button)
1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945
{
    if ((infoPtr->hwndToolTip) && !(button->fsStyle & BTNS_SEP)) {
        TTTOOLINFOW ti;

        ZeroMemory(&ti, sizeof(ti));
        ti.cbSize   = sizeof(ti);
        ti.hwnd     = infoPtr->hwndSelf;
        ti.uId      = button->idCommand;

        SendMessageW(infoPtr->hwndToolTip, TTM_DELTOOLW, 0, (LPARAM)&ti);
    }
}

1946
static void TOOLBAR_TooltipSetRect(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *button)
1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961
{
    /* Set the toolTip only for non-hidden, non-separator button */
    if (infoPtr->hwndToolTip && !(button->fsStyle & BTNS_SEP))
    {
        TTTOOLINFOW ti;

        ZeroMemory(&ti, sizeof(ti));
        ti.cbSize = sizeof(ti);
        ti.hwnd = infoPtr->hwndSelf;
        ti.uId = button->idCommand;
        ti.rect = button->rect;
        SendMessageW(infoPtr->hwndToolTip, TTM_NEWTOOLRECTW, 0, (LPARAM)&ti);
    }
}

1962 1963 1964 1965 1966 1967 1968
/* Creates the tooltip control */
static void
TOOLBAR_TooltipCreateControl(TOOLBAR_INFO *infoPtr)
{
    int i;
    NMTOOLTIPSCREATED nmttc;

1969
    infoPtr->hwndToolTip = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, WS_POPUP,
1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986
            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
            infoPtr->hwndSelf, 0, 0, 0);

    if (!infoPtr->hwndToolTip)
        return;

    /* Send NM_TOOLTIPSCREATED notification */
    nmttc.hwndToolTips = infoPtr->hwndToolTip;
    TOOLBAR_SendNotify(&nmttc.hdr, infoPtr, NM_TOOLTIPSCREATED);

    for (i = 0; i < infoPtr->nNumButtons; i++)
    {
        TOOLBAR_TooltipAddTool(infoPtr, &infoPtr->buttons[i]);
        TOOLBAR_TooltipSetRect(infoPtr, &infoPtr->buttons[i]);
    }
}

1987 1988 1989 1990 1991 1992 1993 1994
/* keeps available button list box sorted by button id */
static void TOOLBAR_Cust_InsertAvailButton(HWND hwnd, PCUSTOMBUTTON btnInfoNew)
{
    int i;
    int count;
    PCUSTOMBUTTON btnInfo;
    HWND hwndAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);

Robert Shearman's avatar
Robert Shearman committed
1995
    TRACE("button %s, idCommand %d\n", debugstr_w(btnInfoNew->text), btnInfoNew->btn.idCommand);
1996

1997
    count = SendMessageW(hwndAvail, LB_GETCOUNT, 0, 0);
1998 1999 2000 2001

    /* position 0 is always separator */
    for (i = 1; i < count; i++)
    {
2002
        btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndAvail, LB_GETITEMDATA, i, 0);
2003 2004
        if (btnInfoNew->btn.idCommand < btnInfo->btn.idCommand)
        {
2005 2006
            i = SendMessageW(hwndAvail, LB_INSERTSTRING, i, 0);
            SendMessageW(hwndAvail, LB_SETITEMDATA, i, (LPARAM)btnInfoNew);
2007 2008 2009 2010
            return;
        }
    }
    /* id higher than all others add to end */
2011 2012
    i = SendMessageW(hwndAvail, LB_ADDSTRING, 0, 0);
    SendMessageW(hwndAvail, LB_SETITEMDATA, i, (LPARAM)btnInfoNew);
2013 2014
}

2015
static void TOOLBAR_Cust_MoveButton(const CUSTDLG_INFO *custInfo, HWND hwnd, INT nIndexFrom, INT nIndexTo)
2016
{
2017
    NMTOOLBARW nmtb;
2018 2019 2020 2021 2022 2023

	TRACE("index from %d, index to %d\n", nIndexFrom, nIndexTo);

    if (nIndexFrom == nIndexTo)
        return;

2024 2025 2026
    /* MSDN states that iItem is the index of the button, rather than the
     * command ID as used by every other NMTOOLBAR notification */
    nmtb.iItem = nIndexFrom;
2027
    if (TOOLBAR_SendNotify(&nmtb.hdr, custInfo->tbInfo, TBN_QUERYINSERT))
2028 2029 2030 2031
    {
        PCUSTOMBUTTON btnInfo;
        NMHDR hdr;
        HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);
2032
        int count = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2033

2034
        btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndList, LB_GETITEMDATA, nIndexFrom, 0);
2035

2036 2037 2038 2039
        SendMessageW(hwndList, LB_DELETESTRING, nIndexFrom, 0);
        SendMessageW(hwndList, LB_INSERTSTRING, nIndexTo, 0);
        SendMessageW(hwndList, LB_SETITEMDATA, nIndexTo, (LPARAM)btnInfo);
        SendMessageW(hwndList, LB_SETCURSEL, nIndexTo, 0);
2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051

        if (nIndexTo <= 0)
            EnableWindow(GetDlgItem(hwnd,IDC_MOVEUP_BTN), FALSE);
        else
            EnableWindow(GetDlgItem(hwnd,IDC_MOVEUP_BTN), TRUE);

        /* last item is always separator, so -2 instead of -1 */
        if (nIndexTo >= (count - 2))
            EnableWindow(GetDlgItem(hwnd,IDC_MOVEDN_BTN), FALSE);
        else
            EnableWindow(GetDlgItem(hwnd,IDC_MOVEDN_BTN), TRUE);

2052
        SendMessageW(custInfo->tbHwnd, TB_DELETEBUTTON, nIndexFrom, 0);
Robert Shearman's avatar
Robert Shearman committed
2053
        SendMessageW(custInfo->tbHwnd, TB_INSERTBUTTONW, nIndexTo, (LPARAM)&(btnInfo->btn));
2054 2055 2056 2057 2058

        TOOLBAR_SendNotify(&hdr, custInfo->tbInfo, TBN_TOOLBARCHANGE);
    }
}

2059
static void TOOLBAR_Cust_AddButton(const CUSTDLG_INFO *custInfo, HWND hwnd, INT nIndexAvail, INT nIndexTo)
2060
{
2061
    NMTOOLBARW nmtb;
2062 2063 2064

    TRACE("Add: nIndexAvail %d, nIndexTo %d\n", nIndexAvail, nIndexTo);

2065 2066 2067
    /* MSDN states that iItem is the index of the button, rather than the
     * command ID as used by every other NMTOOLBAR notification */
    nmtb.iItem = nIndexAvail;
2068
    if (TOOLBAR_SendNotify(&nmtb.hdr, custInfo->tbInfo, TBN_QUERYINSERT))
2069 2070 2071 2072 2073
    {
        PCUSTOMBUTTON btnInfo;
        NMHDR hdr;
        HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);
        HWND hwndAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
2074
        int count = SendMessageW(hwndAvail, LB_GETCOUNT, 0, 0);
2075

2076
        btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndAvail, LB_GETITEMDATA, nIndexAvail, 0);
2077 2078 2079 2080

        if (nIndexAvail != 0) /* index == 0 indicates separator */
        {
            /* remove from 'available buttons' list */
2081
            SendMessageW(hwndAvail, LB_DELETESTRING, nIndexAvail, 0);
2082
            if (nIndexAvail == count-1)
2083
                SendMessageW(hwndAvail, LB_SETCURSEL, nIndexAvail-1 , 0);
2084
            else
2085
                SendMessageW(hwndAvail, LB_SETCURSEL, nIndexAvail , 0);
2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097
        }
        else
        {
            PCUSTOMBUTTON btnNew;

            /* duplicate 'separator' button */
            btnNew = (PCUSTOMBUTTON)Alloc(sizeof(CUSTOMBUTTON));
            memcpy(btnNew, btnInfo, sizeof(CUSTOMBUTTON));
            btnInfo = btnNew;
        }

        /* insert into 'toolbar button' list */
2098 2099
        SendMessageW(hwndList, LB_INSERTSTRING, nIndexTo, 0);
        SendMessageW(hwndList, LB_SETITEMDATA, nIndexTo, (LPARAM)btnInfo);
2100

2101
        SendMessageW(custInfo->tbHwnd, TB_INSERTBUTTONW, nIndexTo, (LPARAM)&(btnInfo->btn));
2102 2103 2104 2105 2106

        TOOLBAR_SendNotify(&hdr, custInfo->tbInfo, TBN_TOOLBARCHANGE);
    }
}

2107
static void TOOLBAR_Cust_RemoveButton(const CUSTDLG_INFO *custInfo, HWND hwnd, INT index)
2108 2109 2110 2111 2112 2113
{
    PCUSTOMBUTTON btnInfo;
    HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);

    TRACE("Remove: index %d\n", index);

2114
    btnInfo = (PCUSTOMBUTTON)SendMessageW(hwndList, LB_GETITEMDATA, index, 0);
2115 2116 2117 2118 2119 2120

    /* send TBN_QUERYDELETE notification */
    if (TOOLBAR_IsButtonRemovable(custInfo->tbInfo, index, btnInfo))
    {
        NMHDR hdr;

2121 2122
        SendMessageW(hwndList, LB_DELETESTRING, index, 0);
        SendMessageW(hwndList, LB_SETCURSEL, index , 0);
2123

2124
        SendMessageW(custInfo->tbHwnd, TB_DELETEBUTTON, index, 0);
2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136

        /* insert into 'available button' list */
        if (!(btnInfo->btn.fsStyle & BTNS_SEP))
            TOOLBAR_Cust_InsertAvailButton(hwnd, btnInfo);
        else
            Free(btnInfo);

        TOOLBAR_SendNotify(&hdr, custInfo->tbInfo, TBN_TOOLBARCHANGE);
    }
}

/* drag list notification function for toolbar buttons list box */
2137 2138
static LRESULT TOOLBAR_Cust_ToolbarDragListNotification(const CUSTDLG_INFO *custInfo, HWND hwnd,
                                                        const DRAGLISTINFO *pDLI)
2139 2140 2141 2142 2143 2144 2145
{
    HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);
    switch (pDLI->uNotification)
    {
    case DL_BEGINDRAG:
    {
        INT nCurrentItem = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2146
        INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2147 2148 2149 2150 2151 2152 2153
        /* no dragging for last item (separator) */
        if (nCurrentItem >= (nCount - 1)) return FALSE;
        return TRUE;
    }
    case DL_DRAGGING:
    {
        INT nCurrentItem = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2154
        INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180
        /* no dragging past last item (separator) */
        if ((nCurrentItem >= 0) && (nCurrentItem < (nCount - 1)))
        {
            DrawInsert(hwnd, hwndList, nCurrentItem);
            /* FIXME: native uses "move button" cursor */
            return DL_COPYCURSOR;
        }

        /* not over toolbar buttons list */
        if (nCurrentItem < 0)
        {
            POINT ptWindow = pDLI->ptCursor;
            HWND hwndListAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
            MapWindowPoints(NULL, hwnd, &ptWindow, 1);
            /* over available buttons list? */
            if (ChildWindowFromPoint(hwnd, ptWindow) == hwndListAvail)
                /* FIXME: native uses "move button" cursor */
                return DL_COPYCURSOR;
        }
        /* clear drag arrow */
        DrawInsert(hwnd, hwndList, -1);
        return DL_STOPCURSOR;
    }
    case DL_DROPPED:
    {
        INT nIndexTo = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2181 2182
        INT nIndexFrom = SendMessageW(hwndList, LB_GETCURSEL, 0, 0);
        INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201
        if ((nIndexTo >= 0) && (nIndexTo < (nCount - 1)))
        {
            /* clear drag arrow */
            DrawInsert(hwnd, hwndList, -1);
            /* move item */
            TOOLBAR_Cust_MoveButton(custInfo, hwnd, nIndexFrom, nIndexTo);
        }
        /* not over toolbar buttons list */
        if (nIndexTo < 0)
        {
            POINT ptWindow = pDLI->ptCursor;
            HWND hwndListAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
            MapWindowPoints(NULL, hwnd, &ptWindow, 1);
            /* over available buttons list? */
            if (ChildWindowFromPoint(hwnd, ptWindow) == hwndListAvail)
                TOOLBAR_Cust_RemoveButton(custInfo, hwnd, nIndexFrom);
        }
        break;
    }
Robert Shearman's avatar
Robert Shearman committed
2202
    case DL_CANCELDRAG:
2203 2204 2205 2206 2207
        /* Clear drag arrow */
        DrawInsert(hwnd, hwndList, -1);
        break;
    }

Robert Shearman's avatar
Robert Shearman committed
2208
    return 0;
2209 2210 2211
}

/* drag list notification function for available buttons list box */
2212 2213
static LRESULT TOOLBAR_Cust_AvailDragListNotification(const CUSTDLG_INFO *custInfo, HWND hwnd,
                                                      const DRAGLISTINFO *pDLI)
2214 2215 2216 2217 2218 2219 2220 2221 2222
{
    HWND hwndList = GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX);
    switch (pDLI->uNotification)
    {
    case DL_BEGINDRAG:
        return TRUE;
    case DL_DRAGGING:
    {
        INT nCurrentItem = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2223
        INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
2224
        /* no dragging past last item (separator) */
Robert Shearman's avatar
Robert Shearman committed
2225
        if ((nCurrentItem >= 0) && (nCurrentItem < nCount))
2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249
        {
            DrawInsert(hwnd, hwndList, nCurrentItem);
            /* FIXME: native uses "move button" cursor */
            return DL_COPYCURSOR;
        }

        /* not over toolbar buttons list */
        if (nCurrentItem < 0)
        {
            POINT ptWindow = pDLI->ptCursor;
            HWND hwndListAvail = GetDlgItem(hwnd, IDC_AVAILBTN_LBOX);
            MapWindowPoints(NULL, hwnd, &ptWindow, 1);
            /* over available buttons list? */
            if (ChildWindowFromPoint(hwnd, ptWindow) == hwndListAvail)
                /* FIXME: native uses "move button" cursor */
                return DL_COPYCURSOR;
        }
        /* clear drag arrow */
        DrawInsert(hwnd, hwndList, -1);
        return DL_STOPCURSOR;
    }
    case DL_DROPPED:
    {
        INT nIndexTo = LBItemFromPt(hwndList, pDLI->ptCursor, TRUE);
2250 2251
        INT nCount = SendMessageW(hwndList, LB_GETCOUNT, 0, 0);
        INT nIndexFrom = SendDlgItemMessageW(hwnd, IDC_AVAILBTN_LBOX, LB_GETCURSEL, 0, 0);
Robert Shearman's avatar
Robert Shearman committed
2252
        if ((nIndexTo >= 0) && (nIndexTo < nCount))
2253 2254 2255 2256 2257 2258 2259
        {
            /* clear drag arrow */
            DrawInsert(hwnd, hwndList, -1);
            /* add item */
            TOOLBAR_Cust_AddButton(custInfo, hwnd, nIndexFrom, nIndexTo);
        }
    }
Robert Shearman's avatar
Robert Shearman committed
2260
    case DL_CANCELDRAG:
2261 2262 2263
        /* Clear drag arrow */
        DrawInsert(hwnd, hwndList, -1);
        break;
Robert Shearman's avatar
Robert Shearman committed
2264 2265
    }
    return 0;
2266 2267 2268
}

extern UINT uDragListMessage;
2269 2270 2271 2272 2273

/***********************************************************************
 * TOOLBAR_CustomizeDialogProc
 * This function implements the toolbar customization dialog.
 */
2274
static INT_PTR CALLBACK
2275 2276
TOOLBAR_CustomizeDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
2277
    PCUSTDLG_INFO custInfo = (PCUSTDLG_INFO)GetWindowLongPtrW (hwnd, DWLP_USER);
2278 2279
    PCUSTOMBUTTON btnInfo;
    NMTOOLBARA nmtb;
2280
    TOOLBAR_INFO *infoPtr = custInfo ? custInfo->tbInfo : NULL;
2281 2282 2283 2284

    switch (uMsg)
    {
	case WM_INITDIALOG:
2285
	    custInfo = (PCUSTDLG_INFO)lParam;
2286
	    SetWindowLongPtrW (hwnd, DWLP_USER, (LONG_PTR)custInfo);
2287

2288
	    if (custInfo)
2289
	    {
2290
		WCHAR Buffer[256];
2291 2292
		int i = 0;
		int index;
2293
		NMTBINITCUSTOMIZE nmtbic;
2294

2295
		infoPtr = custInfo->tbInfo;
2296 2297 2298 2299

		/* send TBN_QUERYINSERT notification */
		nmtb.iItem = custInfo->tbInfo->nNumButtons;

2300
		if (!TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_QUERYINSERT))
2301 2302
		    return FALSE;

2303
		nmtbic.hwndDialog = hwnd;
2304
		/* Send TBN_INITCUSTOMIZE notification */
2305
		if (TOOLBAR_SendNotify (&nmtbic.hdr, infoPtr, TBN_INITCUSTOMIZE) ==
2306
		    TBNRF_HIDEHELP)
2307
                {
2308 2309 2310
                    TRACE("TBNRF_HIDEHELP requested\n");
                    ShowWindow(GetDlgItem(hwnd, IDC_HELP_BTN), SW_HIDE);
                }
2311

2312 2313
		/* add items to 'toolbar buttons' list and check if removable */
		for (i = 0; i < custInfo->tbInfo->nNumButtons; i++)
2314
                {
2315
		    btnInfo = (PCUSTOMBUTTON)Alloc(sizeof(CUSTOMBUTTON));
2316
                    memset (&btnInfo->btn, 0, sizeof(TBBUTTON));
2317
                    btnInfo->btn.fsStyle = BTNS_SEP;
2318 2319
                    btnInfo->bVirtual = FALSE;
		    LoadStringW (COMCTL32_hModule, IDS_SEPARATOR, btnInfo->text, 64);
2320 2321

		    /* send TBN_QUERYDELETE notification */
2322
                    btnInfo->bRemovable = TOOLBAR_IsButtonRemovable(infoPtr, i, btnInfo);
2323

Robert Shearman's avatar
Robert Shearman committed
2324 2325
		    index = (int)SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_ADDSTRING, 0, 0);
		    SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_SETITEMDATA, index, (LPARAM)btnInfo);
2326
		}
2327

Robert Shearman's avatar
Robert Shearman committed
2328
		SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_SETITEMHEIGHT, 0, infoPtr->nBitmapHeight + 8);
2329

2330
		/* insert separator button into 'available buttons' list */
2331
		btnInfo = (PCUSTOMBUTTON)Alloc(sizeof(CUSTOMBUTTON));
2332
		memset (&btnInfo->btn, 0, sizeof(TBBUTTON));
2333
		btnInfo->btn.fsStyle = BTNS_SEP;
2334 2335
		btnInfo->bVirtual = FALSE;
		btnInfo->bRemovable = TRUE;
2336
		LoadStringW (COMCTL32_hModule, IDS_SEPARATOR, btnInfo->text, 64);
Robert Shearman's avatar
Robert Shearman committed
2337 2338
		index = (int)SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_ADDSTRING, 0, (LPARAM)btnInfo);
		SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_SETITEMDATA, index, (LPARAM)btnInfo);
2339 2340 2341 2342 2343

		/* insert all buttons into dsa */
		for (i = 0;; i++)
		{
		    /* send TBN_GETBUTTONINFO notification */
2344
                    NMTOOLBARW nmtb;
2345 2346 2347 2348
		    nmtb.iItem = i;
		    nmtb.pszText = Buffer;
		    nmtb.cchText = 256;

2349 2350 2351 2352
                    /* Clear previous button's text */
                    ZeroMemory(nmtb.pszText, nmtb.cchText * sizeof(WCHAR));

                    if (!TOOLBAR_GetButtonInfo(infoPtr, &nmtb))
2353 2354
			break;

2355
		    TRACE("WM_INITDIALOG style: %x iItem(%d) idCommand(%d) iString(%ld) %s\n",
2356 2357 2358 2359 2360
                        nmtb.tbButton.fsStyle, i, 
                        nmtb.tbButton.idCommand,
                        nmtb.tbButton.iString,
                        nmtb.tbButton.iString >= 0 ? debugstr_w(infoPtr->strings[nmtb.tbButton.iString])
                        : "");
2361 2362

		    /* insert button into the apropriate list */
2363
		    index = TOOLBAR_GetButtonIndex (custInfo->tbInfo, nmtb.tbButton.idCommand, FALSE);
2364
		    if (index == -1)
2365
		    {
2366
			btnInfo = (PCUSTOMBUTTON)Alloc(sizeof(CUSTOMBUTTON));
2367 2368
			btnInfo->bVirtual = FALSE;
			btnInfo->bRemovable = TRUE;
2369 2370 2371
		    }
		    else
		    {
Robert Shearman's avatar
Robert Shearman committed
2372
                        btnInfo = (PCUSTOMBUTTON)SendDlgItemMessageW (hwnd, 
2373 2374 2375 2376
                            IDC_TOOLBARBTN_LBOX, LB_GETITEMDATA, index, 0);
                    }

                    memcpy (&btnInfo->btn, &nmtb.tbButton, sizeof(TBBUTTON));
2377
                    if (!(nmtb.tbButton.fsStyle & BTNS_SEP))
2378 2379 2380 2381 2382 2383 2384 2385 2386
                    {
                        if (lstrlenW(nmtb.pszText))
                            lstrcpyW(btnInfo->text, nmtb.pszText);
                        else if (nmtb.tbButton.iString >= 0 && 
                            nmtb.tbButton.iString < infoPtr->nNumStrings)
                        {
                            lstrcpyW(btnInfo->text, 
                                infoPtr->strings[nmtb.tbButton.iString]);
                        }
2387
		    }
2388 2389 2390

		    if (index == -1)
			TOOLBAR_Cust_InsertAvailButton(hwnd, btnInfo);
2391 2392
		}

Robert Shearman's avatar
Robert Shearman committed
2393
		SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_SETITEMHEIGHT, 0, infoPtr->nBitmapHeight + 8);
2394

2395
		/* select first item in the 'available' list */
Robert Shearman's avatar
Robert Shearman committed
2396
		SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_SETCURSEL, 0, 0);
2397

2398
		/* append 'virtual' separator button to the 'toolbar buttons' list */
2399
		btnInfo = (PCUSTOMBUTTON)Alloc(sizeof(CUSTOMBUTTON));
2400
		memset (&btnInfo->btn, 0, sizeof(TBBUTTON));
2401
		btnInfo->btn.fsStyle = BTNS_SEP;
2402 2403
		btnInfo->bVirtual = TRUE;
		btnInfo->bRemovable = FALSE;
2404
		LoadStringW (COMCTL32_hModule, IDS_SEPARATOR, btnInfo->text, 64);
Robert Shearman's avatar
Robert Shearman committed
2405 2406
		index = (int)SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_ADDSTRING, 0, (LPARAM)btnInfo);
		SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_SETITEMDATA, index, (LPARAM)btnInfo);
2407 2408

		/* select last item in the 'toolbar' list */
Robert Shearman's avatar
Robert Shearman committed
2409 2410
		SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_SETCURSEL, index, 0);
		SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_SETTOPINDEX, index, 0);
2411

2412 2413 2414
        MakeDragList(GetDlgItem(hwnd, IDC_TOOLBARBTN_LBOX));
        MakeDragList(GetDlgItem(hwnd, IDC_AVAILBTN_LBOX));

2415
		/* set focus and disable buttons */
2416
		PostMessageW (hwnd, WM_USER, 0, 0);
2417 2418 2419
	    }
	    return TRUE;

2420 2421 2422 2423 2424 2425 2426
	case WM_USER:
	    EnableWindow (GetDlgItem (hwnd,IDC_MOVEUP_BTN), FALSE);
	    EnableWindow (GetDlgItem (hwnd,IDC_MOVEDN_BTN), FALSE);
	    EnableWindow (GetDlgItem (hwnd,IDC_REMOVE_BTN), FALSE);
	    SetFocus (GetDlgItem (hwnd, IDC_TOOLBARBTN_LBOX));
	    return TRUE;

2427 2428 2429 2430 2431 2432 2433
	case WM_CLOSE:
	    EndDialog(hwnd, FALSE);
	    return TRUE;

	case WM_COMMAND:
	    switch (LOWORD(wParam))
	    {
2434 2435 2436 2437 2438 2439 2440 2441
		case IDC_TOOLBARBTN_LBOX:
		    if (HIWORD(wParam) == LBN_SELCHANGE)
		    {
			PCUSTOMBUTTON btnInfo;
			NMTOOLBARA nmtb;
			int count;
			int index;

Robert Shearman's avatar
Robert Shearman committed
2442 2443
			count = SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETCOUNT, 0, 0);
			index = SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETCURSEL, 0, 0);
2444 2445 2446

			/* send TBN_QUERYINSERT notification */
			nmtb.iItem = index;
2447
		        TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_QUERYINSERT);
2448 2449

			/* get list box item */
Robert Shearman's avatar
Robert Shearman committed
2450
			btnInfo = (PCUSTOMBUTTON)SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETITEMDATA, index, 0);
2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481

			if (index == (count - 1))
			{
			    /* last item (virtual separator) */
			    EnableWindow (GetDlgItem (hwnd,IDC_MOVEUP_BTN), FALSE);
			    EnableWindow (GetDlgItem (hwnd,IDC_MOVEDN_BTN), FALSE);
			}
			else if (index == (count - 2))
			{
			    /* second last item (last non-virtual item) */
			    EnableWindow (GetDlgItem (hwnd,IDC_MOVEUP_BTN), TRUE);
			    EnableWindow (GetDlgItem (hwnd,IDC_MOVEDN_BTN), FALSE);
			}
			else if (index == 0)
			{
			    /* first item */
			    EnableWindow (GetDlgItem (hwnd,IDC_MOVEUP_BTN), FALSE);
			    EnableWindow (GetDlgItem (hwnd,IDC_MOVEDN_BTN), TRUE);
			}
			else
			{
			    EnableWindow (GetDlgItem (hwnd,IDC_MOVEUP_BTN), TRUE);
			    EnableWindow (GetDlgItem (hwnd,IDC_MOVEDN_BTN), TRUE);
			}

			EnableWindow (GetDlgItem (hwnd,IDC_REMOVE_BTN), btnInfo->bRemovable);
		    }
		    break;

		case IDC_MOVEUP_BTN:
		    {
Robert Shearman's avatar
Robert Shearman committed
2482
			int index = SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETCURSEL, 0, 0);
2483
			TOOLBAR_Cust_MoveButton(custInfo, hwnd, index, index-1);
2484 2485 2486 2487 2488
		    }
		    break;

		case IDC_MOVEDN_BTN: /* move down */
		    {
Robert Shearman's avatar
Robert Shearman committed
2489
			int index = SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETCURSEL, 0, 0);
2490
			TOOLBAR_Cust_MoveButton(custInfo, hwnd, index, index+1);
2491 2492 2493 2494 2495
		    }
		    break;

		case IDC_REMOVE_BTN: /* remove button */
		    {
Robert Shearman's avatar
Robert Shearman committed
2496
			int index = SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETCURSEL, 0, 0);
2497 2498 2499 2500

			if (LB_ERR == index)
				break;

2501
			TOOLBAR_Cust_RemoveButton(custInfo, hwnd, index);
2502 2503
		    }
		    break;
2504 2505 2506 2507 2508 2509
		case IDC_HELP_BTN:
			TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_CUSTHELP);
			break;
		case IDC_RESET_BTN:
			TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_RESET);
			break;
2510 2511 2512 2513

		case IDOK: /* Add button */
		    {
			int index;
2514
			int indexto;
2515

Robert Shearman's avatar
Robert Shearman committed
2516 2517
			index = SendDlgItemMessageW(hwnd, IDC_AVAILBTN_LBOX, LB_GETCURSEL, 0, 0);
			indexto = SendDlgItemMessageW(hwnd, IDC_TOOLBARBTN_LBOX, LB_GETCURSEL, 0, 0);
2518

2519
			TOOLBAR_Cust_AddButton(custInfo, hwnd, index, indexto);
2520 2521 2522
		    }
		    break;

2523 2524 2525 2526 2527 2528 2529
		case IDCANCEL:
		    EndDialog(hwnd, FALSE);
		    break;
	    }
	    return TRUE;

	case WM_DESTROY:
2530 2531 2532 2533 2534
	    {
		int count;
		int i;

		/* delete items from 'toolbar buttons' listbox*/
Robert Shearman's avatar
Robert Shearman committed
2535
		count = SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETCOUNT, 0, 0);
2536 2537
		for (i = 0; i < count; i++)
		{
Robert Shearman's avatar
Robert Shearman committed
2538
		    btnInfo = (PCUSTOMBUTTON)SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_GETITEMDATA, i, 0);
2539
		    Free(btnInfo);
Robert Shearman's avatar
Robert Shearman committed
2540
		    SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_SETITEMDATA, 0, 0);
2541
		}
Robert Shearman's avatar
Robert Shearman committed
2542
		SendDlgItemMessageW (hwnd, IDC_TOOLBARBTN_LBOX, LB_RESETCONTENT, 0, 0);
2543 2544 2545


		/* delete items from 'available buttons' listbox*/
Robert Shearman's avatar
Robert Shearman committed
2546
		count = SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_GETCOUNT, 0, 0);
2547 2548
		for (i = 0; i < count; i++)
		{
Robert Shearman's avatar
Robert Shearman committed
2549
		    btnInfo = (PCUSTOMBUTTON)SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_GETITEMDATA, i, 0);
2550
		    Free(btnInfo);
Robert Shearman's avatar
Robert Shearman committed
2551
		    SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_SETITEMDATA, i, 0);
2552
		}
Robert Shearman's avatar
Robert Shearman committed
2553
		SendDlgItemMessageW (hwnd, IDC_AVAILBTN_LBOX, LB_RESETCONTENT, 0, 0);
2554
            }
2555 2556 2557 2558 2559 2560 2561 2562
	    return TRUE;

	case WM_DRAWITEM:
	    if (wParam == IDC_AVAILBTN_LBOX || wParam == IDC_TOOLBARBTN_LBOX)
	    {
		LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
		RECT rcButton;
		RECT rcText;
2563
		HPEN hPen, hOldPen;
2564 2565 2566 2567
		HBRUSH hOldBrush;
		COLORREF oldText = 0;
		COLORREF oldBk = 0;

2568
		/* get item data */
Robert Shearman's avatar
Robert Shearman committed
2569
		btnInfo = (PCUSTOMBUTTON)SendDlgItemMessageW (hwnd, wParam, LB_GETITEMDATA, (WPARAM)lpdis->itemID, 0);
2570
		if (btnInfo == NULL)
2571
		{
2572 2573
		    FIXME("btnInfo invalid!\n");
		    return TRUE;
2574 2575
		}

2576
		/* set colors and select objects */
2577
		oldBk = SetBkColor (lpdis->hDC, (lpdis->itemState & ODS_FOCUS)?comctl32_color.clrHighlight:comctl32_color.clrWindow);
2578
		if (btnInfo->bVirtual)
2579
		   oldText = SetTextColor (lpdis->hDC, comctl32_color.clrGrayText);
2580
		else
2581
		   oldText = SetTextColor (lpdis->hDC, (lpdis->itemState & ODS_FOCUS)?comctl32_color.clrHighlightText:comctl32_color.clrWindowText);
2582 2583 2584
                hPen = CreatePen( PS_SOLID, 1,
                     GetSysColor( (lpdis->itemState & ODS_SELECTED)?COLOR_HIGHLIGHT:COLOR_WINDOW));
		hOldPen = SelectObject (lpdis->hDC, hPen );
2585 2586 2587 2588 2589 2590 2591 2592 2593 2594
		hOldBrush = SelectObject (lpdis->hDC, GetSysColorBrush ((lpdis->itemState & ODS_FOCUS)?COLOR_HIGHLIGHT:COLOR_WINDOW));

		/* fill background rectangle */
		Rectangle (lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top,
			   lpdis->rcItem.right, lpdis->rcItem.bottom);

		/* calculate button and text rectangles */
		CopyRect (&rcButton, &lpdis->rcItem);
		InflateRect (&rcButton, -1, -1);
		CopyRect (&rcText, &rcButton);
2595
		rcButton.right = rcButton.left + custInfo->tbInfo->nBitmapWidth + 6;
2596 2597 2598 2599 2600 2601 2602
		rcText.left = rcButton.right + 2;

		/* draw focus rectangle */
		if (lpdis->itemState & ODS_FOCUS)
		    DrawFocusRect (lpdis->hDC, &lpdis->rcItem);

		/* draw button */
2603
		if (!(infoPtr->dwStyle & TBSTYLE_FLAT))
2604
		    DrawEdge (lpdis->hDC, &rcButton, EDGE_RAISED, BF_RECT|BF_MIDDLE|BF_SOFT);
2605

2606
		/* draw image and text */
2607
		if ((btnInfo->btn.fsStyle & BTNS_SEP) == 0) {
2608 2609 2610 2611 2612 2613
			HIMAGELIST himl = GETDEFIMAGELIST(infoPtr, GETHIMLID(infoPtr, 
				btnInfo->btn.iBitmap));
		    ImageList_Draw (himl, GETIBITMAP(infoPtr, btnInfo->btn.iBitmap), 
				lpdis->hDC, rcButton.left+3, rcButton.top+3, ILD_NORMAL);
		}
		DrawTextW (lpdis->hDC,  btnInfo->text, -1, &rcText,
2614 2615
			       DT_LEFT | DT_VCENTER | DT_SINGLELINE);

2616
		/* delete objects and reset colors */
2617 2618
		SelectObject (lpdis->hDC, hOldBrush);
		SelectObject (lpdis->hDC, hOldPen);
2619 2620
		SetBkColor (lpdis->hDC, oldBk);
		SetTextColor (lpdis->hDC, oldText);
2621
                DeleteObject( hPen );
2622 2623 2624 2625 2626 2627 2628 2629 2630
		return TRUE;
	    }
	    return FALSE;

	case WM_MEASUREITEM:
	    if (wParam == IDC_AVAILBTN_LBOX || wParam == IDC_TOOLBARBTN_LBOX)
	    {
		MEASUREITEMSTRUCT *lpmis = (MEASUREITEMSTRUCT*)lParam;

2631
		lpmis->itemHeight = 15 + 8; /* default height */
2632 2633 2634 2635 2636 2637

		return TRUE;
	    }
	    return FALSE;

	default:
2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655
            if (uDragListMessage && (uMsg == uDragListMessage))
            {
                if (wParam == IDC_TOOLBARBTN_LBOX)
                {
                    LRESULT res = TOOLBAR_Cust_ToolbarDragListNotification(
                        custInfo, hwnd, (DRAGLISTINFO *)lParam);
                    SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, res);
                    return TRUE;
                }
                else if (wParam == IDC_AVAILBTN_LBOX)
                {
                    LRESULT res = TOOLBAR_Cust_AvailDragListNotification(
                        custInfo, hwnd, (DRAGLISTINFO *)lParam);
                    SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, res);
                    return TRUE;
                }
            }
            return FALSE;
2656 2657 2658
    }
}

2659 2660 2661 2662 2663 2664
static BOOL
TOOLBAR_AddBitmapToImageList(TOOLBAR_INFO *infoPtr, HIMAGELIST himlDef, const TBITMAP_INFO *bitmap)
{
    HBITMAP hbmLoad;
    INT nCountBefore = ImageList_GetImageCount(himlDef);
    INT nCountAfter;
2665
    INT cxIcon, cyIcon;
2666 2667 2668 2669 2670 2671
    INT nAdded;
    INT nIndex;

    TRACE("adding hInst=%p nID=%d nButtons=%d\n", bitmap->hInst, bitmap->nID, bitmap->nButtons);
    /* Add bitmaps to the default image list */
    if (bitmap->hInst == NULL)         /* a handle was passed */
2672
        hbmLoad = (HBITMAP)CopyImage(ULongToHandle(bitmap->nID), IMAGE_BITMAP, 0, 0, 0);
2673 2674
    else
        hbmLoad = CreateMappedBitmap(bitmap->hInst, bitmap->nID, 0, NULL, 0);
2675 2676 2677

    /* enlarge the bitmap if needed */
    ImageList_GetIconSize(himlDef, &cxIcon, &cyIcon);
2678 2679
    if (bitmap->hInst != COMCTL32_hModule)
        COMCTL32_EnsureBitmapSize(&hbmLoad, cxIcon*(INT)bitmap->nButtons, cyIcon, comctl32_color.clrBtnFace);
2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728
    
    nIndex = ImageList_AddMasked(himlDef, hbmLoad, comctl32_color.clrBtnFace);
    DeleteObject(hbmLoad);
    if (nIndex == -1)
        return FALSE;
    
    nCountAfter = ImageList_GetImageCount(himlDef);
    nAdded =  nCountAfter - nCountBefore;
    if (bitmap->nButtons == 0) /* wParam == 0 is special and means add only one image */
    {
        ImageList_SetImageCount(himlDef, nCountBefore + 1);
    } else if (nAdded > (INT)bitmap->nButtons) {
        TRACE("Added more images than wParam: Previous image number %i added %i while wParam %i. Images in list %i\n",
            nCountBefore, nAdded, bitmap->nButtons, nCountAfter);
    }

    infoPtr->nNumBitmaps += nAdded;
    return TRUE;
}

static void
TOOLBAR_CheckImageListIconSize(TOOLBAR_INFO *infoPtr)
{
    HIMAGELIST himlDef;
    HIMAGELIST himlNew;
    INT cx, cy;
    INT i;
    
    himlDef = GETDEFIMAGELIST(infoPtr, 0);
    if (himlDef == NULL || himlDef != infoPtr->himlInt)
        return;
    if (!ImageList_GetIconSize(himlDef, &cx, &cy))
        return;
    if (cx == infoPtr->nBitmapWidth && cy == infoPtr->nBitmapHeight)
        return;

    TRACE("Update icon size: %dx%d -> %dx%d\n",
        cx, cy, infoPtr->nBitmapWidth, infoPtr->nBitmapHeight);

    himlNew = ImageList_Create(infoPtr->nBitmapWidth, infoPtr->nBitmapHeight,
                                ILC_COLORDDB|ILC_MASK, 8, 2);
    for (i = 0; i < infoPtr->nNumBitmapInfos; i++)
        TOOLBAR_AddBitmapToImageList(infoPtr, himlNew, &infoPtr->bitmaps[i]);
    TOOLBAR_InsertImageList(&infoPtr->himlDef, &infoPtr->cimlDef, himlNew, 0);
    infoPtr->himlInt = himlNew;

    infoPtr->nNumBitmaps -= ImageList_GetImageCount(himlDef);
    ImageList_Destroy(himlDef);
}
2729

2730 2731 2732 2733
/***********************************************************************
 * TOOLBAR_AddBitmap:  Add the bitmaps to the default image list.
 *
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
2734
static LRESULT
2735
TOOLBAR_AddBitmap (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
2736
{
2737
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
2738
    LPTBADDBITMAP lpAddBmp = (LPTBADDBITMAP)lParam;
2739
    TBITMAP_INFO info;
2740
    INT iSumButtons, i;
2741
    HIMAGELIST himlDef;
Alexandre Julliard's avatar
Alexandre Julliard committed
2742

2743
    TRACE("hwnd=%p wParam=%lx lParam=%lx\n", hwnd, wParam, lParam);
2744
    if (!lpAddBmp)
Alexandre Julliard's avatar
Alexandre Julliard committed
2745 2746
	return -1;

2747 2748
    if (lpAddBmp->hInst == HINST_COMMCTRL)
    {
2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778
        info.hInst = COMCTL32_hModule;
        switch (lpAddBmp->nID)
        {
            case IDB_STD_SMALL_COLOR:
	        info.nButtons = 15;
	        info.nID = IDB_STD_SMALL;
	        break;
            case IDB_STD_LARGE_COLOR:
	        info.nButtons = 15;
	        info.nID = IDB_STD_LARGE;
	        break;
            case IDB_VIEW_SMALL_COLOR:
	        info.nButtons = 12;
	        info.nID = IDB_VIEW_SMALL;
	        break;
            case IDB_VIEW_LARGE_COLOR:
	        info.nButtons = 12;
	        info.nID = IDB_VIEW_LARGE;
	        break;
            case IDB_HIST_SMALL_COLOR:
	        info.nButtons = 5;
	        info.nID = IDB_HIST_SMALL;
	        break;
            case IDB_HIST_LARGE_COLOR:
	        info.nButtons = 5;
	        info.nID = IDB_HIST_LARGE;
	        break;
	    default:
	        return -1;
	}
2779

2780
	TRACE ("adding %d internal bitmaps!\n", info.nButtons);
2781

2782
	/* Windows resize all the buttons to the size of a newly added standard image */
2783
	if (lpAddBmp->nID & 1)
2784
	{
2785 2786
	    /* large icons: 24x24. Will make the button 31x30 */
	    SendMessageW (hwnd, TB_SETBITMAPSIZE, 0, MAKELPARAM(24, 24));
2787 2788
	}
	else
2789
	{
2790 2791
	    /* small icons: 16x16. Will make the buttons 23x22 */
	    SendMessageW (hwnd, TB_SETBITMAPSIZE, 0, MAKELPARAM(16, 16));
2792
	}
2793

2794
	TOOLBAR_CalcToolbar (hwnd);
2795 2796 2797
    }
    else
    {
2798 2799 2800 2801
	info.nButtons = (INT)wParam;
	info.hInst = lpAddBmp->hInst;
	info.nID = lpAddBmp->nID;
	TRACE("adding %d bitmaps!\n", info.nButtons);
2802
    }
2803 2804 2805 2806 2807 2808 2809 2810 2811 2812
    
    /* check if the bitmap is already loaded and compute iSumButtons */
    iSumButtons = 0;
    for (i = 0; i < infoPtr->nNumBitmapInfos; i++)
    {
        if (infoPtr->bitmaps[i].hInst == info.hInst &&
            infoPtr->bitmaps[i].nID == info.nID)
            return iSumButtons;
        iSumButtons += infoPtr->bitmaps[i].nButtons;
    }
2813

2814
    if (!infoPtr->cimlDef) {
2815 2816
	/* create new default image list */
	TRACE ("creating default image list!\n");
2817

2818
        himlDef = ImageList_Create (infoPtr->nBitmapWidth, infoPtr->nBitmapHeight,
2819
                                    ILC_COLORDDB | ILC_MASK, info.nButtons, 2);
2820
	TOOLBAR_InsertImageList(&infoPtr->himlDef, &infoPtr->cimlDef, himlDef, 0);
2821
        infoPtr->himlInt = himlDef;
2822 2823 2824 2825 2826 2827 2828 2829
    }
    else {
        himlDef = GETDEFIMAGELIST(infoPtr, 0);
    }

    if (!himlDef) {
        WARN("No default image list available\n");
        return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2830 2831
    }

2832 2833
    if (!TOOLBAR_AddBitmapToImageList(infoPtr, himlDef, &info))
        return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2834

2835
    TRACE("Number of bitmap infos: %d\n", infoPtr->nNumBitmapInfos);
2836
    infoPtr->bitmaps = ReAlloc(infoPtr->bitmaps, (infoPtr->nNumBitmapInfos + 1) * sizeof(TBITMAP_INFO));
2837
    infoPtr->bitmaps[infoPtr->nNumBitmapInfos] = info;
2838 2839 2840
    infoPtr->nNumBitmapInfos++;
    TRACE("Number of bitmap infos: %d\n", infoPtr->nNumBitmapInfos);

Robert Shearman's avatar
Robert Shearman committed
2841
    InvalidateRect(hwnd, NULL, TRUE);
2842
    return iSumButtons;
Alexandre Julliard's avatar
Alexandre Julliard committed
2843 2844 2845 2846
}


static LRESULT
2847
TOOLBAR_AddButtonsT(HWND hwnd, WPARAM wParam, LPARAM lParam, BOOL fUnicode)
Alexandre Julliard's avatar
Alexandre Julliard committed
2848
{
2849
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
2850
    LPTBBUTTON lpTbb = (LPTBBUTTON)lParam;
2851
    INT nOldButtons, nNewButtons, nAddButtons, nCount;
2852
    BOOL fHasString = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2853

2854
    TRACE("adding %ld buttons (unicode=%d)!\n", wParam, fUnicode);
Alexandre Julliard's avatar
Alexandre Julliard committed
2855

2856
    nAddButtons = (UINT)wParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
2857 2858 2859
    nOldButtons = infoPtr->nNumButtons;
    nNewButtons = nOldButtons + nAddButtons;

2860
    infoPtr->buttons = ReAlloc(infoPtr->buttons, sizeof(TBUTTON_INFO)*nNewButtons);
Alexandre Julliard's avatar
Alexandre Julliard committed
2861 2862
    infoPtr->nNumButtons = nNewButtons;

Alexandre Julliard's avatar
Alexandre Julliard committed
2863
    /* insert new button data */
Alexandre Julliard's avatar
Alexandre Julliard committed
2864
    for (nCount = 0; nCount < nAddButtons; nCount++) {
Alexandre Julliard's avatar
Alexandre Julliard committed
2865 2866 2867 2868 2869 2870
	TBUTTON_INFO *btnPtr = &infoPtr->buttons[nOldButtons+nCount];
	btnPtr->iBitmap   = lpTbb[nCount].iBitmap;
	btnPtr->idCommand = lpTbb[nCount].idCommand;
	btnPtr->fsState   = lpTbb[nCount].fsState;
	btnPtr->fsStyle   = lpTbb[nCount].fsStyle;
	btnPtr->dwData    = lpTbb[nCount].dwData;
2871
	btnPtr->bHot      = FALSE;
2872
        if(HIWORD(lpTbb[nCount].iString) && lpTbb[nCount].iString != -1)
2873 2874 2875 2876 2877
        {
            if (fUnicode)
                Str_SetPtrW ((LPWSTR*)&btnPtr->iString, (LPWSTR)lpTbb[nCount].iString );
            else
                Str_SetPtrAtoW((LPWSTR*)&btnPtr->iString, (LPSTR)lpTbb[nCount].iString);
2878
            fHasString = TRUE;
2879
        }
2880 2881
        else
            btnPtr->iString   = lpTbb[nCount].iString;
2882

2883
        TOOLBAR_TooltipAddTool(infoPtr, btnPtr);
2884 2885
    }

2886 2887 2888 2889
    if (infoPtr->nNumStrings > 0 || fHasString)
        TOOLBAR_CalcToolbar(hwnd);
    else
        TOOLBAR_LayoutToolbar(hwnd);
2890
    TOOLBAR_AutoSize (hwnd);
2891

Guy Albertelli's avatar
Guy Albertelli committed
2892 2893
    TOOLBAR_DumpToolbar (infoPtr, __LINE__);

Robert Shearman's avatar
Robert Shearman committed
2894
    InvalidateRect(hwnd, NULL, TRUE);
2895 2896 2897

    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2898 2899 2900


static LRESULT
2901
TOOLBAR_AddStringW (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
2902
{
2903
#define MAX_RESOURCE_STRING_LENGTH 512
2904
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
2905
    BOOL fFirstString = (infoPtr->nNumStrings == 0);
2906
    INT nIndex = infoPtr->nNumStrings;
Alexandre Julliard's avatar
Alexandre Julliard committed
2907

2908
    if ((wParam) && (HIWORD(lParam) == 0)) {
2909 2910 2911 2912
	WCHAR szString[MAX_RESOURCE_STRING_LENGTH];
	WCHAR delimiter;
	WCHAR *next_delim;
	WCHAR *p;
2913
	INT len;
2914
	TRACE("adding string from resource!\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
2915

2916
        len = LoadStringW ((HINSTANCE)wParam, (UINT)lParam,
2917
                             szString, MAX_RESOURCE_STRING_LENGTH);
Alexandre Julliard's avatar
Alexandre Julliard committed
2918

2919 2920 2921
        TRACE("len=%d %s\n", len, debugstr_w(szString));
        if (len == 0 || len == 1)
            return nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
2922

2923 2924 2925 2926 2927 2928
        TRACE("Delimiter: 0x%x\n", *szString);
        delimiter = *szString;
        p = szString + 1;

        while ((next_delim = strchrW(p, delimiter)) != NULL) {
            *next_delim = 0;
2929 2930 2931 2932 2933 2934
            if (next_delim + 1 >= szString + len)
            {
                /* this may happen if delimiter == '\0' or if the last char is a
                 * delimiter (then it is ignored like the native does) */
                break;
            }
2935 2936 2937 2938 2939 2940 2941

            infoPtr->strings = ReAlloc(infoPtr->strings, sizeof(LPWSTR)*(infoPtr->nNumStrings+1));
            Str_SetPtrW(&infoPtr->strings[infoPtr->nNumStrings], p);
            infoPtr->nNumStrings++;

            p = next_delim + 1;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
2942 2943
    }
    else {
2944
	LPWSTR p = (LPWSTR)lParam;
2945
	INT len;
Alexandre Julliard's avatar
Alexandre Julliard committed
2946

2947 2948
	if (p == NULL)
	    return -1;
2949
	TRACE("adding string(s) from array!\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
2950
	while (*p) {
2951
            len = strlenW (p);
Alexandre Julliard's avatar
Alexandre Julliard committed
2952

2953 2954 2955
            TRACE("len=%d %s\n", len, debugstr_w(p));
            infoPtr->strings = ReAlloc(infoPtr->strings, sizeof(LPWSTR)*(infoPtr->nNumStrings+1));
            Str_SetPtrW (&infoPtr->strings[infoPtr->nNumStrings], p);
Alexandre Julliard's avatar
Alexandre Julliard committed
2956 2957 2958 2959 2960 2961
	    infoPtr->nNumStrings++;

	    p += (len+1);
	}
    }

2962 2963
    if (fFirstString)
        TOOLBAR_CalcToolbar(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
2964 2965 2966 2967
    return nIndex;
}


2968
static LRESULT
2969
TOOLBAR_AddStringA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2970
{
2971
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
2972
    BOOL fFirstString = (infoPtr->nNumStrings == 0);
2973
    LPSTR p;
2974
    INT nIndex;
2975
    INT len;
2976

2977 2978
    if ((wParam) && (HIWORD(lParam) == 0))  /* load from resources */
        return TOOLBAR_AddStringW(hwnd, wParam, lParam);
2979

2980 2981 2982
    p = (LPSTR)lParam;
    if (p == NULL)
        return -1;
2983

2984 2985 2986 2987 2988
    TRACE("adding string(s) from array!\n");
    nIndex = infoPtr->nNumStrings;
    while (*p) {
        len = strlen (p);
        TRACE("len=%d \"%s\"\n", len, p);
2989

2990 2991 2992
        infoPtr->strings = ReAlloc(infoPtr->strings, sizeof(LPWSTR)*(infoPtr->nNumStrings+1));
        Str_SetPtrAtoW(&infoPtr->strings[infoPtr->nNumStrings], p);
        infoPtr->nNumStrings++;
2993

2994
        p += (len+1);
2995 2996
    }

2997 2998
    if (fFirstString)
        TOOLBAR_CalcToolbar(hwnd);
2999 3000
    return nIndex;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3001 3002 3003


static LRESULT
3004
TOOLBAR_AutoSize (HWND hwnd)
Alexandre Julliard's avatar
Alexandre Julliard committed
3005
{
3006
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3007 3008
    RECT parent_rect;
    HWND parent;
3009
    INT  x, y;
3010
    INT  cx, cy;
Alexandre Julliard's avatar
Alexandre Julliard committed
3011

3012
    TRACE("auto sizing, style=%x!\n", infoPtr->dwStyle);
Alexandre Julliard's avatar
Alexandre Julliard committed
3013

3014
    parent = GetParent (hwnd);
3015 3016 3017 3018

    if (!parent || !infoPtr->bDoRedraw)
        return 0;

3019
    GetClientRect(parent, &parent_rect);
Alexandre Julliard's avatar
Alexandre Julliard committed
3020

3021 3022 3023
    x = parent_rect.left;
    y = parent_rect.top;

3024
    TRACE("nRows: %d, infoPtr->nButtonHeight: %d\n", infoPtr->nRows, infoPtr->nButtonHeight);
3025

3026 3027 3028 3029 3030
    cy = TOP_BORDER + infoPtr->nRows * infoPtr->nButtonHeight + BOTTOM_BORDER;
    cx = parent_rect.right - parent_rect.left;

    if ((infoPtr->dwStyle & TBSTYLE_WRAPABLE) || (infoPtr->dwExStyle & TBSTYLE_EX_UNDOC1))
    {
3031
        TOOLBAR_LayoutToolbar(hwnd);
3032
        InvalidateRect( hwnd, NULL, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
3033
    }
3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047

    if (!(infoPtr->dwStyle & CCS_NORESIZE))
    {
        RECT window_rect;
        UINT uPosFlags = SWP_NOZORDER;

        if ((infoPtr->dwStyle & CCS_BOTTOM) == CCS_NOMOVEY)
        {
            GetWindowRect(hwnd, &window_rect);
            ScreenToClient(parent, (LPPOINT)&window_rect.left);
            y = window_rect.top;
        }
        if ((infoPtr->dwStyle & CCS_BOTTOM) == CCS_BOTTOM)
        {
3048 3049 3050
            GetWindowRect(hwnd, &window_rect);
            y = parent_rect.bottom - ( window_rect.bottom - window_rect.top);
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
3051

3052 3053 3054 3055 3056
        if (infoPtr->dwStyle & CCS_NOPARENTALIGN)
            uPosFlags |= SWP_NOMOVE;
    
        if (!(infoPtr->dwStyle & CCS_NODIVIDER))
            cy += GetSystemMetrics(SM_CYEDGE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3057

3058 3059 3060 3061 3062 3063
        if (infoPtr->dwStyle & WS_BORDER)
        {
            x = y = 1; /* FIXME: this looks wrong */
            cy += GetSystemMetrics(SM_CYEDGE);
            cx += GetSystemMetrics(SM_CXEDGE);
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
3064

3065
        SetWindowPos(hwnd, NULL, x, y, cx, cy, uPosFlags);
3066 3067
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
3068 3069
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3070 3071 3072


static LRESULT
3073
TOOLBAR_ButtonCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3074
{
3075
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3076 3077 3078 3079 3080 3081

    return infoPtr->nNumButtons;
}


static LRESULT
3082
TOOLBAR_ButtonStructSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3083
{
3084
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3085 3086 3087 3088 3089 3090 3091 3092

    infoPtr->dwStructSize = (DWORD)wParam;

    return 0;
}


static LRESULT
3093
TOOLBAR_ChangeBitmap (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3094
{
3095
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3096
    TBUTTON_INFO *btnPtr;
3097
    INT nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
3098

3099
    TRACE("button %ld, iBitmap now %d\n", wParam, LOWORD(lParam));
3100

3101
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3102 3103 3104 3105 3106 3107
    if (nIndex == -1)
	return FALSE;

    btnPtr = &infoPtr->buttons[nIndex];
    btnPtr->iBitmap = LOWORD(lParam);

3108 3109
    /* we HAVE to erase the background, the new bitmap could be */
    /* transparent */
3110
    InvalidateRect(hwnd, &btnPtr->rect, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3111 3112 3113 3114 3115 3116

    return TRUE;
}


static LRESULT
3117
TOOLBAR_CheckButton (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3118
{
3119
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3120
    TBUTTON_INFO *btnPtr;
3121 3122
    INT nIndex;
    INT nOldIndex = -1;
3123
    BOOL bChecked = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3124

3125
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
3126 3127 3128

    TRACE("hwnd=%p, btn index=%d, lParam=0x%08lx\n", hwnd, nIndex, lParam);

Alexandre Julliard's avatar
Alexandre Julliard committed
3129 3130 3131 3132
    if (nIndex == -1)
	return FALSE;

    btnPtr = &infoPtr->buttons[nIndex];
Alexandre Julliard's avatar
Alexandre Julliard committed
3133

3134 3135
    bChecked = (btnPtr->fsState & TBSTATE_CHECKED) ? TRUE : FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
3136 3137
    if (LOWORD(lParam) == FALSE)
	btnPtr->fsState &= ~TBSTATE_CHECKED;
Alexandre Julliard's avatar
Alexandre Julliard committed
3138
    else {
3139
	if (btnPtr->fsStyle & BTNS_GROUP) {
3140
	    nOldIndex =
Alexandre Julliard's avatar
Alexandre Julliard committed
3141 3142 3143 3144 3145 3146
		TOOLBAR_GetCheckedGroupButtonIndex (infoPtr, nIndex);
	    if (nOldIndex == nIndex)
		return 0;
	    if (nOldIndex != -1)
		infoPtr->buttons[nOldIndex].fsState &= ~TBSTATE_CHECKED;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
3147
	btnPtr->fsState |= TBSTATE_CHECKED;
Alexandre Julliard's avatar
Alexandre Julliard committed
3148
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3149

3150 3151
    if( bChecked != LOWORD(lParam) )
    {
Robert Shearman's avatar
Robert Shearman committed
3152 3153
        if (nOldIndex != -1)
            InvalidateRect(hwnd, &infoPtr->buttons[nOldIndex].rect, TRUE);
3154
        InvalidateRect(hwnd, &btnPtr->rect, TRUE);
3155
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3156

Alexandre Julliard's avatar
Alexandre Julliard committed
3157
    /* FIXME: Send a WM_NOTIFY?? */
Alexandre Julliard's avatar
Alexandre Julliard committed
3158

Alexandre Julliard's avatar
Alexandre Julliard committed
3159 3160 3161 3162 3163
    return TRUE;
}


static LRESULT
3164
TOOLBAR_CommandToIndex (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3165
{
3166
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3167

3168
    return TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3169 3170 3171
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3172
static LRESULT
3173
TOOLBAR_Customize (HWND hwnd)
Alexandre Julliard's avatar
Alexandre Julliard committed
3174
{
3175
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3176
    CUSTDLG_INFO custInfo;
3177 3178 3179 3180
    LRESULT ret;
    LPCVOID template;
    HRSRC hRes;
    NMHDR nmhdr;
Alexandre Julliard's avatar
Alexandre Julliard committed
3181

3182
    custInfo.tbInfo = infoPtr;
3183 3184
    custInfo.tbHwnd = hwnd;

3185
    /* send TBN_BEGINADJUST notification */
3186
    TOOLBAR_SendNotify (&nmhdr, infoPtr, TBN_BEGINADJUST);
3187

Robert Shearman's avatar
Robert Shearman committed
3188 3189 3190
    if (!(hRes = FindResourceW (COMCTL32_hModule,
                                MAKEINTRESOURCEW(IDD_TBCUSTOMIZE),
                                (LPWSTR)RT_DIALOG)))
3191 3192 3193 3194 3195
	return FALSE;

    if(!(template = (LPVOID)LoadResource (COMCTL32_hModule, hRes)))
	return FALSE;

Robert Shearman's avatar
Robert Shearman committed
3196
    ret = DialogBoxIndirectParamW ((HINSTANCE)GetWindowLongPtrW(hwnd, GWLP_HINSTANCE),
3197
                                   (LPCDLGTEMPLATEW)template,
3198
                                   hwnd,
3199
                                   TOOLBAR_CustomizeDialogProc,
3200
                                   (LPARAM)&custInfo);
3201 3202

    /* send TBN_ENDADJUST notification */
3203
    TOOLBAR_SendNotify (&nmhdr, infoPtr, TBN_ENDADJUST);
3204 3205

    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
3206
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3207 3208 3209


static LRESULT
3210
TOOLBAR_DeleteButton (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3211
{
3212
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3213
    INT nIndex = (INT)wParam;
Robert Shearman's avatar
Robert Shearman committed
3214
    NMTOOLBARW nmtb;
3215
    TBUTTON_INFO *btnPtr = &infoPtr->buttons[nIndex];
Alexandre Julliard's avatar
Alexandre Julliard committed
3216

Alexandre Julliard's avatar
Alexandre Julliard committed
3217
    if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
3218
        return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3219

Robert Shearman's avatar
Robert Shearman committed
3220
    memset(&nmtb, 0, sizeof(nmtb));
3221 3222 3223 3224 3225 3226 3227
    nmtb.iItem = btnPtr->idCommand;
    nmtb.tbButton.iBitmap = btnPtr->iBitmap;
    nmtb.tbButton.idCommand = btnPtr->idCommand;
    nmtb.tbButton.fsState = btnPtr->fsState;
    nmtb.tbButton.fsStyle = btnPtr->fsStyle;
    nmtb.tbButton.dwData = btnPtr->dwData;
    nmtb.tbButton.iString = btnPtr->iString;
3228
    TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_DELETINGBUTTON);
Robert Shearman's avatar
Robert Shearman committed
3229

3230
    TOOLBAR_TooltipDelTool(infoPtr, &infoPtr->buttons[nIndex]);
Alexandre Julliard's avatar
Alexandre Julliard committed
3231

Alexandre Julliard's avatar
Alexandre Julliard committed
3232
    if (infoPtr->nNumButtons == 1) {
3233
	TRACE(" simple delete!\n");
3234
	Free (infoPtr->buttons);
Alexandre Julliard's avatar
Alexandre Julliard committed
3235 3236 3237 3238
	infoPtr->buttons = NULL;
	infoPtr->nNumButtons = 0;
    }
    else {
Alexandre Julliard's avatar
Alexandre Julliard committed
3239
	TBUTTON_INFO *oldButtons = infoPtr->buttons;
3240
        TRACE("complex delete! [nIndex=%d]\n", nIndex);
Alexandre Julliard's avatar
Alexandre Julliard committed
3241

Alexandre Julliard's avatar
Alexandre Julliard committed
3242
	infoPtr->nNumButtons--;
3243
	infoPtr->buttons = Alloc (sizeof (TBUTTON_INFO) * infoPtr->nNumButtons);
Alexandre Julliard's avatar
Alexandre Julliard committed
3244 3245 3246 3247
        if (nIndex > 0) {
            memcpy (&infoPtr->buttons[0], &oldButtons[0],
                    nIndex * sizeof(TBUTTON_INFO));
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
3248

Alexandre Julliard's avatar
Alexandre Julliard committed
3249 3250 3251 3252 3253
        if (nIndex < infoPtr->nNumButtons) {
            memcpy (&infoPtr->buttons[nIndex], &oldButtons[nIndex+1],
                    (infoPtr->nNumButtons - nIndex) * sizeof(TBUTTON_INFO));
        }

3254
	Free (oldButtons);
Alexandre Julliard's avatar
Alexandre Julliard committed
3255 3256
    }

3257
    TOOLBAR_LayoutToolbar(hwnd);
3258

3259
    InvalidateRect (hwnd, NULL, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3260 3261 3262 3263 3264 3265

    return TRUE;
}


static LRESULT
3266
TOOLBAR_EnableButton (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3267
{
3268
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3269
    TBUTTON_INFO *btnPtr;
3270
    INT nIndex;
3271
    DWORD bState;
Alexandre Julliard's avatar
Alexandre Julliard committed
3272

3273
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
3274

3275
    TRACE("hwnd=%p, btn index=%ld, lParam=0x%08lx\n", hwnd, wParam, lParam);
3276

Alexandre Julliard's avatar
Alexandre Julliard committed
3277 3278 3279 3280
    if (nIndex == -1)
	return FALSE;

    btnPtr = &infoPtr->buttons[nIndex];
3281 3282 3283 3284 3285 3286 3287

    bState = btnPtr->fsState & TBSTATE_ENABLED;

    /* update the toolbar button state */
    if(LOWORD(lParam) == FALSE) {
 	btnPtr->fsState &= ~(TBSTATE_ENABLED | TBSTATE_PRESSED);
    } else {
Alexandre Julliard's avatar
Alexandre Julliard committed
3288
	btnPtr->fsState |= TBSTATE_ENABLED;
3289
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3290

3291
    /* redraw the button only if the state of the button changed */
3292
    if(bState != (btnPtr->fsState & TBSTATE_ENABLED))
Robert Shearman's avatar
Robert Shearman committed
3293
        InvalidateRect(hwnd, &btnPtr->rect, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3294 3295 3296 3297 3298

    return TRUE;
}


3299 3300 3301 3302 3303 3304 3305
static inline LRESULT
TOOLBAR_GetAnchorHighlight (HWND hwnd)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);

    return infoPtr->bAnchor;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3306 3307 3308


static LRESULT
3309
TOOLBAR_GetBitmap (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3310
{
3311
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3312
    INT nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
3313

3314
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3315
    if (nIndex == -1)
Alexandre Julliard's avatar
Alexandre Julliard committed
3316
	return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3317 3318 3319 3320 3321

    return infoPtr->buttons[nIndex].iBitmap;
}


Patrik Stridvall's avatar
Patrik Stridvall committed
3322
static inline LRESULT
3323
TOOLBAR_GetBitmapFlags (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3324
{
3325
    return (GetDeviceCaps (0, LOGPIXELSX) >= 120) ? TBBF_LARGE : 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3326 3327 3328 3329
}


static LRESULT
3330
TOOLBAR_GetButton (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3331
{
3332
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3333
    LPTBBUTTON lpTbb = (LPTBBUTTON)lParam;
3334
    INT nIndex = (INT)wParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
3335 3336
    TBUTTON_INFO *btnPtr;

3337 3338
    if (lpTbb == NULL)
	return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3339 3340 3341 3342 3343 3344 3345 3346 3347

    if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
	return FALSE;

    btnPtr = &infoPtr->buttons[nIndex];
    lpTbb->iBitmap   = btnPtr->iBitmap;
    lpTbb->idCommand = btnPtr->idCommand;
    lpTbb->fsState   = btnPtr->fsState;
    lpTbb->fsStyle   = btnPtr->fsStyle;
3348 3349
    lpTbb->bReserved[0] = 0;
    lpTbb->bReserved[1] = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3350 3351 3352 3353 3354 3355 3356
    lpTbb->dwData    = btnPtr->dwData;
    lpTbb->iString   = btnPtr->iString;

    return TRUE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3357
static LRESULT
3358
TOOLBAR_GetButtonInfoT(HWND hwnd, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
Alexandre Julliard's avatar
Alexandre Julliard committed
3359
{
3360
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3361 3362
    /* TBBUTTONINFOW and TBBUTTONINFOA have the same layout*/
    LPTBBUTTONINFOW lpTbInfo = (LPTBBUTTONINFOW)lParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
3363
    TBUTTON_INFO *btnPtr;
3364
    INT nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
3365

3366 3367
    if (lpTbInfo == NULL)
	return -1;
3368 3369 3370 3371 3372 3373 3374 3375

    /* MSDN documents a iImageLabel field added in Vista but it is not present in
     * the headers and tests shows that even with comctl 6 Vista accepts only the
     * original TBBUTTONINFO size
     */
    if (lpTbInfo->cbSize != sizeof(TBBUTTONINFOW))
    {
        WARN("Invalid button size\n");
3376
	return -1;
3377
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3378

3379 3380
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam,
				     lpTbInfo->dwMask & 0x80000000);
Alexandre Julliard's avatar
Alexandre Julliard committed
3381 3382 3383
    if (nIndex == -1)
	return -1;

Guy Albertelli's avatar
Guy Albertelli committed
3384
    if (!(btnPtr = &infoPtr->buttons[nIndex])) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397

    if (lpTbInfo->dwMask & TBIF_COMMAND)
	lpTbInfo->idCommand = btnPtr->idCommand;
    if (lpTbInfo->dwMask & TBIF_IMAGE)
	lpTbInfo->iImage = btnPtr->iBitmap;
    if (lpTbInfo->dwMask & TBIF_LPARAM)
	lpTbInfo->lParam = btnPtr->dwData;
    if (lpTbInfo->dwMask & TBIF_SIZE)
	lpTbInfo->cx = (WORD)(btnPtr->rect.right - btnPtr->rect.left);
    if (lpTbInfo->dwMask & TBIF_STATE)
	lpTbInfo->fsState = btnPtr->fsState;
    if (lpTbInfo->dwMask & TBIF_STYLE)
	lpTbInfo->fsStyle = btnPtr->fsStyle;
3398 3399 3400 3401
    if (lpTbInfo->dwMask & TBIF_TEXT) {
        /* TB_GETBUTTONINFO doesn't retrieve text from the string list, so we
           can't use TOOLBAR_GetText here */
        if (HIWORD(btnPtr->iString) && (btnPtr->iString != -1)) {
3402 3403 3404 3405 3406
            LPWSTR lpText = (LPWSTR)btnPtr->iString;
            if (bUnicode)
                Str_GetPtrW(lpText, lpTbInfo->pszText, lpTbInfo->cchText);
            else
                Str_GetPtrWtoA(lpText, (LPSTR)lpTbInfo->pszText, lpTbInfo->cchText);
3407 3408
        } else
            lpTbInfo->pszText[0] = '\0';
3409 3410 3411
    }
    return nIndex;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3412 3413 3414


static LRESULT
3415
TOOLBAR_GetButtonSize (HWND hwnd)
Alexandre Julliard's avatar
Alexandre Julliard committed
3416
{
3417
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3418

3419 3420
    return MAKELONG((WORD)infoPtr->nButtonWidth,
                    (WORD)infoPtr->nButtonHeight);
Alexandre Julliard's avatar
Alexandre Julliard committed
3421
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3422 3423 3424


static LRESULT
3425
TOOLBAR_GetButtonTextA (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3426
{
3427
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3428 3429
    INT nIndex;
    LPWSTR lpText;
Alexandre Julliard's avatar
Alexandre Julliard committed
3430

3431
    if (lParam == 0)
Alexandre Julliard's avatar
Alexandre Julliard committed
3432 3433
	return -1;

3434 3435
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
    if (nIndex == -1)
Alexandre Julliard's avatar
Alexandre Julliard committed
3436 3437
	return -1;

3438
    lpText = TOOLBAR_GetText(infoPtr,&infoPtr->buttons[nIndex]);
3439

3440
    return WideCharToMultiByte( CP_ACP, 0, lpText, -1,
3441
                                (LPSTR)lParam, 0x7fffffff, NULL, NULL ) - 1;
3442 3443 3444 3445 3446 3447 3448
}


static LRESULT
TOOLBAR_GetButtonTextW (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3449 3450
    INT nIndex;
    LPWSTR lpText;
3451
    LRESULT ret = 0;
3452

3453 3454
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
    if (nIndex == -1)
3455 3456
	return -1;

3457
    lpText = TOOLBAR_GetText(infoPtr,&infoPtr->buttons[nIndex]);
Alexandre Julliard's avatar
Alexandre Julliard committed
3458

3459 3460 3461
    if (lpText)
    {
        ret = strlenW (lpText);
Alexandre Julliard's avatar
Alexandre Julliard committed
3462

3463 3464 3465 3466 3467
        if (lParam)
            strcpyW ((LPWSTR)lParam, lpText);
    }

    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
3468 3469 3470
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3471
static LRESULT
3472
TOOLBAR_GetDisabledImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3473
{
3474
    TRACE("hwnd=%p, wParam=%ld, lParam=0x%lx\n", hwnd, wParam, lParam);
Robert Shearman's avatar
Robert Shearman committed
3475 3476
    /* UNDOCUMENTED: wParam is actually the ID of the image list to return */
    return (LRESULT)GETDISIMAGELIST(TOOLBAR_GetInfoPtr (hwnd), wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
3477 3478 3479
}


3480
static inline LRESULT
3481
TOOLBAR_GetExtendedStyle (HWND hwnd)
Alexandre Julliard's avatar
Alexandre Julliard committed
3482
{
3483
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3484

3485 3486
    TRACE("\n");

Alexandre Julliard's avatar
Alexandre Julliard committed
3487 3488
    return infoPtr->dwExStyle;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3489 3490 3491


static LRESULT
3492
TOOLBAR_GetHotImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3493
{
3494
    TRACE("hwnd=%p, wParam=%ld, lParam=0x%lx\n", hwnd, wParam, lParam);
Robert Shearman's avatar
Robert Shearman committed
3495 3496
    /* UNDOCUMENTED: wParam is actually the ID of the image list to return */
    return (LRESULT)GETHOTIMAGELIST(TOOLBAR_GetInfoPtr (hwnd), wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
3497 3498 3499
}


3500 3501 3502 3503 3504
static LRESULT
TOOLBAR_GetHotItem (HWND hwnd)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);

3505
    if (!((infoPtr->dwStyle & TBSTYLE_FLAT) || GetWindowTheme (infoPtr->hwndSelf)))
3506 3507 3508 3509 3510 3511 3512
	return -1;

    if (infoPtr->nHotItem < 0)
	return -1;

    return (LRESULT)infoPtr->nHotItem;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3513 3514 3515


static LRESULT
3516
TOOLBAR_GetDefImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3517
{
3518
    TRACE("hwnd=%p, wParam=%ld, lParam=0x%lx\n", hwnd, wParam, lParam);
Robert Shearman's avatar
Robert Shearman committed
3519 3520
    /* UNDOCUMENTED: wParam is actually the ID of the image list to return */
    return (LRESULT) GETDEFIMAGELIST(TOOLBAR_GetInfoPtr(hwnd), wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
3521 3522 3523
}


3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546
static LRESULT
TOOLBAR_GetInsertMark (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    TBINSERTMARK *lptbim = (TBINSERTMARK*)lParam;

    TRACE("hwnd = %p, lptbim = %p\n", hwnd, lptbim);

    *lptbim = infoPtr->tbim;

    return 0;
}


static LRESULT
TOOLBAR_GetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);

    TRACE("hwnd = %p\n", hwnd);

    return (LRESULT)infoPtr->clrInsertMark;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3547 3548 3549


static LRESULT
3550
TOOLBAR_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3551
{
3552
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3553
    TBUTTON_INFO *btnPtr;
3554 3555
    LPRECT     lpRect;
    INT        nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
3556

3557
    nIndex = (INT)wParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
3558 3559 3560
    btnPtr = &infoPtr->buttons[nIndex];
    if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
	return FALSE;
3561
    lpRect = (LPRECT)lParam;
3562 3563 3564 3565
    if (lpRect == NULL)
	return FALSE;
    if (btnPtr->fsState & TBSTATE_HIDDEN)
	return FALSE;
3566

Alexandre Julliard's avatar
Alexandre Julliard committed
3567 3568 3569 3570 3571 3572 3573 3574 3575
    lpRect->left   = btnPtr->rect.left;
    lpRect->right  = btnPtr->rect.right;
    lpRect->bottom = btnPtr->rect.bottom;
    lpRect->top    = btnPtr->rect.top;

    return TRUE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3576
static LRESULT
3577
TOOLBAR_GetMaxSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3578
{
3579
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3580
    LPSIZE lpSize = (LPSIZE)lParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
3581

Alexandre Julliard's avatar
Alexandre Julliard committed
3582 3583 3584
    if (lpSize == NULL)
	return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
3585 3586
    lpSize->cx = infoPtr->rcBound.right - infoPtr->rcBound.left;
    lpSize->cy = infoPtr->rcBound.bottom - infoPtr->rcBound.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
3587

3588
    TRACE("maximum size %d x %d\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
3589 3590
	   infoPtr->rcBound.right - infoPtr->rcBound.left,
	   infoPtr->rcBound.bottom - infoPtr->rcBound.top);
Alexandre Julliard's avatar
Alexandre Julliard committed
3591 3592 3593 3594 3595

    return TRUE;
}


3596
/* << TOOLBAR_GetObject >> */
3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607


static LRESULT
TOOLBAR_GetPadding (HWND hwnd)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    DWORD oldPad;

    oldPad = MAKELONG(infoPtr->szPadding.cx, infoPtr->szPadding.cy);
    return (LRESULT) oldPad;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3608 3609 3610


static LRESULT
3611
TOOLBAR_GetRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3612
{
3613
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3614
    TBUTTON_INFO *btnPtr;
3615 3616
    LPRECT     lpRect;
    INT        nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
3617

3618
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3619 3620 3621
    btnPtr = &infoPtr->buttons[nIndex];
    if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
	return FALSE;
3622
    lpRect = (LPRECT)lParam;
3623 3624
    if (lpRect == NULL)
	return FALSE;
3625

Alexandre Julliard's avatar
Alexandre Julliard committed
3626 3627 3628 3629 3630 3631 3632
    lpRect->left   = btnPtr->rect.left;
    lpRect->right  = btnPtr->rect.right;
    lpRect->bottom = btnPtr->rect.bottom;
    lpRect->top    = btnPtr->rect.top;

    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3633 3634 3635


static LRESULT
3636
TOOLBAR_GetRows (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3637
{
3638
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3639

3640
    return infoPtr->nRows;
Alexandre Julliard's avatar
Alexandre Julliard committed
3641 3642 3643 3644
}


static LRESULT
3645
TOOLBAR_GetState (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3646
{
3647
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3648
    INT nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
3649

3650
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
3651 3652
    if (nIndex == -1)
	return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3653 3654 3655 3656 3657 3658

    return infoPtr->buttons[nIndex].fsState;
}


static LRESULT
3659
TOOLBAR_GetStyle (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3660
{
3661
    return GetWindowLongW(hwnd, GWL_STYLE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3662 3663 3664
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3665
static LRESULT
3666
TOOLBAR_GetTextRows (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3667
{
3668
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3669 3670 3671

    return infoPtr->nMaxTextRows;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3672 3673 3674


static LRESULT
3675
TOOLBAR_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3676
{
3677
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3678

3679 3680
    if ((infoPtr->dwStyle & TBSTYLE_TOOLTIPS) && (infoPtr->hwndToolTip == NULL))
        TOOLBAR_TooltipCreateControl(infoPtr);
3681
    return (LRESULT)infoPtr->hwndToolTip;
Alexandre Julliard's avatar
Alexandre Julliard committed
3682 3683 3684
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3685
static LRESULT
3686
TOOLBAR_GetUnicodeFormat (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3687
{
3688
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3689

Robert Shearman's avatar
Robert Shearman committed
3690
    TRACE("%s hwnd=%p\n",
3691
	   infoPtr->bUnicode ? "TRUE" : "FALSE", hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3692 3693 3694

    return infoPtr->bUnicode;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3695 3696


3697
static inline LRESULT
3698 3699 3700 3701 3702 3703 3704
TOOLBAR_GetVersion (HWND hwnd)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    return infoPtr->iVersion;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3705
static LRESULT
3706
TOOLBAR_HideButton (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3707
{
3708
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3709
    TBUTTON_INFO *btnPtr;
3710
    INT nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
3711

3712 3713
    TRACE("\n");

3714
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3715 3716 3717 3718 3719 3720 3721 3722 3723
    if (nIndex == -1)
	return FALSE;

    btnPtr = &infoPtr->buttons[nIndex];
    if (LOWORD(lParam) == FALSE)
	btnPtr->fsState &= ~TBSTATE_HIDDEN;
    else
	btnPtr->fsState |= TBSTATE_HIDDEN;

3724
    TOOLBAR_LayoutToolbar (hwnd);
3725

3726
    InvalidateRect (hwnd, NULL, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3727 3728 3729 3730 3731

    return TRUE;
}


3732
static inline LRESULT
3733
TOOLBAR_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3734
{
3735
    return TOOLBAR_InternalHitTest (hwnd, (LPPOINT)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
3736 3737 3738
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3739
static LRESULT
3740
TOOLBAR_Indeterminate (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3741
{
3742
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3743
    TBUTTON_INFO *btnPtr;
3744
    INT nIndex;
3745
    DWORD oldState;
Alexandre Julliard's avatar
Alexandre Julliard committed
3746

3747
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3748 3749 3750 3751
    if (nIndex == -1)
	return FALSE;

    btnPtr = &infoPtr->buttons[nIndex];
3752
    oldState = btnPtr->fsState;
Alexandre Julliard's avatar
Alexandre Julliard committed
3753 3754 3755 3756 3757
    if (LOWORD(lParam) == FALSE)
	btnPtr->fsState &= ~TBSTATE_INDETERMINATE;
    else
	btnPtr->fsState |= TBSTATE_INDETERMINATE;

3758
    if(oldState != btnPtr->fsState)
Robert Shearman's avatar
Robert Shearman committed
3759
        InvalidateRect(hwnd, &btnPtr->rect, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3760 3761 3762 3763 3764 3765

    return TRUE;
}


static LRESULT
3766
TOOLBAR_InsertButtonT(HWND hwnd, WPARAM wParam, LPARAM lParam, BOOL fUnicode)
Alexandre Julliard's avatar
Alexandre Julliard committed
3767
{
3768
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3769
    LPTBBUTTON lpTbb = (LPTBBUTTON)lParam;
3770
    INT nIndex = (INT)wParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
3771

3772 3773
    if (lpTbb == NULL)
	return FALSE;
3774

3775 3776
    TOOLBAR_DumpButton(infoPtr, (TBUTTON_INFO *)lpTbb, nIndex, FALSE);

3777 3778 3779 3780 3781 3782 3783
    if (nIndex == -1) {
       /* EPP: this seems to be an undocumented call (from my IE4)
	* I assume in that case that:
	* - index of insertion is at the end of existing buttons
	* I only see this happen with nIndex == -1, but it could have a special
	* meaning (like -nIndex (or ~nIndex) to get the real position of insertion).
	*/
3784
	nIndex = infoPtr->nNumButtons;
3785 3786 3787

    } else if (nIndex < 0)
       return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3788

3789
    TRACE("inserting button index=%d\n", nIndex);
Alexandre Julliard's avatar
Alexandre Julliard committed
3790 3791
    if (nIndex > infoPtr->nNumButtons) {
	nIndex = infoPtr->nNumButtons;
3792
	TRACE("adjust index=%d\n", nIndex);
Alexandre Julliard's avatar
Alexandre Julliard committed
3793 3794 3795
    }

    infoPtr->nNumButtons++;
3796 3797 3798
    infoPtr->buttons = ReAlloc(infoPtr->buttons, sizeof(TBUTTON_INFO) * infoPtr->nNumButtons);
    memmove(&infoPtr->buttons[nIndex+1], &infoPtr->buttons[nIndex],
	    (infoPtr->nNumButtons - nIndex - 1) * sizeof(TBUTTON_INFO));
3799

3800 3801 3802 3803 3804 3805
    /* insert new button */
    infoPtr->buttons[nIndex].iBitmap   = lpTbb->iBitmap;
    infoPtr->buttons[nIndex].idCommand = lpTbb->idCommand;
    infoPtr->buttons[nIndex].fsState   = lpTbb->fsState;
    infoPtr->buttons[nIndex].fsStyle   = lpTbb->fsStyle;
    infoPtr->buttons[nIndex].dwData    = lpTbb->dwData;
3806 3807
    /* if passed string and not index, then add string */
    if(HIWORD(lpTbb->iString) && lpTbb->iString!=-1) {
3808 3809 3810 3811
        if (fUnicode)
            Str_SetPtrW((LPWSTR *)&infoPtr->buttons[nIndex].iString, (LPWSTR)lpTbb->iString);
        else
            Str_SetPtrAtoW((LPWSTR *)&infoPtr->buttons[nIndex].iString, (LPCSTR )lpTbb->iString);
3812 3813 3814
    }
    else
        infoPtr->buttons[nIndex].iString   = lpTbb->iString;
3815

3816
    TOOLBAR_TooltipAddTool(infoPtr, &infoPtr->buttons[nIndex]);
3817

3818 3819 3820 3821
    if (infoPtr->nNumStrings > 0)
        TOOLBAR_CalcToolbar(hwnd);
    else
        TOOLBAR_LayoutToolbar(hwnd);
3822
    TOOLBAR_AutoSize (hwnd);
3823

3824
    InvalidateRect (hwnd, NULL, TRUE);
3825 3826 3827 3828

    return TRUE;
}

3829
/* << TOOLBAR_InsertMarkHitTest >> */
Alexandre Julliard's avatar
Alexandre Julliard committed
3830 3831


Alexandre Julliard's avatar
Alexandre Julliard committed
3832
static LRESULT
3833
TOOLBAR_IsButtonChecked (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3834
{
3835
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3836
    INT nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
3837

3838
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3839
    if (nIndex == -1)
3840
	return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3841 3842 3843 3844 3845 3846

    return (infoPtr->buttons[nIndex].fsState & TBSTATE_CHECKED);
}


static LRESULT
3847
TOOLBAR_IsButtonEnabled (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3848
{
3849
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3850
    INT nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
3851

3852
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3853
    if (nIndex == -1)
3854
	return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3855 3856 3857 3858 3859 3860

    return (infoPtr->buttons[nIndex].fsState & TBSTATE_ENABLED);
}


static LRESULT
3861
TOOLBAR_IsButtonHidden (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3862
{
3863
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3864
    INT nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
3865

3866
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3867
    if (nIndex == -1)
3868
	return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3869 3870 3871 3872 3873 3874

    return (infoPtr->buttons[nIndex].fsState & TBSTATE_HIDDEN);
}


static LRESULT
3875
TOOLBAR_IsButtonHighlighted (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3876
{
3877
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3878
    INT nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
3879

3880
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3881
    if (nIndex == -1)
3882
	return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3883 3884 3885 3886 3887 3888

    return (infoPtr->buttons[nIndex].fsState & TBSTATE_MARKED);
}


static LRESULT
3889
TOOLBAR_IsButtonIndeterminate (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3890
{
3891
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3892
    INT nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
3893

3894
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3895
    if (nIndex == -1)
3896
	return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3897 3898 3899 3900 3901 3902

    return (infoPtr->buttons[nIndex].fsState & TBSTATE_INDETERMINATE);
}


static LRESULT
3903
TOOLBAR_IsButtonPressed (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3904
{
3905
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
3906
    INT nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
3907

3908
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3909
    if (nIndex == -1)
3910
	return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3911 3912 3913 3914 3915

    return (infoPtr->buttons[nIndex].fsState & TBSTATE_PRESSED);
}


3916 3917 3918 3919 3920 3921 3922
static LRESULT
TOOLBAR_LoadImages (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TBADDBITMAP tbab;
    tbab.hInst = (HINSTANCE)lParam;
    tbab.nID = (UINT_PTR)wParam;

3923
    TRACE("hwnd = %p, hInst = %p, nID = %lu\n", hwnd, tbab.hInst, tbab.nID);
3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977

    return TOOLBAR_AddBitmap(hwnd, 0, (LPARAM)&tbab);
}


static LRESULT
TOOLBAR_MapAccelerator (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    WCHAR wAccel = (WCHAR)wParam;
    UINT* pIDButton = (UINT*)lParam;
    WCHAR wszAccel[] = {'&',wAccel,0};
    int i;
    
    TRACE("hwnd = %p, wAccel = %x(%s), pIDButton = %p\n",
        hwnd, wAccel, debugstr_wn(&wAccel,1), pIDButton);
    
    for (i = 0; i < infoPtr->nNumButtons; i++)
    {
        TBUTTON_INFO *btnPtr = infoPtr->buttons+i;
        if (!(btnPtr->fsStyle & BTNS_NOPREFIX) &&
            !(btnPtr->fsState & TBSTATE_HIDDEN))
        {
            int iLen = strlenW(wszAccel);
            LPCWSTR lpszStr = TOOLBAR_GetText(infoPtr, btnPtr);
            
            if (!lpszStr)
                continue;

            while (*lpszStr)
            {
                if ((lpszStr[0] == '&') && (lpszStr[1] == '&'))
                {
                    lpszStr += 2;
                    continue;
                }
                if (!strncmpiW(lpszStr, wszAccel, iLen))
                {
                    *pIDButton = btnPtr->idCommand;
                    return TRUE;
                }
                lpszStr++;
            }
        }
    }
    return FALSE;
}


static LRESULT
TOOLBAR_MarkButton (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    INT nIndex;
3978 3979
    DWORD oldState;
    TBUTTON_INFO *btnPtr;
3980

3981
    TRACE("hwnd = %p, wParam = %ld, lParam = 0x%08lx\n", hwnd, wParam, lParam);
3982 3983 3984 3985 3986

    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
    if (nIndex == -1)
        return FALSE;

3987 3988 3989
    btnPtr = &infoPtr->buttons[nIndex];
    oldState = btnPtr->fsState;

3990
    if (LOWORD(lParam))
3991
        btnPtr->fsState |= TBSTATE_MARKED;
3992
    else
3993 3994 3995 3996
        btnPtr->fsState &= ~TBSTATE_MARKED;

    if(oldState != btnPtr->fsState)
        InvalidateRect(hwnd, &btnPtr->rect, TRUE);
3997 3998 3999 4000

    return TRUE;
}

Robert Shearman's avatar
Robert Shearman committed
4001 4002

/* fixes up an index of a button affected by a move */
4003
static inline void TOOLBAR_MoveFixupIndex(INT* pIndex, INT nIndex, INT nMoveIndex, BOOL bMoveUp)
Robert Shearman's avatar
Robert Shearman committed
4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030
{
    if (bMoveUp)
    {
        if (*pIndex > nIndex && *pIndex <= nMoveIndex)
            (*pIndex)--;
        else if (*pIndex == nIndex)
            *pIndex = nMoveIndex;
    }
    else
    {
        if (*pIndex >= nMoveIndex && *pIndex < nIndex)
            (*pIndex)++;
        else if (*pIndex == nIndex)
            *pIndex = nMoveIndex;
    }
}


static LRESULT
TOOLBAR_MoveButton (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    INT nIndex;
    INT nCount;
    INT nMoveIndex = (INT)lParam;
    TBUTTON_INFO button;

4031
    TRACE("hwnd=%p, wParam=%ld, lParam=%ld\n", hwnd, wParam, lParam);
Robert Shearman's avatar
Robert Shearman committed
4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065

    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, TRUE);
    if ((nIndex == -1) || (nMoveIndex < 0))
        return FALSE;

    if (nMoveIndex > infoPtr->nNumButtons - 1)
        nMoveIndex = infoPtr->nNumButtons - 1;

    button = infoPtr->buttons[nIndex];

    /* move button right */
    if (nIndex < nMoveIndex)
    {
        nCount = nMoveIndex - nIndex;
        memmove(&infoPtr->buttons[nIndex], &infoPtr->buttons[nIndex+1], nCount*sizeof(TBUTTON_INFO));
        infoPtr->buttons[nMoveIndex] = button;

        TOOLBAR_MoveFixupIndex(&infoPtr->nButtonDown, nIndex, nMoveIndex, TRUE);
        TOOLBAR_MoveFixupIndex(&infoPtr->nButtonDrag, nIndex, nMoveIndex, TRUE);
        TOOLBAR_MoveFixupIndex(&infoPtr->nOldHit, nIndex, nMoveIndex, TRUE);
        TOOLBAR_MoveFixupIndex(&infoPtr->nHotItem, nIndex, nMoveIndex, TRUE);
    }
    else if (nIndex > nMoveIndex) /* move button left */
    {
        nCount = nIndex - nMoveIndex;
        memmove(&infoPtr->buttons[nMoveIndex+1], &infoPtr->buttons[nMoveIndex], nCount*sizeof(TBUTTON_INFO));
        infoPtr->buttons[nMoveIndex] = button;

        TOOLBAR_MoveFixupIndex(&infoPtr->nButtonDown, nIndex, nMoveIndex, FALSE);
        TOOLBAR_MoveFixupIndex(&infoPtr->nButtonDrag, nIndex, nMoveIndex, FALSE);
        TOOLBAR_MoveFixupIndex(&infoPtr->nOldHit, nIndex, nMoveIndex, FALSE);
        TOOLBAR_MoveFixupIndex(&infoPtr->nHotItem, nIndex, nMoveIndex, FALSE);
    }

4066
    TOOLBAR_LayoutToolbar(hwnd);
4067
    TOOLBAR_AutoSize(hwnd);
Robert Shearman's avatar
Robert Shearman committed
4068 4069 4070 4071
    InvalidateRect(hwnd, NULL, TRUE);

    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4072 4073 4074


static LRESULT
4075
TOOLBAR_PressButton (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4076
{
4077
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
4078
    TBUTTON_INFO *btnPtr;
4079
    INT nIndex;
4080
    DWORD oldState;
Alexandre Julliard's avatar
Alexandre Julliard committed
4081

4082
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4083 4084 4085 4086
    if (nIndex == -1)
	return FALSE;

    btnPtr = &infoPtr->buttons[nIndex];
4087
    oldState = btnPtr->fsState;
Alexandre Julliard's avatar
Alexandre Julliard committed
4088 4089 4090 4091 4092
    if (LOWORD(lParam) == FALSE)
	btnPtr->fsState &= ~TBSTATE_PRESSED;
    else
	btnPtr->fsState |= TBSTATE_PRESSED;

4093
    if(oldState != btnPtr->fsState)
Robert Shearman's avatar
Robert Shearman committed
4094
        InvalidateRect(hwnd, &btnPtr->rect, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4095 4096 4097 4098

    return TRUE;
}

4099 4100
/* FIXME: there might still be some confusion her between number of buttons
 * and number of bitmaps */
4101 4102 4103 4104 4105 4106 4107
static LRESULT
TOOLBAR_ReplaceBitmap (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    LPTBREPLACEBITMAP lpReplace = (LPTBREPLACEBITMAP) lParam;
    HBITMAP hBitmap;
    int i = 0, nOldButtons = 0, pos = 0;
4108
    int nOldBitmaps, nNewBitmaps = 0;
4109
    HIMAGELIST himlDef = 0;
4110

4111
    TRACE("hInstOld %p nIDOld %lx hInstNew %p nIDNew %lx nButtons %x\n",
4112 4113
          lpReplace->hInstOld, lpReplace->nIDOld, lpReplace->hInstNew, lpReplace->nIDNew,
          lpReplace->nButtons);
Alexandre Julliard's avatar
Alexandre Julliard committed
4114

4115
    if (lpReplace->hInstOld == HINST_COMMCTRL)
4116 4117 4118 4119 4120 4121
    {
        FIXME("changing standard bitmaps not implemented\n");
        return FALSE;
    }
    else if (lpReplace->hInstOld != 0)
        FIXME("resources not in the current module not implemented\n");
4122

4123
    TRACE("To be replaced hInstOld %p nIDOld %lx\n", lpReplace->hInstOld, lpReplace->nIDOld);
4124 4125
    for (i = 0; i < infoPtr->nNumBitmapInfos; i++) {
        TBITMAP_INFO *tbi = &infoPtr->bitmaps[i];
4126
        TRACE("tbimapinfo %d hInstOld %p nIDOld %x\n", i, tbi->hInst, tbi->nID);
4127 4128
        if (tbi->hInst == lpReplace->hInstOld && tbi->nID == lpReplace->nIDOld)
        {
4129
            TRACE("Found: nButtons %d hInst %p nID %x\n", tbi->nButtons, tbi->hInst, tbi->nID);
4130 4131 4132 4133
            nOldButtons = tbi->nButtons;
            tbi->nButtons = lpReplace->nButtons;
            tbi->hInst = lpReplace->hInstNew;
            tbi->nID = lpReplace->nIDNew;
4134
            TRACE("tbimapinfo changed %d hInstOld %p nIDOld %x\n", i, tbi->hInst, tbi->nID);
4135 4136 4137 4138 4139 4140 4141
            break;
        }
        pos += tbi->nButtons;
    }

    if (nOldButtons == 0)
    {
4142
        WARN("No hinst/bitmap found! hInst %p nID %lx\n", lpReplace->hInstOld, lpReplace->nIDOld);
4143 4144
        return FALSE;
    }
4145
    
4146 4147 4148 4149 4150 4151 4152 4153
    /* copy the bitmap before adding it as ImageList_AddMasked modifies the
    * bitmap
    */
    if (lpReplace->hInstNew)
        hBitmap = LoadBitmapW(lpReplace->hInstNew,(LPWSTR)lpReplace->nIDNew);
    else
        hBitmap = CopyImage((HBITMAP)lpReplace->nIDNew, IMAGE_BITMAP, 0, 0, 0);

4154 4155
    himlDef = GETDEFIMAGELIST(infoPtr, 0); /* fixme: correct? */
    nOldBitmaps = ImageList_GetImageCount(himlDef);
4156

4157
    /* ImageList_Replace(GETDEFIMAGELIST(), pos, hBitmap, NULL); */
4158

4159
    for (i = pos + nOldBitmaps - 1; i >= pos; i--)
4160
        ImageList_Remove(himlDef, i);
4161

4162
    if (hBitmap)
4163
    {
4164
       ImageList_AddMasked (himlDef, hBitmap, comctl32_color.clrBtnFace);
4165
       nNewBitmaps = ImageList_GetImageCount(himlDef);
4166
       DeleteObject(hBitmap);
4167
    }
4168

4169 4170 4171 4172 4173
    infoPtr->nNumBitmaps = infoPtr->nNumBitmaps - nOldBitmaps + nNewBitmaps;

    TRACE(" pos %d  %d old bitmaps replaced by %d new ones.\n",
            pos, nOldBitmaps, nNewBitmaps);

Robert Shearman's avatar
Robert Shearman committed
4174
    InvalidateRect(hwnd, NULL, TRUE);
4175 4176
    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4177

4178 4179 4180

/* helper for TOOLBAR_SaveRestoreW */
static BOOL
4181
TOOLBAR_Save(const TOOLBAR_INFO *infoPtr, const TBSAVEPARAMSW *lpSave)
Alexandre Julliard's avatar
Alexandre Julliard committed
4182
{
4183 4184
    FIXME("save to %s %s\n", debugstr_w(lpSave->pszSubKey),
        debugstr_w(lpSave->pszValueName));
Alexandre Julliard's avatar
Alexandre Julliard committed
4185

4186 4187
    return FALSE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4188 4189


4190 4191 4192 4193 4194
/* helper for TOOLBAR_Restore */
static void
TOOLBAR_DeleteAllButtons(TOOLBAR_INFO *infoPtr)
{
    INT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
4195

4196 4197
    for (i = 0; i < infoPtr->nNumButtons; i++)
    {
4198
        TOOLBAR_TooltipDelTool(infoPtr, &infoPtr->buttons[i]);
Alexandre Julliard's avatar
Alexandre Julliard committed
4199 4200
    }

4201 4202 4203 4204 4205 4206 4207 4208
    Free(infoPtr->buttons);
    infoPtr->buttons = NULL;
    infoPtr->nNumButtons = 0;
}


/* helper for TOOLBAR_SaveRestoreW */
static BOOL
4209
TOOLBAR_Restore(TOOLBAR_INFO *infoPtr, const TBSAVEPARAMSW *lpSave)
4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232
{
    LONG res;
    HKEY hkey = NULL;
    BOOL ret = FALSE;
    DWORD dwType;
    DWORD dwSize = 0;
    NMTBRESTORE nmtbr;

    /* restore toolbar information */
    TRACE("restore from %s %s\n", debugstr_w(lpSave->pszSubKey),
        debugstr_w(lpSave->pszValueName));

    memset(&nmtbr, 0, sizeof(nmtbr));

    res = RegOpenKeyExW(lpSave->hkr, lpSave->pszSubKey, 0,
        KEY_QUERY_VALUE, &hkey);
    if (!res)
        res = RegQueryValueExW(hkey, lpSave->pszValueName, NULL, &dwType,
            NULL, &dwSize);
    if (!res && dwType != REG_BINARY)
        res = ERROR_FILE_NOT_FOUND;
    if (!res)
    {
4233
        nmtbr.pData = Alloc(dwSize);
4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281
        nmtbr.cbData = (UINT)dwSize;
        if (!nmtbr.pData) res = ERROR_OUTOFMEMORY;
    }
    if (!res)
        res = RegQueryValueExW(hkey, lpSave->pszValueName, NULL, &dwType,
            (LPBYTE)nmtbr.pData, &dwSize);
    if (!res)
    {
        nmtbr.pCurrent = nmtbr.pData;
        nmtbr.iItem = -1;
        nmtbr.cbBytesPerRecord = sizeof(DWORD);
        nmtbr.cButtons = nmtbr.cbData / nmtbr.cbBytesPerRecord;

        if (!TOOLBAR_SendNotify(&nmtbr.hdr, infoPtr, TBN_RESTORE))
        {
            INT i;

            /* remove all existing buttons as this function is designed to
             * restore the toolbar to a previously saved state */
            TOOLBAR_DeleteAllButtons(infoPtr);

            for (i = 0; i < nmtbr.cButtons; i++)
            {
                nmtbr.iItem = i;
                nmtbr.tbButton.iBitmap = -1;
                nmtbr.tbButton.fsState = 0;
                nmtbr.tbButton.fsStyle = 0;
                nmtbr.tbButton.idCommand = 0;
                if (*nmtbr.pCurrent == (DWORD)-1)
                {
                    /* separator */
                    nmtbr.tbButton.fsStyle = TBSTYLE_SEP;
                    nmtbr.tbButton.iBitmap = SEPARATOR_WIDTH;
                }
                else if (*nmtbr.pCurrent == (DWORD)-2)
                    /* hidden button */
                    nmtbr.tbButton.fsState = TBSTATE_HIDDEN;
                else
                    nmtbr.tbButton.idCommand = (int)*nmtbr.pCurrent;

                nmtbr.pCurrent++;
                
                TOOLBAR_SendNotify(&nmtbr.hdr, infoPtr, TBN_RESTORE);

                /* can't contain real string as we don't know whether
                 * the client put an ANSI or Unicode string in there */
                if (HIWORD(nmtbr.tbButton.iString))
                    nmtbr.tbButton.iString = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
4282

4283 4284
                TOOLBAR_InsertButtonT(infoPtr->hwndSelf, -1,
                    (LPARAM)&nmtbr.tbButton, TRUE);
4285
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
4286

4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304
            /* do legacy notifications */
            if (infoPtr->iVersion < 5)
            {
                /* FIXME: send TBN_BEGINADJUST */
                FIXME("send TBN_GETBUTTONINFO for each button\n");
                /* FIXME: send TBN_ENDADJUST */
            }

            /* remove all uninitialised buttons
             * note: loop backwards to avoid having to fixup i on a
             * delete */
            for (i = infoPtr->nNumButtons - 1; i >= 0; i--)
                if (infoPtr->buttons[i].iBitmap == -1)
                    TOOLBAR_DeleteButton(infoPtr->hwndSelf, i, 0);

            /* only indicate success if at least one button survived */
            if (infoPtr->nNumButtons > 0) ret = TRUE;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
4305
    }
4306
    Free (nmtbr.pData);
4307
    RegCloseKey(hkey);
Alexandre Julliard's avatar
Alexandre Julliard committed
4308

4309
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
4310 4311 4312
}


4313
static LRESULT
4314
TOOLBAR_SaveRestoreW (HWND hwnd, WPARAM wParam, const TBSAVEPARAMSW *lpSave)
4315 4316 4317
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);

4318
    if (lpSave == NULL) return 0;
4319

4320 4321 4322 4323 4324
    if (wParam)
        return TOOLBAR_Save(infoPtr, lpSave);
    else
        return TOOLBAR_Restore(infoPtr, lpSave);
}
4325 4326


4327
static LRESULT
4328
TOOLBAR_SaveRestoreA (HWND hwnd, WPARAM wParam, const TBSAVEPARAMSA *lpSave)
4329
{
4330
    LPWSTR pszValueName = 0, pszSubKey = 0;
4331
    TBSAVEPARAMSW SaveW;
4332
    LRESULT result = 0;
4333
    int len;
4334

4335
    if (lpSave == NULL) return 0;
4336

4337
    len = MultiByteToWideChar(CP_ACP, 0, lpSave->pszSubKey, -1, NULL, 0);
4338
    pszSubKey = Alloc(len * sizeof(WCHAR));
4339 4340
    if (pszSubKey) goto exit;
    MultiByteToWideChar(CP_ACP, 0, lpSave->pszSubKey, -1, pszSubKey, len);
4341

4342
    len = MultiByteToWideChar(CP_ACP, 0, lpSave->pszValueName, -1, NULL, 0);
4343
    pszValueName = Alloc(len * sizeof(WCHAR));
4344 4345 4346 4347 4348 4349 4350 4351 4352
    if (!pszValueName) goto exit;
    MultiByteToWideChar(CP_ACP, 0, lpSave->pszValueName, -1, pszValueName, len);

    SaveW.pszValueName = pszValueName;
    SaveW.pszSubKey = pszSubKey;
    SaveW.hkr = lpSave->hkr;
    result = TOOLBAR_SaveRestoreW(hwnd, wParam, &SaveW);

exit:
4353 4354
    Free (pszValueName);
    Free (pszSubKey);
4355

4356
    return result;
4357 4358 4359 4360 4361 4362 4363 4364 4365
}


static LRESULT
TOOLBAR_SetAnchorHighlight (HWND hwnd, WPARAM wParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    BOOL bOldAnchor = infoPtr->bAnchor;

4366 4367
    TRACE("hwnd=%p, bAnchor = %s\n", hwnd, wParam ? "TRUE" : "FALSE");

4368 4369
    infoPtr->bAnchor = (BOOL)wParam;

4370
    /* Native does not remove the hot effect from an already hot button */
4371

4372 4373
    return (LRESULT)bOldAnchor;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4374

Alexandre Julliard's avatar
Alexandre Julliard committed
4375

Alexandre Julliard's avatar
Alexandre Julliard committed
4376
static LRESULT
4377
TOOLBAR_SetBitmapSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4378
{
4379
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
4380
    HIMAGELIST himlDef = GETDEFIMAGELIST(infoPtr, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
4381

4382
    TRACE("hwnd=%p, wParam=%ld, lParam=%ld\n", hwnd, wParam, lParam);
Robert Shearman's avatar
Robert Shearman committed
4383 4384

    if (wParam != 0)
4385
        FIXME("wParam is %ld. Perhaps image list index?\n", wParam);
Robert Shearman's avatar
Robert Shearman committed
4386

4387 4388 4389 4390 4391
    if (LOWORD(lParam) == 0)
        lParam = MAKELPARAM(1, HIWORD(lParam));

    if (HIWORD(lParam)==0)
        lParam = MAKELPARAM(LOWORD(lParam), 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
4392

4393 4394 4395 4396 4397
    if (infoPtr->nNumButtons > 0)
        WARN("%d buttons, undoc increase to bitmap size : %d-%d -> %d-%d\n",
             infoPtr->nNumButtons,
             infoPtr->nBitmapWidth, infoPtr->nBitmapHeight,
             LOWORD(lParam), HIWORD(lParam));
4398

4399
    infoPtr->nBitmapWidth = (INT)LOWORD(lParam);
4400
    infoPtr->nBitmapHeight = (INT)HIWORD(lParam);
4401

Robert Shearman's avatar
Robert Shearman committed
4402 4403 4404 4405 4406
    if ((himlDef == infoPtr->himlInt) &&
        (ImageList_GetImageCount(infoPtr->himlInt) == 0))
    {
        ImageList_SetIconSize(infoPtr->himlInt, infoPtr->nBitmapWidth,
            infoPtr->nBitmapHeight);
4407
    }
4408

4409
    TOOLBAR_CalcToolbar(hwnd);
4410
    InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4411 4412 4413 4414
    return TRUE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4415
static LRESULT
4416
TOOLBAR_SetButtonInfoA (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4417
{
4418
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
4419
    LPTBBUTTONINFOA lptbbi = (LPTBBUTTONINFOA)lParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
4420
    TBUTTON_INFO *btnPtr;
4421
    INT nIndex;
Robert Shearman's avatar
Robert Shearman committed
4422
    RECT oldBtnRect;
Alexandre Julliard's avatar
Alexandre Julliard committed
4423 4424 4425

    if (lptbbi == NULL)
	return FALSE;
Juergen Schmied's avatar
Juergen Schmied committed
4426
    if (lptbbi->cbSize < sizeof(TBBUTTONINFOA))
Alexandre Julliard's avatar
Alexandre Julliard committed
4427
	return FALSE;
4428

4429 4430
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam,
				     lptbbi->dwMask & 0x80000000);
Alexandre Julliard's avatar
Alexandre Julliard committed
4431 4432 4433 4434 4435 4436 4437 4438 4439 4440
    if (nIndex == -1)
	return FALSE;

    btnPtr = &infoPtr->buttons[nIndex];
    if (lptbbi->dwMask & TBIF_COMMAND)
	btnPtr->idCommand = lptbbi->idCommand;
    if (lptbbi->dwMask & TBIF_IMAGE)
	btnPtr->iBitmap = lptbbi->iImage;
    if (lptbbi->dwMask & TBIF_LPARAM)
	btnPtr->dwData = lptbbi->lParam;
Robert Shearman's avatar
Robert Shearman committed
4441 4442
    if (lptbbi->dwMask & TBIF_SIZE)
	btnPtr->cx = lptbbi->cx;
Alexandre Julliard's avatar
Alexandre Julliard committed
4443 4444 4445 4446 4447
    if (lptbbi->dwMask & TBIF_STATE)
	btnPtr->fsState = lptbbi->fsState;
    if (lptbbi->dwMask & TBIF_STYLE)
	btnPtr->fsStyle = lptbbi->fsStyle;

Frank Richter's avatar
Frank Richter committed
4448
    if ((lptbbi->dwMask & TBIF_TEXT) && ((INT_PTR)lptbbi->pszText != -1)) {
4449 4450 4451
        if ((HIWORD(btnPtr->iString) == 0) || (btnPtr->iString == -1))
	    /* iString is index, zero it to make Str_SetPtr succeed */
	    btnPtr->iString=0;
4452

4453
         Str_SetPtrAtoW ((LPWSTR *)&btnPtr->iString, lptbbi->pszText);
Alexandre Julliard's avatar
Alexandre Julliard committed
4454
    }
Robert Shearman's avatar
Robert Shearman committed
4455 4456 4457 4458 4459 4460 4461 4462 4463 4464

    /* save the button rect to see if we need to redraw the whole toolbar */
    oldBtnRect = btnPtr->rect;
    TOOLBAR_CalcToolbar(hwnd);

    if (!EqualRect(&oldBtnRect, &btnPtr->rect))
        InvalidateRect(hwnd, NULL, TRUE);
    else
        InvalidateRect(hwnd, &btnPtr->rect, TRUE);

Alexandre Julliard's avatar
Alexandre Julliard committed
4465 4466 4467 4468
    return TRUE;
}


4469 4470 4471 4472 4473 4474 4475
static LRESULT
TOOLBAR_SetButtonInfoW (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    LPTBBUTTONINFOW lptbbi = (LPTBBUTTONINFOW)lParam;
    TBUTTON_INFO *btnPtr;
    INT nIndex;
Robert Shearman's avatar
Robert Shearman committed
4476
    RECT oldBtnRect;
4477 4478 4479 4480 4481 4482

    if (lptbbi == NULL)
	return FALSE;
    if (lptbbi->cbSize < sizeof(TBBUTTONINFOW))
	return FALSE;

4483 4484
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam,
				     lptbbi->dwMask & 0x80000000);
4485 4486 4487 4488 4489 4490 4491 4492 4493 4494
    if (nIndex == -1)
	return FALSE;

    btnPtr = &infoPtr->buttons[nIndex];
    if (lptbbi->dwMask & TBIF_COMMAND)
	btnPtr->idCommand = lptbbi->idCommand;
    if (lptbbi->dwMask & TBIF_IMAGE)
	btnPtr->iBitmap = lptbbi->iImage;
    if (lptbbi->dwMask & TBIF_LPARAM)
	btnPtr->dwData = lptbbi->lParam;
Robert Shearman's avatar
Robert Shearman committed
4495 4496
    if (lptbbi->dwMask & TBIF_SIZE)
	btnPtr->cx = lptbbi->cx;
4497 4498 4499 4500 4501
    if (lptbbi->dwMask & TBIF_STATE)
	btnPtr->fsState = lptbbi->fsState;
    if (lptbbi->dwMask & TBIF_STYLE)
	btnPtr->fsStyle = lptbbi->fsStyle;

Frank Richter's avatar
Frank Richter committed
4502
    if ((lptbbi->dwMask & TBIF_TEXT) && ((INT_PTR)lptbbi->pszText != -1)) {
4503 4504 4505 4506
        if ((HIWORD(btnPtr->iString) == 0) || (btnPtr->iString == -1))
	    /* iString is index, zero it to make Str_SetPtr succeed */
	    btnPtr->iString=0;
        Str_SetPtrW ((LPWSTR *)&btnPtr->iString, lptbbi->pszText);
4507
    }
Robert Shearman's avatar
Robert Shearman committed
4508 4509 4510 4511 4512 4513 4514 4515 4516 4517

    /* save the button rect to see if we need to redraw the whole toolbar */
    oldBtnRect = btnPtr->rect;
    TOOLBAR_CalcToolbar(hwnd);

    if (!EqualRect(&oldBtnRect, &btnPtr->rect))
        InvalidateRect(hwnd, NULL, TRUE);
    else
        InvalidateRect(hwnd, &btnPtr->rect, TRUE);

4518 4519
    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4520 4521 4522


static LRESULT
4523
TOOLBAR_SetButtonSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4524
{
4525
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
4526
    INT cx = (short)LOWORD(lParam), cy = (short)HIWORD(lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
4527

4528
    if ((cx < 0) || (cy < 0))
4529
    {
4530
        ERR("invalid parameter 0x%08x\n", (DWORD)lParam);
4531
        return FALSE;
4532
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4533

4534 4535
    TRACE("%p, cx = %d, cy = %d\n", hwnd, cx, cy);

4536
    /* The documentation claims you can only change the button size before
4537 4538
     * any button has been added. But this is wrong.
     * WINZIP32.EXE (ver 8) calls this on one of its buttons after adding
4539 4540 4541
     * it to the toolbar, and it checks that the return value is nonzero - mjm
     * Further testing shows that we must actually perform the change too.
     */
4542 4543 4544
    /*
     * The documentation also does not mention that if 0 is supplied for
     * either size, the system changes it to the default of 24 wide and
4545
     * 22 high. Demonstarted in ControlSpy Toolbar. GLA 3/02
4546
     */
4547 4548 4549 4550
    if (cx == 0) cx = 24;
    if (cy == 0) cx = 22;
    
    cx = max(cx, infoPtr->szPadding.cx + infoPtr->nBitmapWidth);
4551
    cy = max(cy, infoPtr->szPadding.cy + infoPtr->nBitmapHeight);
4552 4553 4554 4555 4556 4557

    infoPtr->nButtonWidth = cx;
    infoPtr->nButtonHeight = cy;
    
    infoPtr->iTopMargin = default_top_margin(infoPtr);
    TOOLBAR_LayoutToolbar(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
4558 4559 4560 4561
    return TRUE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4562
static LRESULT
4563
TOOLBAR_SetButtonWidth (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4564
{
4565
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
4566

4567
    /* if setting to current values, ignore */
4568 4569
    if ((infoPtr->cxMin == (short)LOWORD(lParam)) &&
	(infoPtr->cxMax == (short)HIWORD(lParam))) {
4570 4571
	TRACE("matches current width, min=%d, max=%d, no recalc\n",
	      infoPtr->cxMin, infoPtr->cxMax);
4572
	return TRUE;
4573
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4574

4575
    /* save new values */
4576 4577
    infoPtr->cxMin = (short)LOWORD(lParam);
    infoPtr->cxMax = (short)HIWORD(lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
4578

4579 4580 4581
    /* otherwise we need to recalc the toolbar and in some cases
       recalc the bounding rectangle (does DrawText w/ DT_CALCRECT
       which doesn't actually draw - GA). */
4582
    TRACE("number of buttons %d, cx=%d, cy=%d, recalcing\n",
4583 4584 4585 4586 4587 4588
	infoPtr->nNumButtons, infoPtr->cxMin, infoPtr->cxMax);

    TOOLBAR_CalcToolbar (hwnd);

    InvalidateRect (hwnd, NULL, TRUE);

Alexandre Julliard's avatar
Alexandre Julliard committed
4589 4590
    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4591 4592 4593


static LRESULT
4594
TOOLBAR_SetCmdId (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4595
{
4596
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
4597
    INT nIndex = (INT)wParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
4598 4599 4600 4601

    if ((nIndex < 0) || (nIndex >= infoPtr->nNumButtons))
	return FALSE;

4602
    infoPtr->buttons[nIndex].idCommand = (INT)lParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
4603

Alexandre Julliard's avatar
Alexandre Julliard committed
4604 4605
    if (infoPtr->hwndToolTip) {

4606
	FIXME("change tool tip!\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
4607 4608 4609

    }

Alexandre Julliard's avatar
Alexandre Julliard committed
4610 4611 4612 4613
    return TRUE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4614
static LRESULT
4615
TOOLBAR_SetDisabledImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4616
{
4617
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
4618
    HIMAGELIST himl = (HIMAGELIST)lParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
4619
    HIMAGELIST himlTemp;
4620
    INT id = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
4621

4622 4623
    if (infoPtr->iVersion >= 5)
        id = wParam;
4624

4625 4626
    himlTemp = TOOLBAR_InsertImageList(&infoPtr->himlDis, 
        &infoPtr->cimlDis, himl, id);
Alexandre Julliard's avatar
Alexandre Julliard committed
4627 4628 4629

    /* FIXME: redraw ? */

4630
    return (LRESULT)himlTemp;
Alexandre Julliard's avatar
Alexandre Julliard committed
4631 4632 4633
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4634
static LRESULT
4635
TOOLBAR_SetDrawTextFlags (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4636
{
4637
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
4638 4639
    DWORD dwTemp;

4640
    TRACE("hwnd = %p, dwMask = 0x%08x, dwDTFlags = 0x%08x\n", hwnd, (DWORD)wParam, (DWORD)lParam);
4641

Alexandre Julliard's avatar
Alexandre Julliard committed
4642 4643 4644 4645 4646 4647
    dwTemp = infoPtr->dwDTFlags;
    infoPtr->dwDTFlags =
	(infoPtr->dwDTFlags & (DWORD)wParam) | (DWORD)lParam;

    return (LRESULT)dwTemp;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4648

Robert Shearman's avatar
Robert Shearman committed
4649 4650 4651 4652 4653 4654
/* This function differs a bit from what MSDN says it does:
 * 1. lParam contains extended style flags to OR with current style
 *  (MSDN isn't clear on the OR bit)
 * 2. wParam appears to contain extended style flags to be reset
 *  (MSDN says that this parameter is reserved)
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
4655
static LRESULT
4656
TOOLBAR_SetExtendedStyle (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4657
{
4658
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
4659 4660 4661
    DWORD dwTemp;

    dwTemp = infoPtr->dwExStyle;
Robert Shearman's avatar
Robert Shearman committed
4662
    infoPtr->dwExStyle &= ~wParam;
4663
    infoPtr->dwExStyle |= (DWORD)lParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
4664

4665
    TRACE("new style 0x%08x\n", infoPtr->dwExStyle);
4666 4667

    if (infoPtr->dwExStyle & ~TBSTYLE_EX_ALL)
4668
	FIXME("Unknown Toolbar Extended Style 0x%08x. Please report.\n",
4669 4670
	      (infoPtr->dwExStyle & ~TBSTYLE_EX_ALL));

4671 4672 4673 4674
    TOOLBAR_CalcToolbar (hwnd);

    TOOLBAR_AutoSize(hwnd);

Robert Shearman's avatar
Robert Shearman committed
4675
    InvalidateRect(hwnd, NULL, TRUE);
4676

4677
    return (LRESULT)dwTemp;
Alexandre Julliard's avatar
Alexandre Julliard committed
4678 4679 4680 4681
}


static LRESULT
4682
TOOLBAR_SetHotImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4683
{
4684
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
4685
    HIMAGELIST himlTemp;
4686 4687
    HIMAGELIST himl = (HIMAGELIST)lParam;
    INT id = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
4688

4689 4690
    if (infoPtr->iVersion >= 5)
        id = wParam;
Guy Albertelli's avatar
Guy Albertelli committed
4691

4692 4693
    TRACE("hwnd = %p, himl = %p, id = %d\n", hwnd, himl, id);

4694 4695
    himlTemp = TOOLBAR_InsertImageList(&infoPtr->himlHot, 
        &infoPtr->cimlHot, himl, id);
Alexandre Julliard's avatar
Alexandre Julliard committed
4696 4697 4698

    /* FIXME: redraw ? */

4699
    return (LRESULT)himlTemp;
Alexandre Julliard's avatar
Alexandre Julliard committed
4700 4701 4702
}


4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715
/* Makes previous hot button no longer hot, makes the specified
 * button hot and sends appropriate notifications. dwReason is one or
 * more HICF_ flags. Specify nHit < 0 to make no buttons hot.
 * NOTE 1: this function does not validate nHit
 * NOTE 2: the name of this function is completely made up and
 * not based on any documentation from Microsoft. */
static void
TOOLBAR_SetHotItemEx (TOOLBAR_INFO *infoPtr, INT nHit, DWORD dwReason)
{
    if (infoPtr->nHotItem != nHit)
    {
        NMTBHOTITEM nmhotitem;
        TBUTTON_INFO *btnPtr = NULL, *oldBtnPtr = NULL;
4716

4717
        nmhotitem.dwFlags = dwReason;
4718 4719 4720
        if(infoPtr->nHotItem >= 0)
        {
            oldBtnPtr = &infoPtr->buttons[infoPtr->nHotItem];
4721
            nmhotitem.idOld = oldBtnPtr->idCommand;
4722
        }
4723
        else
4724
        {
4725
            nmhotitem.dwFlags |= HICF_ENTERING;
4726 4727
            nmhotitem.idOld = 0;
        }
4728 4729

        if (nHit >= 0)
4730
        {
4731
            btnPtr = &infoPtr->buttons[nHit];
4732 4733
            nmhotitem.idNew = btnPtr->idCommand;
        }
4734
	else
4735
	{
4736
	    nmhotitem.dwFlags |= HICF_LEAVING;
4737 4738
	    nmhotitem.idNew = 0;
	}
4739

4740 4741 4742
	/* now change the hot and invalidate the old and new buttons - if the
	 * parent agrees */
	if (!TOOLBAR_SendNotify(&nmhotitem.hdr, infoPtr, TBN_HOTITEMCHANGE))
4743
	{
4744 4745 4746 4747
            if (oldBtnPtr) {
                oldBtnPtr->bHot = FALSE;
                InvalidateRect(infoPtr->hwndSelf, &oldBtnPtr->rect, TRUE);
            }
4748 4749 4750 4751 4752 4753 4754 4755
            /* setting disabled buttons as hot fails even if the notify contains the button id */
            if (btnPtr && (btnPtr->fsState & TBSTATE_ENABLED)) {
                btnPtr->bHot = TRUE;
                InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
                infoPtr->nHotItem = nHit;
            }
            else
                infoPtr->nHotItem = -1;            
4756 4757 4758 4759
        }
    }
}

4760 4761 4762 4763 4764 4765
static LRESULT
TOOLBAR_SetHotItem (HWND hwnd, WPARAM wParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr(hwnd);
    INT nOldHotItem = infoPtr->nHotItem;

4766 4767
    TRACE("hwnd = %p, nHit = %d\n", hwnd, (INT)wParam);

4768 4769 4770 4771
    if ((INT)wParam > infoPtr->nNumButtons)
        return infoPtr->nHotItem;
    
    if ((INT)wParam < 0)
4772 4773 4774 4775
        wParam = -1;

    /* NOTE: an application can still remove the hot item even if anchor
     * highlighting is enabled */
4776

4777
    TOOLBAR_SetHotItemEx(infoPtr, wParam, HICF_OTHER);
4778 4779

    if (nOldHotItem < 0)
4780
        return -1;
4781 4782 4783

    return (LRESULT)nOldHotItem;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4784 4785 4786


static LRESULT
4787
TOOLBAR_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4788
{
4789
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
4790
    HIMAGELIST himlTemp;
4791
    HIMAGELIST himl = (HIMAGELIST)lParam;
4792
    INT oldButtonWidth = infoPtr->nButtonWidth;
4793
    INT i, id = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
4794

4795 4796
    if (infoPtr->iVersion >= 5)
        id = wParam;
Guy Albertelli's avatar
Guy Albertelli committed
4797

4798 4799
    himlTemp = TOOLBAR_InsertImageList(&infoPtr->himlDef, 
        &infoPtr->cimlDef, himl, id);
Alexandre Julliard's avatar
Alexandre Julliard committed
4800

4801 4802 4803
    infoPtr->nNumBitmaps = 0;
    for (i = 0; i < infoPtr->cimlDef; i++)
        infoPtr->nNumBitmaps += ImageList_GetImageCount(infoPtr->himlDef[i]->himl);
4804

4805 4806 4807
    if (!ImageList_GetIconSize(himl, &infoPtr->nBitmapWidth,
            &infoPtr->nBitmapHeight))
    {
4808 4809
        infoPtr->nBitmapWidth = 1;
        infoPtr->nBitmapHeight = 1;
4810
    }
4811
    TOOLBAR_CalcToolbar(hwnd);
4812 4813
    if (infoPtr->nButtonWidth < oldButtonWidth)
        TOOLBAR_SetButtonSize(hwnd, 0, MAKELONG(oldButtonWidth, infoPtr->nButtonHeight));
4814 4815 4816

    TRACE("hwnd %p, new himl=%p, id = %d, count=%d, bitmap w=%d, h=%d\n",
	  hwnd, infoPtr->himlDef, id, infoPtr->nNumBitmaps,
4817 4818
	  infoPtr->nBitmapWidth, infoPtr->nBitmapHeight);

4819
    InvalidateRect(hwnd, NULL, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4820

4821
    return (LRESULT)himlTemp;
Alexandre Julliard's avatar
Alexandre Julliard committed
4822
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4823

Alexandre Julliard's avatar
Alexandre Julliard committed
4824 4825

static LRESULT
4826
TOOLBAR_SetIndent (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4827
{
4828
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
4829

4830 4831
    infoPtr->nIndent = (INT)wParam;

4832
    TRACE("\n");
4833

4834 4835 4836 4837
    /* process only on indent changing */
    if(infoPtr->nIndent != (INT)wParam)
    {
        infoPtr->nIndent = (INT)wParam;
4838
        TOOLBAR_CalcToolbar (hwnd);
4839 4840
        InvalidateRect(hwnd, NULL, FALSE);
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4841 4842 4843 4844 4845

    return TRUE;
}


4846 4847 4848 4849 4850 4851
static LRESULT
TOOLBAR_SetInsertMark (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    TBINSERTMARK *lptbim = (TBINSERTMARK*)lParam;

4852
    TRACE("hwnd = %p, lptbim = { %d, 0x%08x}\n", hwnd, lptbim->iButton, lptbim->dwFlags);
4853 4854 4855

    if ((lptbim->dwFlags & ~TBIMHT_AFTER) != 0)
    {
4856
        FIXME("Unrecognized flag(s): 0x%08x\n", (lptbim->dwFlags & ~TBIMHT_AFTER));
4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872
        return 0;
    }

    if ((lptbim->iButton == -1) || 
        ((lptbim->iButton < infoPtr->nNumButtons) &&
         (lptbim->iButton >= 0)))
    {
        infoPtr->tbim = *lptbim;
        /* FIXME: don't need to update entire toolbar */
        InvalidateRect(hwnd, NULL, TRUE);
    }
    else
        ERR("Invalid button index %d\n", lptbim->iButton);

    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4873 4874 4875


static LRESULT
4876
TOOLBAR_SetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4877
{
4878
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
4879 4880 4881

    infoPtr->clrInsertMark = (COLORREF)lParam;

4882 4883
    /* FIXME: don't need to update entire toolbar */
    InvalidateRect(hwnd, NULL, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4884 4885 4886 4887 4888

    return 0;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4889
static LRESULT
4890
TOOLBAR_SetMaxTextRows (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4891
{
4892
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
4893

4894
    infoPtr->nMaxTextRows = (INT)wParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
4895

Huw Davies's avatar
Huw Davies committed
4896
    TOOLBAR_CalcToolbar(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
4897 4898 4899 4900
    return TRUE;
}


4901 4902 4903 4904 4905 4906 4907 4908 4909
/* MSDN gives slightly wrong info on padding.
 * 1. It is not only used on buttons with the BTNS_AUTOSIZE style
 * 2. It is not used to create a blank area between the edge of the button
 *    and the text or image if TBSTYLE_LIST is set. It is used to control
 *    the gap between the image and text. 
 * 3. It is not applied to both sides. If TBSTYLE_LIST is set it is used 
 *    to control the bottom and right borders [with the border being
 *    szPadding.cx - (GetSystemMetrics(SM_CXEDGE)+1)], otherwise the padding
 *    is shared evenly on both sides of the button.
4910
 * See blueprints in comments above TOOLBAR_MeasureButton for more info.
4911
 */
4912 4913 4914 4915 4916 4917 4918
static LRESULT
TOOLBAR_SetPadding (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    DWORD  oldPad;

    oldPad = MAKELONG(infoPtr->szPadding.cx, infoPtr->szPadding.cy);
4919 4920
    infoPtr->szPadding.cx = min(LOWORD((DWORD)lParam), GetSystemMetrics(SM_CXEDGE));
    infoPtr->szPadding.cy = min(HIWORD((DWORD)lParam), GetSystemMetrics(SM_CYEDGE));
4921
    TRACE("cx=%d, cy=%d\n",
4922 4923 4924
	  infoPtr->szPadding.cx, infoPtr->szPadding.cy);
    return (LRESULT) oldPad;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4925 4926 4927


static LRESULT
4928
TOOLBAR_SetParent (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4929
{
4930
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
4931
    HWND hwndOldNotify;
Alexandre Julliard's avatar
Alexandre Julliard committed
4932

4933 4934
    TRACE("\n");

Alexandre Julliard's avatar
Alexandre Julliard committed
4935
    hwndOldNotify = infoPtr->hwndNotify;
4936
    infoPtr->hwndNotify = (HWND)wParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
4937

4938
    return (LRESULT)hwndOldNotify;
Alexandre Julliard's avatar
Alexandre Julliard committed
4939 4940 4941 4942
}


static LRESULT
4943
TOOLBAR_SetRows (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
4944
{
4945
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
4946
    LPRECT lprc = (LPRECT)lParam;
4947 4948
    int rows = LOWORD(wParam);
    BOOL bLarger = HIWORD(wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
4949

4950
    TRACE("\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
4951

4952
    TRACE("Setting rows to %d (%d)\n", rows, bLarger);
Alexandre Julliard's avatar
Alexandre Julliard committed
4953

4954
    if(infoPtr->nRows != rows)
4955
    {
4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033
        TBUTTON_INFO *btnPtr = infoPtr->buttons;
        int curColumn = 0; /* Current column                      */
        int curRow    = 0; /* Current row                         */
        int hidden    = 0; /* Number of hidden buttons */
        int seps      = 0; /* Number of separators     */
        int idealWrap = 0; /* Ideal wrap point         */
        int i;
        BOOL wrap;

        /*
           Calculate new size and wrap points - Under windows, setrows will
           change the dimensions if needed to show the number of requested
           rows (if CCS_NORESIZE is set), or will take up the whole window
           (if no CCS_NORESIZE).

           Basic algorithum - If N buttons, and y rows requested, each row
           contains N/y buttons.

           FIXME: Handling of separators not obvious from testing results
           FIXME: Take width of window into account?
         */

        /* Loop through the buttons one by one counting key items  */
        for (i = 0; i < infoPtr->nNumButtons; i++ )
        {
            btnPtr[i].fsState &= ~TBSTATE_WRAP;
            if (btnPtr[i].fsState & TBSTATE_HIDDEN)
                hidden++;
            else if (btnPtr[i].fsStyle & BTNS_SEP)
                seps++;
        }

        /* FIXME: Separators make this quite complex */
        if (seps) FIXME("Separators unhandled\n");

        /* Round up so more per line, ie less rows */
        idealWrap = (infoPtr->nNumButtons - hidden + (rows-1)) / rows;

        /* Calculate ideal wrap point if we are allowed to grow, but cannot
           achieve the requested number of rows. */
        if (bLarger && idealWrap > 1)
        {
            int resRows = (infoPtr->nNumButtons + (idealWrap-1)) / idealWrap;
            int moreRows = (infoPtr->nNumButtons + (idealWrap-2)) / (idealWrap-1);

            if (resRows < rows && moreRows > rows)
            {
                idealWrap--;
                TRACE("Changing idealWrap due to bLarger (now %d)\n", idealWrap);
            }
        }

        curColumn = curRow = 0;
        wrap = FALSE;
        TRACE("Trying to wrap at %d (%d,%d,%d)\n", idealWrap,
              infoPtr->nNumButtons, hidden, rows);

        for (i = 0; i < infoPtr->nNumButtons; i++ )
        {
            if (btnPtr[i].fsState & TBSTATE_HIDDEN)
                continue;

            /* Step on, wrap if necessary or flag next to wrap */
            if (!wrap) {
                curColumn++;
            } else {
                wrap = FALSE;
                curColumn = 1;
                curRow++;
            }

            if (curColumn > (idealWrap-1)) {
                wrap = TRUE;
                btnPtr[i].fsState |= TBSTATE_WRAP;
            }
        }

        TRACE("Result - %d rows\n", curRow + 1);
5034

5035 5036 5037
        /* recalculate toolbar */
        TOOLBAR_CalcToolbar (hwnd);

5038 5039 5040 5041 5042 5043 5044 5045 5046 5047
        /* Resize if necessary (Only if NORESIZE is set - odd, but basically
           if NORESIZE is NOT set, then the toolbar will always be resized to
           take up the whole window. With it set, sizing needs to be manual. */
        if (infoPtr->dwStyle & CCS_NORESIZE) {
            SetWindowPos(hwnd, NULL, 0, 0,
                         infoPtr->rcBound.right - infoPtr->rcBound.left,
                         infoPtr->rcBound.bottom - infoPtr->rcBound.top,
                         SWP_NOMOVE);
        }

5048
        /* repaint toolbar */
Robert Shearman's avatar
Robert Shearman committed
5049
        InvalidateRect(hwnd, NULL, TRUE);
5050
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
5051 5052 5053 5054 5055 5056 5057 5058 5059

    /* return bounding rectangle */
    if (lprc) {
	lprc->left   = infoPtr->rcBound.left;
	lprc->right  = infoPtr->rcBound.right;
	lprc->top    = infoPtr->rcBound.top;
	lprc->bottom = infoPtr->rcBound.bottom;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
5060 5061 5062
    return 0;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
5063 5064

static LRESULT
5065
TOOLBAR_SetState (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
5066
{
5067
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
5068
    TBUTTON_INFO *btnPtr;
5069
    INT nIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
5070

5071
    nIndex = TOOLBAR_GetButtonIndex (infoPtr, (INT)wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
5072 5073 5074 5075 5076
    if (nIndex == -1)
	return FALSE;

    btnPtr = &infoPtr->buttons[nIndex];

5077 5078 5079 5080
    /* if hidden state has changed the invalidate entire window and recalc */
    if ((btnPtr->fsState & TBSTATE_HIDDEN) != (LOWORD(lParam) & TBSTATE_HIDDEN)) {
	btnPtr->fsState = LOWORD(lParam);
	TOOLBAR_CalcToolbar (hwnd);
Robert Shearman's avatar
Robert Shearman committed
5081
	InvalidateRect(hwnd, 0, TRUE);
5082 5083 5084
	return TRUE;
    }

5085 5086 5087 5088
    /* process state changing if current state doesn't match new state */
    if(btnPtr->fsState != LOWORD(lParam))
    {
        btnPtr->fsState = LOWORD(lParam);
Robert Shearman's avatar
Robert Shearman committed
5089
        InvalidateRect(hwnd, &btnPtr->rect, TRUE);
5090
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
5091 5092 5093 5094 5095 5096

    return TRUE;
}


static LRESULT
5097
TOOLBAR_SetStyle (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
5098
{
5099
    SetWindowLongW(hwnd, GWL_STYLE, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
5100

Alexandre Julliard's avatar
Alexandre Julliard committed
5101 5102 5103 5104
    return TRUE;
}


5105
static inline LRESULT
5106
TOOLBAR_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
5107
{
5108
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
5109

5110 5111
    TRACE("hwnd=%p, hwndTooltip=%p, lParam=0x%lx\n", hwnd, (HWND)wParam, lParam);

5112
    infoPtr->hwndToolTip = (HWND)wParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
5113 5114 5115 5116
    return 0;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
5117
static LRESULT
5118
TOOLBAR_SetUnicodeFormat (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
5119
{
5120
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
5121
    BOOL bTemp;
Alexandre Julliard's avatar
Alexandre Julliard committed
5122

Robert Shearman's avatar
Robert Shearman committed
5123
    TRACE("%s hwnd=%p\n",
5124
	   ((BOOL)wParam) ? "TRUE" : "FALSE", hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
5125

Alexandre Julliard's avatar
Alexandre Julliard committed
5126
    bTemp = infoPtr->bUnicode;
5127
    infoPtr->bUnicode = (BOOL)wParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
5128 5129

    return bTemp;
Alexandre Julliard's avatar
Alexandre Julliard committed
5130
}
Alexandre Julliard's avatar
Alexandre Julliard committed
5131 5132


5133 5134 5135 5136 5137
static LRESULT
TOOLBAR_GetColorScheme (HWND hwnd, LPCOLORSCHEME lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);

5138 5139
    lParam->clrBtnHighlight = (infoPtr->clrBtnHighlight == CLR_DEFAULT) ?
	                       comctl32_color.clrBtnHighlight :
5140
                               infoPtr->clrBtnHighlight;
5141
    lParam->clrBtnShadow = (infoPtr->clrBtnShadow == CLR_DEFAULT) ?
5142
	                   comctl32_color.clrBtnShadow : infoPtr->clrBtnShadow;
5143 5144 5145 5146 5147
    return 1;
}


static LRESULT
5148
TOOLBAR_SetColorScheme (HWND hwnd, const COLORSCHEME *lParam)
5149 5150 5151
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);

5152
    TRACE("new colors Hl=%x Shd=%x, old colors Hl=%x Shd=%x\n",
5153 5154 5155 5156 5157
	  lParam->clrBtnHighlight, lParam->clrBtnShadow,
	  infoPtr->clrBtnHighlight, infoPtr->clrBtnShadow);

    infoPtr->clrBtnHighlight = lParam->clrBtnHighlight;
    infoPtr->clrBtnShadow = lParam->clrBtnShadow;
Robert Shearman's avatar
Robert Shearman committed
5158
    InvalidateRect(hwnd, NULL, TRUE);
5159 5160 5161 5162
    return 0;
}


5163 5164 5165 5166 5167 5168 5169 5170
static LRESULT
TOOLBAR_SetVersion (HWND hwnd, INT iVersion)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    INT iOldVersion = infoPtr->iVersion;

    infoPtr->iVersion = iVersion;

5171 5172 5173
    if (infoPtr->iVersion >= 5)
        TOOLBAR_SetUnicodeFormat(hwnd, (WPARAM)TRUE, (LPARAM)0);

5174 5175 5176
    return iOldVersion;
}

Robert Shearman's avatar
Robert Shearman committed
5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195

static LRESULT
TOOLBAR_GetStringA (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr(hwnd);
    WORD iString = HIWORD(wParam);
    WORD buffersize = LOWORD(wParam);
    LPSTR str = (LPSTR)lParam;
    LRESULT ret = -1;

    TRACE("hwnd=%p, iString=%d, buffersize=%d, string=%p\n", hwnd, iString, buffersize, str);

    if (iString < infoPtr->nNumStrings)
    {
        ret = WideCharToMultiByte(CP_ACP, 0, infoPtr->strings[iString], -1, str, buffersize, NULL, NULL);

        TRACE("returning %s\n", debugstr_a(str));
    }
    else
5196
        WARN("String index %d out of range (largest is %d)\n", iString, infoPtr->nNumStrings - 1);
Robert Shearman's avatar
Robert Shearman committed
5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227

    return ret;
}


static LRESULT
TOOLBAR_GetStringW (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr(hwnd);
    WORD iString = HIWORD(wParam);
    WORD len = LOWORD(wParam)/sizeof(WCHAR) - 1;
    LPWSTR str = (LPWSTR)lParam;
    LRESULT ret = -1;

    TRACE("hwnd=%p, iString=%d, buffersize=%d, string=%p\n", hwnd, iString, LOWORD(wParam), str);

    if (iString < infoPtr->nNumStrings)
    {
        len = min(len, strlenW(infoPtr->strings[iString]));
        ret = (len+1)*sizeof(WCHAR);
        memcpy(str, infoPtr->strings[iString], ret);
        str[len] = '\0';

        TRACE("returning %s\n", debugstr_w(str));
    }
    else
        ERR("String index %d out of range (largest is %d)\n", iString, infoPtr->nNumStrings - 1);

    return ret;
}

5228 5229
/* UNDOCUMENTED MESSAGE: This appears to set some kind of size. Perhaps it
 * is the maximum size of the toolbar? */
5230 5231
static LRESULT TOOLBAR_Unkwn45D(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
5232
    SIZE * pSize = (SIZE*)lParam;
5233
    FIXME("hwnd=%p, wParam=0x%08lx, size.cx=%d, size.cy=%d stub!\n", hwnd, wParam, pSize->cx, pSize->cy);
5234 5235
    return 0;
}
5236

5237 5238 5239 5240 5241

/* UNDOCUMENTED MESSAGE: This is an extended version of the
 * TB_SETHOTITEM message. It allows the caller to specify a reason why the
 * hot item changed (rather than just the HICF_OTHER that TB_SETHOTITEM
 * sends). */
5242 5243 5244 5245 5246
static LRESULT
TOOLBAR_Unkwn45E (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr(hwnd);
    INT nOldHotItem = infoPtr->nHotItem;
5247

5248
    TRACE("old item=%d, new item=%d, flags=%08x\n",
5249
	  nOldHotItem, infoPtr->nHotItem, (DWORD)lParam);
5250 5251

    if ((INT) wParam < 0 || (INT)wParam > infoPtr->nNumButtons)
5252
        wParam = -1;
5253

5254 5255 5256
    /* NOTE: an application can still remove the hot item even if anchor
     * highlighting is enabled */

5257
    TOOLBAR_SetHotItemEx(infoPtr, wParam, lParam);
5258

5259
    GetFocus();
5260

5261
    return (nOldHotItem < 0) ? -1 : (LRESULT)nOldHotItem;
5262 5263
}

5264 5265 5266
/* UNDOCUMENTED MESSAGE: This sets the toolbar global iListGap parameter
 * which controls the amount of spacing between the image and the text
 * of buttons for TBSTYLE_LIST toolbars. */
5267 5268
static LRESULT TOOLBAR_Unkwn460(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
5269 5270
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr(hwnd);

5271
    TRACE("hwnd=%p iListGap=%ld\n", hwnd, wParam);
5272 5273 5274 5275 5276
    
    if (lParam != 0)
        FIXME("lParam = 0x%08lx. Please report\n", lParam);
    
    infoPtr->iListGap = (INT)wParam;
5277 5278

    InvalidateRect(hwnd, NULL, TRUE);
5279

5280 5281
    return 0;
}
5282

Robert Shearman's avatar
Robert Shearman committed
5283 5284 5285 5286 5287 5288
/* UNDOCUMENTED MESSAGE: This returns the number of maximum number
 * of image lists associated with the various states. */
static LRESULT TOOLBAR_Unkwn462(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr(hwnd);

5289
    TRACE("hwnd=%p wParam %08lx lParam %08lx\n", hwnd, wParam, lParam);
Robert Shearman's avatar
Robert Shearman committed
5290 5291 5292 5293

    return max(infoPtr->cimlDef, max(infoPtr->cimlHot, infoPtr->cimlDis));
}

5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309
static LRESULT
TOOLBAR_Unkwn463 (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    LPSIZE lpsize = (LPSIZE)lParam;

    if (lpsize == NULL)
	return FALSE;

    /*
     * Testing shows the following:
     *   wParam    = 0 adjust cx value
     *             = 1 set cy value to max size.
     *   lParam    pointer to SIZE structure
     *
     */
5310
    TRACE("[0463] wParam %ld, lParam 0x%08lx -> 0x%08x 0x%08x\n",
5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324
	  wParam, lParam, lpsize->cx, lpsize->cy);

    switch(wParam) {
    case 0:
	if (lpsize->cx == -1) {
	    /* **** this is wrong, native measures each button and sets it */
	    lpsize->cx = infoPtr->rcBound.right - infoPtr->rcBound.left;
	}
	else if(HIWORD(lpsize->cx)) {
	    RECT rc;
	    HWND hwndParent = GetParent(hwnd);

	    GetWindowRect(hwnd, &rc);
	    MapWindowPoints(0, hwndParent, (LPPOINT)&rc, 2);
5325
            TRACE("mapped to (%d,%d)-(%d,%d)\n",
5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337
		rc.left, rc.top, rc.right, rc.bottom);
	    lpsize->cx = max(rc.right-rc.left,
			     infoPtr->rcBound.right - infoPtr->rcBound.left);
	}
	else {
	    lpsize->cx = infoPtr->rcBound.right - infoPtr->rcBound.left;
	}
	break;
    case 1:
	lpsize->cy = infoPtr->rcBound.bottom - infoPtr->rcBound.top;
	break;
    default:
5338
	ERR("Unknown wParam %ld for Toolbar message [0463]. Please report\n",
5339 5340 5341
	    wParam);
	return 0;
    }
5342
    TRACE("[0463] set to -> 0x%08x 0x%08x\n",
5343 5344 5345 5346
	  lpsize->cx, lpsize->cy);
    return 1;
}

5347 5348
static LRESULT TOOLBAR_Unkwn464(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
5349
    FIXME("hwnd=%p wParam %08lx lParam %08lx\n", hwnd, wParam, lParam);
5350 5351 5352 5353 5354

    InvalidateRect(hwnd, NULL, TRUE);
    return 1;
}

5355

Alexandre Julliard's avatar
Alexandre Julliard committed
5356
static LRESULT
5357
TOOLBAR_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
5358
{
5359
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
5360
    DWORD dwStyle = GetWindowLongW (hwnd, GWL_STYLE);
5361
    LOGFONTW logFont;
Alexandre Julliard's avatar
Alexandre Julliard committed
5362

Robert Shearman's avatar
Robert Shearman committed
5363 5364
    TRACE("hwnd = %p\n", hwnd);

Alexandre Julliard's avatar
Alexandre Julliard committed
5365
    /* initialize info structure */
5366
    infoPtr->nButtonWidth = 23;
Alexandre Julliard's avatar
Alexandre Julliard committed
5367
    infoPtr->nButtonHeight = 22;
5368
    infoPtr->nBitmapHeight = 16;
Alexandre Julliard's avatar
Alexandre Julliard committed
5369 5370
    infoPtr->nBitmapWidth = 16;

Alexandre Julliard's avatar
Alexandre Julliard committed
5371 5372 5373
    infoPtr->nMaxTextRows = 1;
    infoPtr->cxMin = -1;
    infoPtr->cxMax = -1;
5374 5375
    infoPtr->nNumBitmaps = 0;
    infoPtr->nNumStrings = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
5376

Alexandre Julliard's avatar
Alexandre Julliard committed
5377
    infoPtr->bCaptured = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
5378
    infoPtr->nButtonDown = -1;
Robert Shearman's avatar
Robert Shearman committed
5379
    infoPtr->nButtonDrag = -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
5380
    infoPtr->nOldHit = -1;
5381
    infoPtr->nHotItem = -1;
5382
    infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
5383
    infoPtr->dwDTFlags = (dwStyle & TBSTYLE_LIST) ? DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS: DT_CENTER | DT_END_ELLIPSIS;
5384
    infoPtr->bAnchor = FALSE; /* no anchor highlighting */
5385
    infoPtr->bDragOutSent = FALSE;
5386
    infoPtr->iVersion = 0;
5387
    infoPtr->hwndSelf = hwnd;
5388
    infoPtr->bDoRedraw = TRUE;
5389 5390
    infoPtr->clrBtnHighlight = CLR_DEFAULT;
    infoPtr->clrBtnShadow = CLR_DEFAULT;
5391 5392
    infoPtr->szPadding.cx = DEFPAD_CX;
    infoPtr->szPadding.cy = DEFPAD_CY;
5393
    infoPtr->iListGap = DEFLISTGAP;
5394
    infoPtr->iTopMargin = default_top_margin(infoPtr);
5395
    infoPtr->dwStyle = dwStyle;
5396
    infoPtr->tbim.iButton = -1;
5397
    GetClientRect(hwnd, &infoPtr->client_rect);
5398 5399
    infoPtr->bUnicode = infoPtr->hwndNotify && 
        (NFR_UNICODE == SendMessageW(hwnd, WM_NOTIFYFORMAT, (WPARAM)hwnd, (LPARAM)NF_REQUERY));
5400
    infoPtr->hwndToolTip = NULL; /* if needed the tooltip control will be created after a WM_MOUSEMOVE */
Alexandre Julliard's avatar
Alexandre Julliard committed
5401

5402 5403
    SystemParametersInfoW (SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
    infoPtr->hFont = infoPtr->hDefaultFont = CreateFontIndirectW (&logFont);
5404 5405
    
    OpenThemeData (hwnd, themeClass);
5406

5407 5408
    TOOLBAR_CheckStyle (hwnd, dwStyle);

Alexandre Julliard's avatar
Alexandre Julliard committed
5409 5410 5411 5412 5413
    return 0;
}


static LRESULT
5414
TOOLBAR_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
5415
{
5416
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
5417

Alexandre Julliard's avatar
Alexandre Julliard committed
5418
    /* delete tooltip control */
Alexandre Julliard's avatar
Alexandre Julliard committed
5419
    if (infoPtr->hwndToolTip)
5420
	DestroyWindow (infoPtr->hwndToolTip);
Alexandre Julliard's avatar
Alexandre Julliard committed
5421

5422
    /* delete temporary buffer for tooltip text */
5423
    Free (infoPtr->pszTooltipText);
5424
    Free (infoPtr->bitmaps);            /* bitmaps list */
5425

Alexandre Julliard's avatar
Alexandre Julliard committed
5426
    /* delete button data */
5427
    Free (infoPtr->buttons);
Alexandre Julliard's avatar
Alexandre Julliard committed
5428

Alexandre Julliard's avatar
Alexandre Julliard committed
5429 5430
    /* delete strings */
    if (infoPtr->strings) {
5431
	INT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
5432
	for (i = 0; i < infoPtr->nNumStrings; i++)
5433
	    Free (infoPtr->strings[i]);
Alexandre Julliard's avatar
Alexandre Julliard committed
5434

5435
	Free (infoPtr->strings);
Alexandre Julliard's avatar
Alexandre Julliard committed
5436 5437
    }

5438 5439 5440
    /* destroy internal image list */
    if (infoPtr->himlInt)
	ImageList_Destroy (infoPtr->himlInt);
Alexandre Julliard's avatar
Alexandre Julliard committed
5441

5442 5443 5444 5445
	TOOLBAR_DeleteImageList(&infoPtr->himlDef, &infoPtr->cimlDef);
	TOOLBAR_DeleteImageList(&infoPtr->himlDis, &infoPtr->cimlDis);
	TOOLBAR_DeleteImageList(&infoPtr->himlHot, &infoPtr->cimlHot);

Alexandre Julliard's avatar
Alexandre Julliard committed
5446
    /* delete default font */
5447
    DeleteObject (infoPtr->hDefaultFont);
5448 5449
        
    CloseThemeData (GetWindowTheme (hwnd));
Alexandre Julliard's avatar
Alexandre Julliard committed
5450

Alexandre Julliard's avatar
Alexandre Julliard committed
5451
    /* free toolbar info data */
5452
    Free (infoPtr);
5453
    SetWindowLongPtrW (hwnd, 0, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
5454 5455 5456 5457 5458

    return 0;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
5459
static LRESULT
5460
TOOLBAR_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
5461
{
5462
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
5463
    NMTBCUSTOMDRAW tbcd;
5464 5465
    INT ret = FALSE;
    DWORD ntfret;
5466
    HTHEME theme = GetWindowTheme (hwnd);
5467
    DWORD dwEraseCustDraw = 0;
5468

5469 5470 5471 5472
    /* the app has told us not to redraw the toolbar */
    if (!infoPtr->bDoRedraw)
        return FALSE;

5473
    if (infoPtr->dwStyle & TBSTYLE_CUSTOMERASE) {
5474 5475 5476
	ZeroMemory (&tbcd, sizeof(NMTBCUSTOMDRAW));
	tbcd.nmcd.dwDrawStage = CDDS_PREERASE;
	tbcd.nmcd.hdc = (HDC)wParam;
5477
	ntfret = TOOLBAR_SendNotify (&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
5478
	dwEraseCustDraw = ntfret & 0xffff;
5479

5480
	/* FIXME: in general the return flags *can* be or'ed together */
5481
	switch (dwEraseCustDraw)
5482 5483 5484 5485 5486 5487
	    {
	    case CDRF_DODEFAULT:
		break;
	    case CDRF_SKIPDEFAULT:
		return TRUE;
	    default:
5488
		FIXME("[%p] response %d not handled to NM_CUSTOMDRAW (CDDS_PREERASE)\n",
5489
		      hwnd, ntfret);
5490 5491
	    }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
5492

5493
    /* If the toolbar is "transparent" then pass the WM_ERASEBKGND up
5494 5495
     * to my parent for processing.
     */
5496
    if (theme || (infoPtr->dwStyle & TBSTYLE_TRANSPARENT)) {
5497 5498 5499 5500 5501 5502 5503 5504 5505
	POINT pt, ptorig;
	HDC hdc = (HDC)wParam;
	HWND parent;

	pt.x = 0;
	pt.y = 0;
	parent = GetParent(hwnd);
	MapWindowPoints(hwnd, parent, &pt, 1);
	OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
5506
	ret = SendMessageW (parent, WM_ERASEBKGND, wParam, lParam);
5507 5508
	SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);
    }
Guy Albertelli's avatar
Guy Albertelli committed
5509
    if (!ret)
5510
	ret = DefWindowProcW (hwnd, WM_ERASEBKGND, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
5511

5512
    if (dwEraseCustDraw & CDRF_NOTIFYPOSTERASE) {
5513 5514 5515
	ZeroMemory (&tbcd, sizeof(NMTBCUSTOMDRAW));
	tbcd.nmcd.dwDrawStage = CDDS_POSTERASE;
	tbcd.nmcd.hdc = (HDC)wParam;
5516
	ntfret = TOOLBAR_SendNotify (&tbcd.nmcd.hdr, infoPtr, NM_CUSTOMDRAW);
5517 5518
	dwEraseCustDraw = ntfret & 0xffff;
	switch (dwEraseCustDraw)
5519 5520 5521 5522 5523 5524
	    {
	    case CDRF_DODEFAULT:
		break;
	    case CDRF_SKIPDEFAULT:
		return TRUE;
	    default:
5525
		FIXME("[%p] response %d not handled to NM_CUSTOMDRAW (CDDS_POSTERASE)\n",
5526
		      hwnd, ntfret);
5527 5528
	    }
    }
5529
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
5530 5531 5532
}


Eric Pouech's avatar
Eric Pouech committed
5533 5534 5535 5536 5537
static LRESULT
TOOLBAR_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);

5538
    return (LRESULT)infoPtr->hFont;
Eric Pouech's avatar
Eric Pouech committed
5539 5540 5541
}


5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612
static void
TOOLBAR_SetRelativeHotItem(TOOLBAR_INFO *infoPtr, INT iDirection, DWORD dwReason)
{
    INT i;
    INT nNewHotItem = infoPtr->nHotItem;

    for (i = 0; i < infoPtr->nNumButtons; i++)
    {
        /* did we wrap? */
        if ((nNewHotItem + iDirection < 0) ||
            (nNewHotItem + iDirection >= infoPtr->nNumButtons))
        {
            NMTBWRAPHOTITEM nmtbwhi;
            nmtbwhi.idNew = infoPtr->buttons[nNewHotItem].idCommand;
            nmtbwhi.iDirection = iDirection;
            nmtbwhi.dwReason = dwReason;
    
            if (TOOLBAR_SendNotify(&nmtbwhi.hdr, infoPtr, TBN_WRAPHOTITEM))
                return;
        }

        nNewHotItem += iDirection;
        nNewHotItem = (nNewHotItem + infoPtr->nNumButtons) % infoPtr->nNumButtons;

        if ((infoPtr->buttons[nNewHotItem].fsState & TBSTATE_ENABLED) &&
            !(infoPtr->buttons[nNewHotItem].fsStyle & BTNS_SEP))
        {
            TOOLBAR_SetHotItemEx(infoPtr, nNewHotItem, dwReason);
            break;
        }
    }
}

static LRESULT
TOOLBAR_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    NMKEY nmkey;

    nmkey.nVKey = (UINT)wParam;
    nmkey.uFlags = HIWORD(lParam);

    if (TOOLBAR_SendNotify(&nmkey.hdr, infoPtr, NM_KEYDOWN))
        return DefWindowProcW(hwnd, WM_KEYDOWN, wParam, lParam);

    switch ((UINT)wParam)
    {
    case VK_LEFT:
    case VK_UP:
        TOOLBAR_SetRelativeHotItem(infoPtr, -1, HICF_ARROWKEYS);
        break;
    case VK_RIGHT:
    case VK_DOWN:
        TOOLBAR_SetRelativeHotItem(infoPtr, 1, HICF_ARROWKEYS);
        break;
    case VK_SPACE:
    case VK_RETURN:
        if ((infoPtr->nHotItem >= 0) &&
            (infoPtr->buttons[infoPtr->nHotItem].fsState & TBSTATE_ENABLED))
        {
            SendMessageW (infoPtr->hwndNotify, WM_COMMAND,
                MAKEWPARAM(infoPtr->buttons[infoPtr->nHotItem].idCommand, BN_CLICKED),
                (LPARAM)hwnd);
        }
        break;
    }

    return 0;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
5613
static LRESULT
5614
TOOLBAR_LButtonDblClk (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
5615
{
5616 5617
    POINT pt;
    INT   nHit;
Alexandre Julliard's avatar
Alexandre Julliard committed
5618

5619 5620
    pt.x = (short)LOWORD(lParam);
    pt.y = (short)HIWORD(lParam);
5621
    nHit = TOOLBAR_InternalHitTest (hwnd, &pt);
Alexandre Julliard's avatar
Alexandre Julliard committed
5622

5623 5624
    if (nHit >= 0)
        TOOLBAR_LButtonDown (hwnd, wParam, lParam);
5625
    else if (GetWindowLongW (hwnd, GWL_STYLE) & CCS_ADJUSTABLE)
5626
	TOOLBAR_Customize (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
5627 5628 5629 5630 5631 5632

    return 0;
}


static LRESULT
5633
TOOLBAR_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
5634
{
5635
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
5636
    TBUTTON_INFO *btnPtr;
5637 5638
    POINT pt;
    INT   nHit;
5639
    NMTOOLBARA nmtb;
5640
    NMMOUSE nmmouse;
Robert Shearman's avatar
Robert Shearman committed
5641 5642
    BOOL bDragKeyPressed;

5643 5644
    TRACE("\n");

5645
    if (infoPtr->dwStyle & TBSTYLE_ALTDRAG)
Robert Shearman's avatar
Robert Shearman committed
5646 5647 5648
        bDragKeyPressed = (GetKeyState(VK_MENU) < 0);
    else
        bDragKeyPressed = (wParam & MK_SHIFT);
Alexandre Julliard's avatar
Alexandre Julliard committed
5649

Alexandre Julliard's avatar
Alexandre Julliard committed
5650
    if (infoPtr->hwndToolTip)
5651
	TOOLBAR_RelayEvent (infoPtr->hwndToolTip, hwnd,
Alexandre Julliard's avatar
Alexandre Julliard committed
5652 5653
			    WM_LBUTTONDOWN, wParam, lParam);

5654 5655
    pt.x = (short)LOWORD(lParam);
    pt.y = (short)HIWORD(lParam);
5656
    nHit = TOOLBAR_InternalHitTest (hwnd, &pt);
Alexandre Julliard's avatar
Alexandre Julliard committed
5657

5658 5659
    btnPtr = &infoPtr->buttons[nHit];

5660
    if ((nHit >= 0) && bDragKeyPressed && (infoPtr->dwStyle & CCS_ADJUSTABLE))
Robert Shearman's avatar
Robert Shearman committed
5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672
    {
        infoPtr->nButtonDrag = nHit;
        SetCapture (hwnd);
        
        /* If drag cursor has not been loaded, load it.
         * Note: it doesn't need to be freed */
        if (!hCursorDrag)
            hCursorDrag = LoadCursorW(COMCTL32_hModule, (LPCWSTR)IDC_MOVEBUTTON);
        SetCursor(hCursorDrag);
    }
    else if (nHit >= 0)
    {
5673 5674 5675 5676 5677
	RECT arrowRect;
	infoPtr->nOldHit = nHit;

	CopyRect(&arrowRect, &btnPtr->rect);
	arrowRect.left = max(btnPtr->rect.left, btnPtr->rect.right - DDARROW_WIDTH);
5678

5679
	/* for EX_DRAWDDARROWS style,  click must be in the drop-down arrow rect */
5680 5681 5682 5683 5684
	if ((btnPtr->fsState & TBSTATE_ENABLED) && 
	     ((btnPtr->fsStyle & BTNS_WHOLEDROPDOWN) ||
	      ((btnPtr->fsStyle & BTNS_DROPDOWN) &&
	       ((TOOLBAR_HasDropDownArrows(infoPtr->dwExStyle) && PtInRect(&arrowRect, pt)) ||
	       (!TOOLBAR_HasDropDownArrows(infoPtr->dwExStyle))))))
5685
	{
5686
	    LRESULT res;
5687 5688

	    /* draw in pressed state */
5689 5690 5691 5692
	    if (btnPtr->fsStyle & BTNS_WHOLEDROPDOWN)
	        btnPtr->fsState |= TBSTATE_PRESSED;
	    else
	        btnPtr->bDropDownPressed = TRUE;
5693 5694
	    RedrawWindow(hwnd,&btnPtr->rect,0,
			RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW);
5695

5696
	    memset(&nmtb, 0, sizeof(nmtb));
5697
	    nmtb.iItem = btnPtr->idCommand;
5698
	    nmtb.rcButton = btnPtr->rect;
5699 5700
	    res = TOOLBAR_SendNotify ((NMHDR *) &nmtb, infoPtr,
				  TBN_DROPDOWN);
5701 5702 5703 5704 5705 5706 5707
	    TRACE("TBN_DROPDOWN responded with %ld\n", res);

            if (res != TBDDRET_TREATPRESSED)
            {
                MSG msg;

                /* redraw button in unpressed state */
5708 5709 5710 5711
	        if (btnPtr->fsStyle & BTNS_WHOLEDROPDOWN)
       	            btnPtr->fsState &= ~TBSTATE_PRESSED;
       	        else
       	            btnPtr->bDropDownPressed = FALSE;
5712 5713 5714 5715 5716 5717 5718
       	        InvalidateRect(hwnd, &btnPtr->rect, TRUE);

                /* find and set hot item
                 * NOTE: native doesn't do this, but that is a bug */
                GetCursorPos(&pt);
                ScreenToClient(hwnd, &pt);
                nHit = TOOLBAR_InternalHitTest(hwnd, &pt);
5719 5720
                if (!infoPtr->bAnchor || (nHit >= 0))
                    TOOLBAR_SetHotItemEx(infoPtr, nHit, HICF_MOUSE | HICF_LMOUSE);
5721
                
5722 5723 5724 5725
                /* remove any left mouse button down or double-click messages
                 * so that we can get a toggle effect on the button */
                while (PeekMessageW(&msg, hwnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE) ||
                       PeekMessageW(&msg, hwnd, WM_LBUTTONDBLCLK, WM_LBUTTONDBLCLK, PM_REMOVE))
5726 5727
                    ;

5728
		return 0;
5729
            }
5730 5731 5732 5733
	    /* otherwise drop through and process as pushed */
       	}
	infoPtr->bCaptured = TRUE;
	infoPtr->nButtonDown = nHit;
5734
	infoPtr->bDragOutSent = FALSE;
5735

5736
	btnPtr->fsState |= TBSTATE_PRESSED;
5737 5738

        TOOLBAR_SetHotItemEx(infoPtr, nHit, HICF_MOUSE | HICF_LMOUSE);
5739

5740
        if (btnPtr->fsState & TBSTATE_ENABLED)
Robert Shearman's avatar
Robert Shearman committed
5741
	    InvalidateRect(hwnd, &btnPtr->rect, TRUE);
5742 5743
	UpdateWindow(hwnd);
	SetCapture (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
5744 5745
    }

5746 5747
    if (nHit >=0)
    {
5748
        memset(&nmtb, 0, sizeof(nmtb));
5749 5750 5751
        nmtb.iItem = btnPtr->idCommand;
        TOOLBAR_SendNotify((NMHDR *)&nmtb, infoPtr, TBN_BEGINDRAG);
    }
Robert Shearman's avatar
Robert Shearman committed
5752

5753 5754 5755
    nmmouse.dwHitInfo = nHit;

    /* !!! Undocumented - sends NM_LDOWN with the NMMOUSE structure. */
5756
    if (nHit < 0)
5757 5758 5759 5760 5761 5762 5763 5764 5765 5766
        nmmouse.dwItemSpec = -1;
    else
    {
        nmmouse.dwItemSpec = infoPtr->buttons[nmmouse.dwHitInfo].idCommand;
        nmmouse.dwItemData = infoPtr->buttons[nmmouse.dwHitInfo].dwData;
    }

    ClientToScreen(hwnd, &pt); 
    nmmouse.pt = pt;

5767
    if (!TOOLBAR_SendNotify(&nmmouse.hdr, infoPtr, NM_LDOWN))
5768 5769
        return DefWindowProcW(hwnd, WM_LBUTTONDOWN, wParam, lParam);

Alexandre Julliard's avatar
Alexandre Julliard committed
5770 5771 5772 5773
    return 0;
}

static LRESULT
5774
TOOLBAR_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
5775
{
5776
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
5777
    TBUTTON_INFO *btnPtr;
5778 5779 5780 5781
    POINT pt;
    INT   nHit;
    INT   nOldIndex = -1;
    BOOL  bSendMessage = TRUE;
5782 5783 5784
    NMHDR hdr;
    NMMOUSE nmmouse;
    NMTOOLBARA nmtb;
Alexandre Julliard's avatar
Alexandre Julliard committed
5785

Alexandre Julliard's avatar
Alexandre Julliard committed
5786
    if (infoPtr->hwndToolTip)
5787
	TOOLBAR_RelayEvent (infoPtr->hwndToolTip, hwnd,
Alexandre Julliard's avatar
Alexandre Julliard committed
5788 5789
			    WM_LBUTTONUP, wParam, lParam);

5790 5791
    pt.x = (short)LOWORD(lParam);
    pt.y = (short)HIWORD(lParam);
5792
    nHit = TOOLBAR_InternalHitTest (hwnd, &pt);
Alexandre Julliard's avatar
Alexandre Julliard committed
5793

5794 5795
    if (!infoPtr->bAnchor || (nHit >= 0))
        TOOLBAR_SetHotItemEx(infoPtr, nHit, HICF_MOUSE | HICF_LMOUSE);
5796

Robert Shearman's avatar
Robert Shearman committed
5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831
    if (infoPtr->nButtonDrag >= 0) {
        RECT rcClient;
        NMHDR hdr;

        btnPtr = &infoPtr->buttons[infoPtr->nButtonDrag];
        ReleaseCapture();
        /* reset cursor */
        SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_ARROW));

        GetClientRect(hwnd, &rcClient);
        if (PtInRect(&rcClient, pt))
        {
            INT nButton = -1;
            if (nHit >= 0)
                nButton = nHit;
            else if (nHit < -1)
                nButton = -nHit;
            else if ((nHit == -1) && PtInRect(&infoPtr->buttons[-nHit].rect, pt))
                nButton = -nHit;

            if (nButton == infoPtr->nButtonDrag)
            {
                /* if the button is moved sightly left and we have a
                 * separator there then remove it */
                if (pt.x < (btnPtr->rect.left + (btnPtr->rect.right - btnPtr->rect.left)/2))
                {
                    if ((nButton > 0) && (infoPtr->buttons[nButton-1].fsStyle & BTNS_SEP))
                        TOOLBAR_DeleteButton(hwnd, nButton - 1, 0);
                }
                else /* else insert a separator before the dragged button */
                {
                    TBBUTTON tbb;
                    memset(&tbb, 0, sizeof(tbb));
                    tbb.fsStyle = BTNS_SEP;
                    tbb.iString = -1;
5832
                    TOOLBAR_InsertButtonT(hwnd, nButton, (LPARAM)&tbb, TRUE);
Robert Shearman's avatar
Robert Shearman committed
5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860
                }
            }
            else
            {
                if (nButton == -1)
                {
                    if ((infoPtr->nNumButtons > 0) && (pt.x < infoPtr->buttons[0].rect.left))
                        TOOLBAR_MoveButton(hwnd, infoPtr->nButtonDrag, 0);
                    else
                        TOOLBAR_MoveButton(hwnd, infoPtr->nButtonDrag, infoPtr->nNumButtons);
                }
                else
                    TOOLBAR_MoveButton(hwnd, infoPtr->nButtonDrag, nButton);
            }
        }
        else
        {
            TRACE("button %d dragged out of toolbar\n", infoPtr->nButtonDrag);
            TOOLBAR_DeleteButton(hwnd, (WPARAM)infoPtr->nButtonDrag, 0);
        }

        /* button under cursor changed so need to re-set hot item */
        TOOLBAR_SetHotItemEx(infoPtr, nHit, HICF_MOUSE | HICF_LMOUSE);
        infoPtr->nButtonDrag = -1;

        TOOLBAR_SendNotify(&hdr, infoPtr, TBN_TOOLBARCHANGE);
    }
    else if (infoPtr->nButtonDown >= 0) {
Alexandre Julliard's avatar
Alexandre Julliard committed
5861
	btnPtr = &infoPtr->buttons[infoPtr->nButtonDown];
Alexandre Julliard's avatar
Alexandre Julliard committed
5862
	btnPtr->fsState &= ~TBSTATE_PRESSED;
Alexandre Julliard's avatar
Alexandre Julliard committed
5863

5864 5865
	if (btnPtr->fsStyle & BTNS_CHECK) {
		if (btnPtr->fsStyle & BTNS_GROUP) {
Alexandre Julliard's avatar
Alexandre Julliard committed
5866
		    nOldIndex = TOOLBAR_GetCheckedGroupButtonIndex (infoPtr,
5867 5868
			nHit);
		    if (nOldIndex == nHit)
Alexandre Julliard's avatar
Alexandre Julliard committed
5869
			bSendMessage = FALSE;
5870
		    if ((nOldIndex != nHit) &&
Alexandre Julliard's avatar
Alexandre Julliard committed
5871 5872
			(nOldIndex != -1))
			infoPtr->buttons[nOldIndex].fsState &= ~TBSTATE_CHECKED;
Alexandre Julliard's avatar
Alexandre Julliard committed
5873
		    btnPtr->fsState |= TBSTATE_CHECKED;
Alexandre Julliard's avatar
Alexandre Julliard committed
5874 5875 5876 5877 5878 5879 5880
		}
		else {
		    if (btnPtr->fsState & TBSTATE_CHECKED)
			btnPtr->fsState &= ~TBSTATE_CHECKED;
		    else
			btnPtr->fsState |= TBSTATE_CHECKED;
		}
Alexandre Julliard's avatar
Alexandre Julliard committed
5881
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
5882

Robert Shearman's avatar
Robert Shearman committed
5883 5884
        if (nOldIndex != -1)
            InvalidateRect(hwnd, &infoPtr->buttons[nOldIndex].rect, TRUE);
5885

5886 5887 5888
	/*
	 * now we can ReleaseCapture, which triggers CAPTURECHANGED msg,
	 * that resets bCaptured and btn TBSTATE_PRESSED flags,
5889
	 * and obliterates nButtonDown and nOldHit (see TOOLBAR_CaptureChanged)
5890
	 */
5891 5892
	if ((infoPtr->bCaptured) && (infoPtr->nButtonDown >= 0))
	    ReleaseCapture ();
5893
	infoPtr->nButtonDown = -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
5894

5895
	/* Issue NM_RELEASEDCAPTURE to parent to let him know it is released */
5896 5897
	TOOLBAR_SendNotify ((NMHDR *) &hdr, infoPtr,
			NM_RELEASEDCAPTURE);
5898

5899
	/* native issues TBN_ENDDRAG here, if _LBUTTONDOWN issued the
5900 5901
	 * TBN_BEGINDRAG
	 */
5902
	memset(&nmtb, 0, sizeof(nmtb));
5903
	nmtb.iItem = btnPtr->idCommand;
5904 5905
	TOOLBAR_SendNotify ((NMHDR *) &nmtb, infoPtr,
			TBN_ENDDRAG);
5906

5907 5908
	if (btnPtr->fsState & TBSTATE_ENABLED)
	{
5909
	    SendMessageW (infoPtr->hwndNotify, WM_COMMAND,
5910
	      MAKEWPARAM(infoPtr->buttons[nHit].idCommand, BN_CLICKED), (LPARAM)hwnd);
5911 5912 5913 5914

            /* In case we have just been destroyed... */
            if(!IsWindow(hwnd))
                return 0;
5915 5916
        }
    }
5917

5918 5919 5920 5921
    /* !!! Undocumented - toolbar at 4.71 level and above sends
    * NM_CLICK with the NMMOUSE structure. */
    nmmouse.dwHitInfo = nHit;

5922
    if (nHit < 0)
5923 5924 5925 5926 5927
        nmmouse.dwItemSpec = -1;
    else
    {
        nmmouse.dwItemSpec = infoPtr->buttons[nmmouse.dwHitInfo].idCommand;
        nmmouse.dwItemData = infoPtr->buttons[nmmouse.dwHitInfo].dwData;
5928
    }
5929 5930 5931 5932 5933 5934 5935

    ClientToScreen(hwnd, &pt); 
    nmmouse.pt = pt;

    if (!TOOLBAR_SendNotify((LPNMHDR)&nmmouse, infoPtr, NM_CLICK))
        return DefWindowProcW(hwnd, WM_LBUTTONUP, wParam, lParam);

Alexandre Julliard's avatar
Alexandre Julliard committed
5936 5937 5938
    return 0;
}

5939 5940 5941 5942
static LRESULT
TOOLBAR_RButtonUp( HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
5943
    INT nHit;
5944 5945 5946
    NMMOUSE nmmouse;
    POINT pt;

5947 5948
    pt.x = (short)LOWORD(lParam);
    pt.y = (short)HIWORD(lParam);
5949

5950 5951
    nHit = TOOLBAR_InternalHitTest(hwnd, &pt);
    nmmouse.dwHitInfo = nHit;
5952

5953
    if (nHit < 0) {
5954 5955 5956 5957 5958 5959 5960
	nmmouse.dwItemSpec = -1;
    } else {
	nmmouse.dwItemSpec = infoPtr->buttons[nmmouse.dwHitInfo].idCommand;
	nmmouse.dwItemData = infoPtr->buttons[nmmouse.dwHitInfo].dwData;
    }

    ClientToScreen(hwnd, &pt); 
5961
    nmmouse.pt = pt;
5962

5963 5964
    if (!TOOLBAR_SendNotify((LPNMHDR)&nmmouse, infoPtr, NM_RCLICK))
        return DefWindowProcW(hwnd, WM_RBUTTONUP, wParam, lParam);
5965 5966 5967 5968

    return 0;
}

5969 5970 5971 5972
static LRESULT
TOOLBAR_RButtonDblClk( HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
5973
    NMHDR nmhdr;
5974

5975 5976
    if (!TOOLBAR_SendNotify(&nmhdr, infoPtr, NM_RDBLCLK))
        return DefWindowProcW(hwnd, WM_RBUTTONDBLCLK, wParam, lParam);
5977 5978 5979 5980

    return 0;
}

5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995
static LRESULT
TOOLBAR_CaptureChanged(HWND hwnd)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    TBUTTON_INFO *btnPtr;

    infoPtr->bCaptured = FALSE;

    if (infoPtr->nButtonDown >= 0)
    {
        btnPtr = &infoPtr->buttons[infoPtr->nButtonDown];
       	btnPtr->fsState &= ~TBSTATE_PRESSED;

        infoPtr->nOldHit = -1;

5996
        if (btnPtr->fsState & TBSTATE_ENABLED)
Robert Shearman's avatar
Robert Shearman committed
5997
            InvalidateRect(hwnd, &btnPtr->rect, TRUE);
5998 5999 6000 6001
    }
    return 0;
}

6002 6003 6004 6005 6006
static LRESULT
TOOLBAR_MouseLeave (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);

6007 6008
    /* don't remove hot effects when in anchor highlighting mode or when a
     * drop-down button is pressed */
6009 6010 6011 6012 6013 6014
    if (infoPtr->nHotItem >= 0 && !infoPtr->bAnchor)
    {
        TBUTTON_INFO *hotBtnPtr = &infoPtr->buttons[infoPtr->nHotItem];
        if (!hotBtnPtr->bDropDownPressed)
            TOOLBAR_SetHotItemEx(infoPtr, TOOLBAR_NOWHERE, HICF_MOUSE);
    }
6015

6016 6017 6018
    if (infoPtr->nOldHit < 0)
      return TRUE;

6019 6020 6021 6022
    /* If the last button we were over is depressed then make it not */
    /* depressed and redraw it */
    if(infoPtr->nOldHit == infoPtr->nButtonDown)
    {
6023 6024 6025
      TBUTTON_INFO *btnPtr;
      RECT rc1;

6026 6027 6028 6029
      btnPtr = &infoPtr->buttons[infoPtr->nButtonDown];

      btnPtr->fsState &= ~TBSTATE_PRESSED;

6030
      rc1 = btnPtr->rect;
6031 6032
      InflateRect (&rc1, 1, 1);
      InvalidateRect (hwnd, &rc1, TRUE);
6033 6034
    }

6035 6036 6037 6038 6039 6040 6041 6042 6043
    if (infoPtr->bCaptured && !infoPtr->bDragOutSent)
    {
        NMTOOLBARW nmt;
        ZeroMemory(&nmt, sizeof(nmt));
        nmt.iItem = infoPtr->buttons[infoPtr->nButtonDown].idCommand;
        TOOLBAR_SendNotify(&nmt.hdr, infoPtr, TBN_DRAGOUT);
        infoPtr->bDragOutSent = TRUE;
    }

6044 6045 6046 6047
    infoPtr->nOldHit = -1; /* reset the old hit index as we've left the toolbar */

    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
6048 6049

static LRESULT
6050
TOOLBAR_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
6051
{
6052
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
6053
    POINT pt;
6054
    TRACKMOUSEEVENT trackinfo;
6055 6056
    INT   nHit;
    TBUTTON_INFO *btnPtr;
6057
    
6058 6059 6060
    if ((infoPtr->dwStyle & TBSTYLE_TOOLTIPS) && (infoPtr->hwndToolTip == NULL))
        TOOLBAR_TooltipCreateControl(infoPtr);
    
6061
    if ((infoPtr->dwStyle & TBSTYLE_FLAT) || GetWindowTheme (infoPtr->hwndSelf)) {
6062 6063 6064 6065 6066 6067
        /* fill in the TRACKMOUSEEVENT struct */
        trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
        trackinfo.dwFlags = TME_QUERY;

        /* call _TrackMouseEvent to see if we are currently tracking for this hwnd */
        _TrackMouseEvent(&trackinfo);
6068

6069
        /* Make sure tracking is enabled so we receive a WM_MOUSELEAVE message */
6070
        if(trackinfo.hwndTrack != hwnd || !(trackinfo.dwFlags & TME_LEAVE)) {
6071
            trackinfo.dwFlags = TME_LEAVE; /* notify upon leaving */
6072
            trackinfo.hwndTrack = hwnd;
6073

6074 6075 6076 6077 6078
            /* call TRACKMOUSEEVENT so we receive a WM_MOUSELEAVE message */
            /* and can properly deactivate the hot toolbar button */
            _TrackMouseEvent(&trackinfo);
       }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
6079

Alexandre Julliard's avatar
Alexandre Julliard committed
6080
    if (infoPtr->hwndToolTip)
6081
	TOOLBAR_RelayEvent (infoPtr->hwndToolTip, hwnd,
Alexandre Julliard's avatar
Alexandre Julliard committed
6082 6083
			    WM_MOUSEMOVE, wParam, lParam);

6084 6085
    pt.x = (short)LOWORD(lParam);
    pt.y = (short)HIWORD(lParam);
6086

6087
    nHit = TOOLBAR_InternalHitTest (hwnd, &pt);
Alexandre Julliard's avatar
Alexandre Julliard committed
6088

6089 6090
    if (((infoPtr->dwStyle & TBSTYLE_FLAT) || GetWindowTheme (infoPtr->hwndSelf)) 
        && (!infoPtr->bAnchor || (nHit >= 0)))
6091
        TOOLBAR_SetHotItemEx(infoPtr, nHit, HICF_MOUSE);
6092

6093 6094
    if (infoPtr->nOldHit != nHit)
    {
6095 6096
        if (infoPtr->bCaptured)
        {
6097 6098 6099 6100 6101 6102 6103 6104 6105
            if (!infoPtr->bDragOutSent)
            {
                NMTOOLBARW nmt;
                ZeroMemory(&nmt, sizeof(nmt));
                nmt.iItem = infoPtr->buttons[infoPtr->nButtonDown].idCommand;
                TOOLBAR_SendNotify(&nmt.hdr, infoPtr, TBN_DRAGOUT);
                infoPtr->bDragOutSent = TRUE;
            }

6106 6107 6108
            btnPtr = &infoPtr->buttons[infoPtr->nButtonDown];
            if (infoPtr->nOldHit == infoPtr->nButtonDown) {
                btnPtr->fsState &= ~TBSTATE_PRESSED;
6109
                InvalidateRect(hwnd, &btnPtr->rect, TRUE);
6110 6111 6112
            }
            else if (nHit == infoPtr->nButtonDown) {
                btnPtr->fsState |= TBSTATE_PRESSED;
6113
                InvalidateRect(hwnd, &btnPtr->rect, TRUE);
6114 6115 6116
            }
            infoPtr->nOldHit = nHit;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
6117
    }
6118

Alexandre Julliard's avatar
Alexandre Julliard committed
6119 6120 6121 6122
    return 0;
}


6123
static inline LRESULT
6124
TOOLBAR_NCActivate (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
6125
{
6126
/*    if (wndPtr->dwStyle & CCS_NODIVIDER) */
6127
	return DefWindowProcW (hwnd, WM_NCACTIVATE, wParam, lParam);
6128 6129
/*    else */
/*	return TOOLBAR_NCPaint (wndPtr, wParam, lParam); */
Alexandre Julliard's avatar
Alexandre Julliard committed
6130
}
Alexandre Julliard's avatar
Alexandre Julliard committed
6131 6132


6133
static inline LRESULT
6134
TOOLBAR_NCCalcSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
6135
{
6136
    if (!(GetWindowLongW(hwnd, GWL_STYLE) & CCS_NODIVIDER))
6137
	((LPRECT)lParam)->top += GetSystemMetrics(SM_CYEDGE);
Alexandre Julliard's avatar
Alexandre Julliard committed
6138

6139
    return DefWindowProcW (hwnd, WM_NCCALCSIZE, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6140
}
Alexandre Julliard's avatar
Alexandre Julliard committed
6141 6142 6143


static LRESULT
6144
TOOLBAR_NCCreate (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
6145
{
Alexandre Julliard's avatar
Alexandre Julliard committed
6146
    TOOLBAR_INFO *infoPtr;
6147 6148
    LPCREATESTRUCTA cs = (LPCREATESTRUCTA)lParam;
    DWORD styleadd = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
6149

Alexandre Julliard's avatar
Alexandre Julliard committed
6150
    /* allocate memory for info structure */
6151
    infoPtr = (TOOLBAR_INFO *)Alloc (sizeof(TOOLBAR_INFO));
6152
    SetWindowLongPtrW (hwnd, 0, (LONG_PTR)infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
6153

Alexandre Julliard's avatar
Alexandre Julliard committed
6154
    /* paranoid!! */
Alexandre Julliard's avatar
Alexandre Julliard committed
6155
    infoPtr->dwStructSize = sizeof(TBBUTTON);
6156
    infoPtr->nRows = 1;
6157
    infoPtr->nWidth = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
6158

Alexandre Julliard's avatar
Alexandre Julliard committed
6159
    /* fix instance handle, if the toolbar was created by CreateToolbarEx() */
6160 6161 6162
    if (!GetWindowLongPtrW (hwnd, GWLP_HINSTANCE)) {
        HINSTANCE hInst = (HINSTANCE)GetWindowLongPtrW (GetParent (hwnd), GWLP_HINSTANCE);
	SetWindowLongPtrW (hwnd, GWLP_HINSTANCE, (LONG_PTR)hInst);
6163
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
6164

6165 6166 6167
    /* native control does:
     *    Get a lot of colors and brushes
     *    WM_NOTIFYFORMAT
6168 6169
     *    SystemParametersInfoW(0x1f, 0x3c, adr1, 0)
     *    CreateFontIndirectW(adr1)
6170 6171 6172
     *    CreateBitmap(0x27, 0x24, 1, 1, 0)
     *    hdc = GetDC(toolbar)
     *    GetSystemMetrics(0x48)
6173
     *    fnt2=CreateFontW(0xe, 0, 0, 0, 0x190, 0, 0, 0, 0, 2,
6174 6175
     *                     0, 0, 0, 0, "MARLETT")
     *    oldfnt = SelectObject(hdc, fnt2)
6176 6177
     *    GetCharWidthW(hdc, 0x36, 0x36, adr2)
     *    GetTextMetricsW(hdc, adr3)
6178 6179 6180 6181
     *    SelectObject(hdc, oldfnt)
     *    DeleteObject(fnt2)
     *    ReleaseDC(hdc)
     *    InvalidateRect(toolbar, 0, 1)
6182 6183
     *    SetWindowLongW(toolbar, 0, addr)
     *    SetWindowLongW(toolbar, -16, xxx)  **sometimes**
6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202
     *                                          WM_STYLECHANGING
     *                             CallWinEx   old         new
     *                       ie 1  0x56000a4c  0x46000a4c  0x56008a4d
     *                       ie 2  0x4600094c  0x4600094c  0x4600894d
     *                       ie 3  0x56000b4c  0x46000b4c  0x56008b4d
     *                      rebar  0x50008844  0x40008844  0x50008845
     *                      pager  0x50000844  0x40000844  0x50008845
     *                    IC35mgr  0x5400084e  **nochange**
     *           on entry to _NCCREATE         0x5400084e
     *                    rowlist  0x5400004e  **nochange**
     *           on entry to _NCCREATE         0x5400004e
     *
     */

    /* I think the code below is a bug, but it is the way that the native
     * controls seem to work. The effect is that if the user of TBSTYLE_FLAT
     * forgets to specify TBSTYLE_TRANSPARENT but does specify either
     * CCS_TOP or CCS_BOTTOM (_NOMOVEY and _TOP), then the control
     * does *not* set TBSTYLE_TRANSPARENT even though it should!!!!
6203
     * Somehow, the only cases of this seem to be MFC programs.
6204 6205 6206 6207 6208 6209
     *
     * Note also that the addition of _TRANSPARENT occurs *only* here. It
     * does not occur in the WM_STYLECHANGING routine.
     *    (Guy Albertelli   9/2001)
     *
     */
6210 6211
    if (((infoPtr->dwStyle & TBSTYLE_FLAT) || GetWindowTheme (infoPtr->hwndSelf)) 
        && !(cs->style & TBSTYLE_TRANSPARENT))
6212 6213 6214
	styleadd |= TBSTYLE_TRANSPARENT;
    if (!(cs->style & (CCS_TOP | CCS_NOMOVEY))) {
	styleadd |= CCS_TOP;   /* default to top */
6215
	SetWindowLongW (hwnd, GWL_STYLE, cs->style | styleadd);
6216 6217
    }

6218
    return DefWindowProcW (hwnd, WM_NCCREATE, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6219 6220 6221 6222
}


static LRESULT
6223
TOOLBAR_NCPaint (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
6224
{
6225
    DWORD dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
6226
    RECT rcWindow;
6227
    HDC hdc;
Alexandre Julliard's avatar
Alexandre Julliard committed
6228

6229 6230
    if (dwStyle & WS_MINIMIZE)
	return 0; /* Nothing to do */
Alexandre Julliard's avatar
Alexandre Julliard committed
6231

6232
    DefWindowProcW (hwnd, WM_NCPAINT, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6233

6234
    if (!(hdc = GetDCEx (hwnd, 0, DCX_USESTYLE | DCX_WINDOW)))
Alexandre Julliard's avatar
Alexandre Julliard committed
6235 6236
	return 0;

6237 6238 6239 6240
    if (!(dwStyle & CCS_NODIVIDER))
    {
	GetWindowRect (hwnd, &rcWindow);
	OffsetRect (&rcWindow, -rcWindow.left, -rcWindow.top);
6241 6242
	if( dwStyle & WS_BORDER )
	    OffsetRect (&rcWindow, 1, 1);
6243 6244
	DrawEdge (hdc, &rcWindow, EDGE_ETCHED, BF_TOP);
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
6245

6246
    ReleaseDC( hwnd, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
6247 6248 6249 6250 6251

    return 0;
}


6252 6253
/* handles requests from the tooltip control on what text to display */
static LRESULT TOOLBAR_TTGetDispInfo (TOOLBAR_INFO *infoPtr, NMTTDISPINFOW *lpnmtdi)
Alexandre Julliard's avatar
Alexandre Julliard committed
6254
{
6255
    int index = TOOLBAR_GetButtonIndex(infoPtr, lpnmtdi->hdr.idFrom, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
6256

6257
    TRACE("button index = %d\n", index);
6258

6259
    Free (infoPtr->pszTooltipText);
6260
    infoPtr->pszTooltipText = NULL;
6261

6262 6263
    if (index < 0)
        return 0;
6264

6265
    if (infoPtr->bUnicode)
6266 6267 6268
    {
        WCHAR wszBuffer[INFOTIPSIZE+1];
        NMTBGETINFOTIPW tbgit;
6269
        unsigned int len; /* in chars */
6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288

        wszBuffer[0] = '\0';
        wszBuffer[INFOTIPSIZE] = '\0';

        tbgit.pszText = wszBuffer;
        tbgit.cchTextMax = INFOTIPSIZE;
        tbgit.iItem = lpnmtdi->hdr.idFrom;
        tbgit.lParam = infoPtr->buttons[index].dwData;

        TOOLBAR_SendNotify(&tbgit.hdr, infoPtr, TBN_GETINFOTIPW);

        TRACE("TBN_GETINFOTIPW - got string %s\n", debugstr_w(tbgit.pszText));

        len = strlenW(tbgit.pszText);
        if (len > sizeof(lpnmtdi->szText)/sizeof(lpnmtdi->szText[0])-1)
        {
            /* need to allocate temporary buffer in infoPtr as there
             * isn't enough space in buffer passed to us by the
             * tooltip control */
6289
            infoPtr->pszTooltipText = Alloc((len+1)*sizeof(WCHAR));
6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301
            if (infoPtr->pszTooltipText)
            {
                memcpy(infoPtr->pszTooltipText, tbgit.pszText, (len+1)*sizeof(WCHAR));
                lpnmtdi->lpszText = infoPtr->pszTooltipText;
                return 0;
            }
        }
        else if (len > 0)
        {
            memcpy(lpnmtdi->lpszText, tbgit.pszText, (len+1)*sizeof(WCHAR));
            return 0;
        }
6302
    }
6303 6304 6305 6306
    else
    {
        CHAR szBuffer[INFOTIPSIZE+1];
        NMTBGETINFOTIPA tbgit;
6307
        unsigned int len; /* in chars */
6308

6309 6310
        szBuffer[0] = '\0';
        szBuffer[INFOTIPSIZE] = '\0';
6311

6312 6313 6314 6315
        tbgit.pszText = szBuffer;
        tbgit.cchTextMax = INFOTIPSIZE;
        tbgit.iItem = lpnmtdi->hdr.idFrom;
        tbgit.lParam = infoPtr->buttons[index].dwData;
Alexandre Julliard's avatar
Alexandre Julliard committed
6316

6317
        TOOLBAR_SendNotify(&tbgit.hdr, infoPtr, TBN_GETINFOTIPA);
Alexandre Julliard's avatar
Alexandre Julliard committed
6318

6319 6320
        TRACE("TBN_GETINFOTIPA - got string %s\n", debugstr_a(tbgit.pszText));

6321 6322
        len = MultiByteToWideChar(CP_ACP, 0, tbgit.pszText, -1, NULL, 0);
        if (len > sizeof(lpnmtdi->szText)/sizeof(lpnmtdi->szText[0]))
6323 6324 6325 6326
        {
            /* need to allocate temporary buffer in infoPtr as there
             * isn't enough space in buffer passed to us by the
             * tooltip control */
6327
            infoPtr->pszTooltipText = Alloc(len*sizeof(WCHAR));
6328 6329
            if (infoPtr->pszTooltipText)
            {
6330
                MultiByteToWideChar(CP_ACP, 0, tbgit.pszText, -1, infoPtr->pszTooltipText, len);
6331 6332 6333 6334 6335 6336
                lpnmtdi->lpszText = infoPtr->pszTooltipText;
                return 0;
            }
        }
        else if (len > 0)
        {
6337 6338
            MultiByteToWideChar(CP_ACP, 0, tbgit.pszText, -1,
                                lpnmtdi->lpszText, sizeof(lpnmtdi->szText)/sizeof(lpnmtdi->szText[0]));
6339 6340 6341
            return 0;
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
6342

6343 6344 6345 6346 6347 6348
    /* if button has text, but it is not shown then automatically
     * use that text as tooltip */
    if ((infoPtr->dwExStyle & TBSTYLE_EX_MIXEDBUTTONS) &&
        !(infoPtr->buttons[index].fsStyle & BTNS_SHOWTEXT))
    {
        LPWSTR pszText = TOOLBAR_GetText(infoPtr, &infoPtr->buttons[index]);
6349
        unsigned int len = pszText ? strlenW(pszText) : 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
6350

6351 6352 6353 6354 6355 6356 6357
        TRACE("using button hidden text %s\n", debugstr_w(pszText));

        if (len > sizeof(lpnmtdi->szText)/sizeof(lpnmtdi->szText[0])-1)
        {
            /* need to allocate temporary buffer in infoPtr as there
             * isn't enough space in buffer passed to us by the
             * tooltip control */
6358
            infoPtr->pszTooltipText = Alloc((len+1)*sizeof(WCHAR));
6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371
            if (infoPtr->pszTooltipText)
            {
                memcpy(infoPtr->pszTooltipText, pszText, (len+1)*sizeof(WCHAR));
                lpnmtdi->lpszText = infoPtr->pszTooltipText;
                return 0;
            }
        }
        else if (len > 0)
        {
            memcpy(lpnmtdi->lpszText, pszText, (len+1)*sizeof(WCHAR));
            return 0;
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
6372

6373
    TRACE("Sending tooltip notification to %p\n", infoPtr->hwndNotify);
Alexandre Julliard's avatar
Alexandre Julliard committed
6374

6375 6376 6377 6378 6379 6380
    /* last resort: send notification on to app */
    /* FIXME: find out what is really used here */
    return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, 0, (LPARAM)lpnmtdi);
}


6381
static inline LRESULT
6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403
TOOLBAR_Notify (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    LPNMHDR lpnmh = (LPNMHDR)lParam;

    switch (lpnmh->code)
    {
    case PGN_CALCSIZE:
    {
        LPNMPGCALCSIZE lppgc = (LPNMPGCALCSIZE)lParam;

        if (lppgc->dwFlag == PGF_CALCWIDTH) {
            lppgc->iWidth = infoPtr->rcBound.right - infoPtr->rcBound.left;
            TRACE("processed PGN_CALCSIZE, returning horz size = %d\n",
                  lppgc->iWidth);
        }
        else {
            lppgc->iHeight = infoPtr->rcBound.bottom - infoPtr->rcBound.top;
            TRACE("processed PGN_CALCSIZE, returning vert size = %d\n",
                  lppgc->iHeight);
        }
    	return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
6404 6405
    }

6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420
    case PGN_SCROLL:
    {
        LPNMPGSCROLL lppgs = (LPNMPGSCROLL)lParam;

        lppgs->iScroll = (lppgs->iDir & (PGF_SCROLLLEFT | PGF_SCROLLRIGHT)) ?
                          infoPtr->nButtonWidth : infoPtr->nButtonHeight;
        TRACE("processed PGN_SCROLL, returning scroll=%d, dir=%d\n",
              lppgs->iScroll, lppgs->iDir);
        return 0;
    }

    case TTN_GETDISPINFOW:
        return TOOLBAR_TTGetDispInfo(infoPtr, (LPNMTTDISPINFOW)lParam);

    case TTN_GETDISPINFOA:
6421
        FIXME("TTN_GETDISPINFOA - should not be received; please report\n");
6422 6423 6424 6425 6426
        return 0;

    default:
        return 0;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
6427 6428 6429
}


6430
static LRESULT
6431
TOOLBAR_NotifyFormat(const TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
6432
{
6433
    LRESULT format;
6434

6435
    TRACE("wParam = 0x%lx, lParam = 0x%08lx\n", wParam, lParam);
6436

6437
    if (lParam == NF_QUERY)
6438 6439
        return NFR_UNICODE;

6440
    if (lParam == NF_REQUERY) {
6441
	format = SendMessageW(infoPtr->hwndNotify,
6442
			 WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
6443 6444 6445 6446
	if ((format != NFR_ANSI) && (format != NFR_UNICODE)) {
	    ERR("wrong response to WM_NOTIFYFORMAT (%ld), assuming ANSI\n",
		format);
	    format = NFR_ANSI;
6447
	}
6448
	return format;
6449
    }
6450
    return 0;
6451 6452 6453
}


Alexandre Julliard's avatar
Alexandre Julliard committed
6454
static LRESULT
6455
TOOLBAR_Paint (HWND hwnd, WPARAM wParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
6456
{
6457
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr(hwnd);
6458 6459
    HDC hdc;
    PAINTSTRUCT ps;
Alexandre Julliard's avatar
Alexandre Julliard committed
6460

6461
    /* fill ps.rcPaint with a default rect */
6462
    memcpy(&(ps.rcPaint), &(infoPtr->rcBound), sizeof(infoPtr->rcBound));
6463 6464

    hdc = wParam==0 ? BeginPaint(hwnd, &ps) : (HDC)wParam;
6465

6466
    TRACE("psrect=(%d,%d)-(%d,%d)\n",
6467 6468 6469
	  ps.rcPaint.left, ps.rcPaint.top,
	  ps.rcPaint.right, ps.rcPaint.bottom);

6470 6471 6472
    TOOLBAR_Refresh (hwnd, hdc, &ps);
    if (!wParam) EndPaint (hwnd, &ps);

Alexandre Julliard's avatar
Alexandre Julliard committed
6473 6474 6475 6476
    return 0;
}


6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490
static LRESULT
TOOLBAR_SetFocus (HWND hwnd, WPARAM wParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);

    TRACE("nHotItem = %d\n", infoPtr->nHotItem);

    /* make first item hot */
    if (infoPtr->nNumButtons > 0)
        TOOLBAR_SetHotItemEx(infoPtr, 0, HICF_OTHER);

    return 0;
}

6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508
static LRESULT
TOOLBAR_SetFont(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr(hwnd);
    
    TRACE("font=%p redraw=%ld\n", (HFONT)wParam, lParam);
    
    if (wParam == 0)
        infoPtr->hFont = infoPtr->hDefaultFont;
    else
        infoPtr->hFont = (HFONT)wParam;

    TOOLBAR_CalcToolbar(hwnd);

    if (lParam)
        InvalidateRect(hwnd, NULL, TRUE);
    return 1;
}
6509

6510 6511 6512 6513 6514 6515 6516 6517
static LRESULT
TOOLBAR_SetRedraw (HWND hwnd, WPARAM wParam, LPARAM lParam)
     /*****************************************************
      *
      * Function;
      *  Handles the WM_SETREDRAW message.
      *
      * Documentation:
6518
      *  According to testing V4.71 of COMCTL32 returns the
6519 6520
      *  *previous* status of the redraw flag (either 0 or 1)
      *  instead of the MSDN documented value of 0 if handled.
Francois Gouget's avatar
Francois Gouget committed
6521
      *  (For laughs see the "consistency" with same function
6522 6523 6524 6525 6526 6527 6528
      *   in rebar.)
      *
      *****************************************************/
{
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
    BOOL oldredraw = infoPtr->bDoRedraw;

6529
    TRACE("set to %s\n",
6530 6531 6532 6533 6534 6535 6536 6537 6538
	  (wParam) ? "TRUE" : "FALSE");
    infoPtr->bDoRedraw = (BOOL) wParam;
    if (wParam) {
	InvalidateRect (infoPtr->hwndSelf, 0, TRUE);
    }
    return (oldredraw) ? 1 : 0;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
6539
static LRESULT
6540
TOOLBAR_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
6541
{
6542
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
6543

6544
    TRACE("sizing toolbar!\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
6545

6546 6547 6548 6549 6550 6551
    if (infoPtr->dwExStyle & TBSTYLE_EX_HIDECLIPPEDBUTTONS)
    {
        RECT delta_width, delta_height, client, dummy;
        DWORD min_x, max_x, min_y, max_y;
        TBUTTON_INFO *btnPtr;
        INT i;
6552

6553 6554
        GetClientRect(hwnd, &client);
        if(client.right > infoPtr->client_rect.right)
6555
        {
6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572
            min_x = infoPtr->client_rect.right;
            max_x = client.right;
        }
        else
        {
            max_x = infoPtr->client_rect.right;
            min_x = client.right;
        }
        if(client.bottom > infoPtr->client_rect.bottom)
        {
            min_y = infoPtr->client_rect.bottom;
            max_y = client.bottom;
        }
        else
        {
            max_y = infoPtr->client_rect.bottom;
            min_y = client.bottom;
6573 6574
        }

6575 6576 6577 6578 6579 6580 6581 6582 6583
        SetRect(&delta_width, min_x, 0, max_x, min_y);
        SetRect(&delta_height, 0, min_y, max_x, max_y);

        TRACE("delta_width %s delta_height %s\n", wine_dbgstr_rect(&delta_width), wine_dbgstr_rect(&delta_height));
        btnPtr = infoPtr->buttons;
        for (i = 0; i < infoPtr->nNumButtons; i++, btnPtr++)
            if(IntersectRect(&dummy, &delta_width, &btnPtr->rect) ||
                IntersectRect(&dummy, &delta_height, &btnPtr->rect))
                InvalidateRect(hwnd, &btnPtr->rect, TRUE);
6584
    }
6585
    GetClientRect(hwnd, &infoPtr->client_rect);
6586
    TOOLBAR_AutoSize(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
6587 6588
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
6589 6590


Alexandre Julliard's avatar
Alexandre Julliard committed
6591
static LRESULT
6592
TOOLBAR_StyleChanged (HWND hwnd, INT nType, const STYLESTRUCT *lpStyle)
Alexandre Julliard's avatar
Alexandre Julliard committed
6593
{
6594 6595
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);

6596 6597
    if (nType == GWL_STYLE)
    {
6598 6599
        DWORD dwOldStyle = infoPtr->dwStyle;

6600 6601 6602 6603 6604 6605
        if (lpStyle->styleNew & TBSTYLE_LIST)
            infoPtr->dwDTFlags = DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS;
        else
            infoPtr->dwDTFlags = DT_CENTER | DT_END_ELLIPSIS;

        TOOLBAR_CheckStyle (hwnd, lpStyle->styleNew);
6606

6607
        TRACE("new style 0x%08x\n", lpStyle->styleNew);
6608

6609 6610
        infoPtr->dwStyle = lpStyle->styleNew;

6611 6612 6613
        if ((dwOldStyle ^ lpStyle->styleNew) & (TBSTYLE_WRAPABLE | CCS_VERT))
            TOOLBAR_LayoutToolbar(hwnd);

6614
        /* only resize if one of the CCS_* styles was changed */
6615
        if ((dwOldStyle ^ lpStyle->styleNew) & COMMON_STYLES)
6616 6617 6618 6619 6620
        {
            TOOLBAR_AutoSize (hwnd);
    
            InvalidateRect(hwnd, NULL, TRUE);
        }
6621 6622
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
6623 6624
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
6625 6626


6627 6628 6629 6630 6631 6632 6633 6634 6635
static LRESULT
TOOLBAR_SysColorChange (HWND hwnd)
{
    COMCTL32_RefreshSysColors();

    return 0;
}


6636 6637 6638 6639 6640 6641 6642 6643 6644
/* update theme after a WM_THEMECHANGED message */
static LRESULT theme_changed (HWND hwnd)
{
    HTHEME theme = GetWindowTheme (hwnd);
    CloseThemeData (theme);
    OpenThemeData (hwnd, themeClass);
    return 0;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
6645

6646
static LRESULT WINAPI
6647
ToolbarWindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
6648
{
6649 6650
    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);

6651
    TRACE("hwnd=%p msg=%x wparam=%lx lparam=%lx\n",
6652 6653
	  hwnd, uMsg, /* SPY_GetMsgName(uMsg), */ wParam, lParam);

6654
    if (!infoPtr && (uMsg != WM_NCCREATE))
6655
	return DefWindowProcW( hwnd, uMsg, wParam, lParam );
6656

Alexandre Julliard's avatar
Alexandre Julliard committed
6657 6658 6659
    switch (uMsg)
    {
	case TB_ADDBITMAP:
6660
	    return TOOLBAR_AddBitmap (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6661

6662
	case TB_ADDBUTTONSA:
6663
	    return TOOLBAR_AddButtonsT(hwnd, wParam, lParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
6664

6665
	case TB_ADDBUTTONSW:
6666
	    return TOOLBAR_AddButtonsT(hwnd, wParam, lParam, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
6667

6668
	case TB_ADDSTRINGA:
6669
	    return TOOLBAR_AddStringA (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6670

6671
	case TB_ADDSTRINGW:
6672
	    return TOOLBAR_AddStringW (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6673 6674

	case TB_AUTOSIZE:
6675
	    return TOOLBAR_AutoSize (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
6676 6677

	case TB_BUTTONCOUNT:
6678
	    return TOOLBAR_ButtonCount (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6679 6680

	case TB_BUTTONSTRUCTSIZE:
6681
	    return TOOLBAR_ButtonStructSize (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6682 6683

	case TB_CHANGEBITMAP:
6684
	    return TOOLBAR_ChangeBitmap (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6685

Alexandre Julliard's avatar
Alexandre Julliard committed
6686
	case TB_CHECKBUTTON:
6687
	    return TOOLBAR_CheckButton (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6688 6689

	case TB_COMMANDTOINDEX:
6690
	    return TOOLBAR_CommandToIndex (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6691

Alexandre Julliard's avatar
Alexandre Julliard committed
6692
	case TB_CUSTOMIZE:
6693
	    return TOOLBAR_Customize (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
6694 6695

	case TB_DELETEBUTTON:
6696
	    return TOOLBAR_DeleteButton (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6697 6698

	case TB_ENABLEBUTTON:
6699
	    return TOOLBAR_EnableButton (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6700

6701 6702
	case TB_GETANCHORHIGHLIGHT:
	    return TOOLBAR_GetAnchorHighlight (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
6703 6704

	case TB_GETBITMAP:
6705
	    return TOOLBAR_GetBitmap (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6706

Alexandre Julliard's avatar
Alexandre Julliard committed
6707
	case TB_GETBITMAPFLAGS:
6708
	    return TOOLBAR_GetBitmapFlags (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6709 6710

	case TB_GETBUTTON:
6711
	    return TOOLBAR_GetButton (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6712

6713
	case TB_GETBUTTONINFOA:
6714
	    return TOOLBAR_GetButtonInfoT(hwnd, wParam, lParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
6715

6716
	case TB_GETBUTTONINFOW:
6717
	    return TOOLBAR_GetButtonInfoT(hwnd, wParam, lParam, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
6718 6719

	case TB_GETBUTTONSIZE:
6720
	    return TOOLBAR_GetButtonSize (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
6721

6722
	case TB_GETBUTTONTEXTA:
6723
	    return TOOLBAR_GetButtonTextA (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6724

6725 6726 6727
	case TB_GETBUTTONTEXTW:
	    return TOOLBAR_GetButtonTextW (hwnd, wParam, lParam);

Alexandre Julliard's avatar
Alexandre Julliard committed
6728
	case TB_GETDISABLEDIMAGELIST:
6729
	    return TOOLBAR_GetDisabledImageList (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6730

Alexandre Julliard's avatar
Alexandre Julliard committed
6731
	case TB_GETEXTENDEDSTYLE:
6732
	    return TOOLBAR_GetExtendedStyle (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
6733 6734

	case TB_GETHOTIMAGELIST:
6735
	    return TOOLBAR_GetHotImageList (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6736

6737 6738
	case TB_GETHOTITEM:
	    return TOOLBAR_GetHotItem (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
6739 6740

	case TB_GETIMAGELIST:
6741
	    return TOOLBAR_GetDefImageList (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6742

6743 6744 6745 6746 6747
	case TB_GETINSERTMARK:
	    return TOOLBAR_GetInsertMark (hwnd, wParam, lParam);

	case TB_GETINSERTMARKCOLOR:
	    return TOOLBAR_GetInsertMarkColor (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6748 6749

	case TB_GETITEMRECT:
6750
	    return TOOLBAR_GetItemRect (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6751

Alexandre Julliard's avatar
Alexandre Julliard committed
6752
	case TB_GETMAXSIZE:
6753
	    return TOOLBAR_GetMaxSize (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6754

6755
/*	case TB_GETOBJECT:			*/ /* 4.71 */
6756 6757 6758

	case TB_GETPADDING:
	    return TOOLBAR_GetPadding (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
6759 6760

	case TB_GETRECT:
6761
	    return TOOLBAR_GetRect (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6762 6763

	case TB_GETROWS:
6764
	    return TOOLBAR_GetRows (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6765 6766

	case TB_GETSTATE:
6767
	    return TOOLBAR_GetState (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6768

Robert Shearman's avatar
Robert Shearman committed
6769 6770 6771 6772 6773 6774
	case TB_GETSTRINGA:
        return TOOLBAR_GetStringA (hwnd, wParam, lParam);

	case TB_GETSTRINGW:
	    return TOOLBAR_GetStringW (hwnd, wParam, lParam);

Alexandre Julliard's avatar
Alexandre Julliard committed
6775
	case TB_GETSTYLE:
6776
	    return TOOLBAR_GetStyle (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6777

Alexandre Julliard's avatar
Alexandre Julliard committed
6778
	case TB_GETTEXTROWS:
6779
	    return TOOLBAR_GetTextRows (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6780 6781

	case TB_GETTOOLTIPS:
6782
	    return TOOLBAR_GetToolTips (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6783

Alexandre Julliard's avatar
Alexandre Julliard committed
6784
	case TB_GETUNICODEFORMAT:
6785
	    return TOOLBAR_GetUnicodeFormat (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6786 6787

	case TB_HIDEBUTTON:
6788
	    return TOOLBAR_HideButton (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6789 6790

	case TB_HITTEST:
6791
	    return TOOLBAR_HitTest (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6792

Alexandre Julliard's avatar
Alexandre Julliard committed
6793
	case TB_INDETERMINATE:
6794
	    return TOOLBAR_Indeterminate (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6795

6796
	case TB_INSERTBUTTONA:
6797
	    return TOOLBAR_InsertButtonT(hwnd, wParam, lParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
6798

6799
	case TB_INSERTBUTTONW:
6800
	    return TOOLBAR_InsertButtonT(hwnd, wParam, lParam, TRUE);
6801

6802
/*	case TB_INSERTMARKHITTEST:		*/ /* 4.71 */
Alexandre Julliard's avatar
Alexandre Julliard committed
6803 6804

	case TB_ISBUTTONCHECKED:
6805
	    return TOOLBAR_IsButtonChecked (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6806 6807

	case TB_ISBUTTONENABLED:
6808
	    return TOOLBAR_IsButtonEnabled (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6809 6810

	case TB_ISBUTTONHIDDEN:
6811
	    return TOOLBAR_IsButtonHidden (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6812 6813

	case TB_ISBUTTONHIGHLIGHTED:
6814
	    return TOOLBAR_IsButtonHighlighted (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6815 6816

	case TB_ISBUTTONINDETERMINATE:
6817
	    return TOOLBAR_IsButtonIndeterminate (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6818 6819

	case TB_ISBUTTONPRESSED:
6820
	    return TOOLBAR_IsButtonPressed (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6821

6822 6823 6824 6825 6826 6827 6828 6829 6830
	case TB_LOADIMAGES:
	    return TOOLBAR_LoadImages (hwnd, wParam, lParam);

	case TB_MAPACCELERATORA:
	case TB_MAPACCELERATORW:
	    return TOOLBAR_MapAccelerator (hwnd, wParam, lParam);

	case TB_MARKBUTTON:
	    return TOOLBAR_MarkButton (hwnd, wParam, lParam);
6831

Robert Shearman's avatar
Robert Shearman committed
6832 6833
	case TB_MOVEBUTTON:
	    return TOOLBAR_MoveButton (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6834 6835

	case TB_PRESSBUTTON:
6836
	    return TOOLBAR_PressButton (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6837

6838 6839
	case TB_REPLACEBITMAP:
            return TOOLBAR_ReplaceBitmap (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6840

6841
	case TB_SAVERESTOREA:
6842
	    return TOOLBAR_SaveRestoreA (hwnd, wParam, (LPTBSAVEPARAMSA)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6843

6844
	case TB_SAVERESTOREW:
6845
	    return TOOLBAR_SaveRestoreW (hwnd, wParam, (LPTBSAVEPARAMSW)lParam);
6846 6847 6848

	case TB_SETANCHORHIGHLIGHT:
	    return TOOLBAR_SetAnchorHighlight (hwnd, wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6849 6850

	case TB_SETBITMAPSIZE:
6851
	    return TOOLBAR_SetBitmapSize (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6852

6853
	case TB_SETBUTTONINFOA:
6854
	    return TOOLBAR_SetButtonInfoA (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6855

6856 6857
	case TB_SETBUTTONINFOW:
	    return TOOLBAR_SetButtonInfoW (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6858 6859

	case TB_SETBUTTONSIZE:
6860
	    return TOOLBAR_SetButtonSize (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6861

Alexandre Julliard's avatar
Alexandre Julliard committed
6862
	case TB_SETBUTTONWIDTH:
6863
	    return TOOLBAR_SetButtonWidth (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6864 6865

	case TB_SETCMDID:
6866
	    return TOOLBAR_SetCmdId (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6867

Alexandre Julliard's avatar
Alexandre Julliard committed
6868
	case TB_SETDISABLEDIMAGELIST:
6869
	    return TOOLBAR_SetDisabledImageList (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6870

Alexandre Julliard's avatar
Alexandre Julliard committed
6871
	case TB_SETDRAWTEXTFLAGS:
6872
	    return TOOLBAR_SetDrawTextFlags (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6873 6874

	case TB_SETEXTENDEDSTYLE:
6875
	    return TOOLBAR_SetExtendedStyle (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6876 6877

	case TB_SETHOTIMAGELIST:
6878
	    return TOOLBAR_SetHotImageList (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6879

6880 6881
	case TB_SETHOTITEM:
	    return TOOLBAR_SetHotItem (hwnd, wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6882 6883

	case TB_SETIMAGELIST:
6884
	    return TOOLBAR_SetImageList (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6885 6886

	case TB_SETINDENT:
6887
	    return TOOLBAR_SetIndent (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6888

6889 6890
	case TB_SETINSERTMARK:
	    return TOOLBAR_SetInsertMark (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6891 6892

	case TB_SETINSERTMARKCOLOR:
6893
	    return TOOLBAR_SetInsertMarkColor (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6894

Alexandre Julliard's avatar
Alexandre Julliard committed
6895
	case TB_SETMAXTEXTROWS:
6896
	    return TOOLBAR_SetMaxTextRows (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6897

6898 6899
	case TB_SETPADDING:
	    return TOOLBAR_SetPadding (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6900 6901

	case TB_SETPARENT:
6902
	    return TOOLBAR_SetParent (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6903 6904

	case TB_SETROWS:
6905
	    return TOOLBAR_SetRows (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6906 6907

	case TB_SETSTATE:
6908
	    return TOOLBAR_SetState (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6909 6910

	case TB_SETSTYLE:
6911
	    return TOOLBAR_SetStyle (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6912

Alexandre Julliard's avatar
Alexandre Julliard committed
6913
	case TB_SETTOOLTIPS:
6914
	    return TOOLBAR_SetToolTips (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6915

Alexandre Julliard's avatar
Alexandre Julliard committed
6916
	case TB_SETUNICODEFORMAT:
6917
	    return TOOLBAR_SetUnicodeFormat (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6918

6919 6920 6921
	case TB_UNKWN45D:
	    return TOOLBAR_Unkwn45D(hwnd, wParam, lParam);

6922 6923 6924
	case TB_UNKWN45E:
	    return TOOLBAR_Unkwn45E (hwnd, wParam, lParam);

6925 6926 6927
	case TB_UNKWN460:
	    return TOOLBAR_Unkwn460(hwnd, wParam, lParam);

Robert Shearman's avatar
Robert Shearman committed
6928 6929 6930
	case TB_UNKWN462:
	    return TOOLBAR_Unkwn462(hwnd, wParam, lParam);

6931 6932 6933
	case TB_UNKWN463:
	    return TOOLBAR_Unkwn463 (hwnd, wParam, lParam);

6934 6935
	case TB_UNKWN464:
	    return TOOLBAR_Unkwn464(hwnd, wParam, lParam);
6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949

/* Common Control Messages */

/*	case TB_GETCOLORSCHEME:			*/ /* identical to CCM_ */
	case CCM_GETCOLORSCHEME:
	    return TOOLBAR_GetColorScheme (hwnd, (LPCOLORSCHEME)lParam);

/*	case TB_SETCOLORSCHEME:			*/ /* identical to CCM_ */
	case CCM_SETCOLORSCHEME:
	    return TOOLBAR_SetColorScheme (hwnd, (LPCOLORSCHEME)lParam);

	case CCM_GETVERSION:
	    return TOOLBAR_GetVersion (hwnd);

6950 6951 6952
	case CCM_SETVERSION:
	    return TOOLBAR_SetVersion (hwnd, (INT)wParam);

Alexandre Julliard's avatar
Alexandre Julliard committed
6953

6954
/*	case WM_CHAR: */
Alexandre Julliard's avatar
Alexandre Julliard committed
6955

Alexandre Julliard's avatar
Alexandre Julliard committed
6956
	case WM_CREATE:
6957
	    return TOOLBAR_Create (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6958

6959 6960 6961
	case WM_DESTROY:
	  return TOOLBAR_Destroy (hwnd, wParam, lParam);

Alexandre Julliard's avatar
Alexandre Julliard committed
6962
	case WM_ERASEBKGND:
6963
	    return TOOLBAR_EraseBackground (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6964

Eric Pouech's avatar
Eric Pouech committed
6965 6966 6967
	case WM_GETFONT:
		return TOOLBAR_GetFont (hwnd, wParam, lParam);

6968 6969 6970
	case WM_KEYDOWN:
	    return TOOLBAR_KeyDown (hwnd, wParam, lParam);

6971
/*	case WM_KILLFOCUS: */
Alexandre Julliard's avatar
Alexandre Julliard committed
6972

Alexandre Julliard's avatar
Alexandre Julliard committed
6973
	case WM_LBUTTONDBLCLK:
6974
	    return TOOLBAR_LButtonDblClk (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6975 6976

	case WM_LBUTTONDOWN:
6977
	    return TOOLBAR_LButtonDown (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6978 6979

	case WM_LBUTTONUP:
6980
	    return TOOLBAR_LButtonUp (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6981

6982 6983 6984
	case WM_RBUTTONUP:
	    return TOOLBAR_RButtonUp (hwnd, wParam, lParam);

6985 6986 6987
	case WM_RBUTTONDBLCLK:
	    return TOOLBAR_RButtonDblClk (hwnd, wParam, lParam);

Alexandre Julliard's avatar
Alexandre Julliard committed
6988
	case WM_MOUSEMOVE:
6989
	    return TOOLBAR_MouseMove (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6990

6991
	case WM_MOUSELEAVE:
6992
	    return TOOLBAR_MouseLeave (hwnd, wParam, lParam);
6993

6994
	case WM_CAPTURECHANGED:
6995
	    return TOOLBAR_CaptureChanged(hwnd);
6996

Alexandre Julliard's avatar
Alexandre Julliard committed
6997
	case WM_NCACTIVATE:
6998
	    return TOOLBAR_NCActivate (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
6999 7000

	case WM_NCCALCSIZE:
7001
	    return TOOLBAR_NCCalcSize (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
7002

7003 7004 7005
	case WM_NCCREATE:
	    return TOOLBAR_NCCreate (hwnd, wParam, lParam);

Alexandre Julliard's avatar
Alexandre Julliard committed
7006
	case WM_NCPAINT:
7007
	    return TOOLBAR_NCPaint (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
7008

Alexandre Julliard's avatar
Alexandre Julliard committed
7009
	case WM_NOTIFY:
7010
	    return TOOLBAR_Notify (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
7011

7012
	case WM_NOTIFYFORMAT:
7013
	    return TOOLBAR_NotifyFormat (infoPtr, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
7014

7015
	case WM_PRINTCLIENT:
Alexandre Julliard's avatar
Alexandre Julliard committed
7016
	case WM_PAINT:
7017
	    return TOOLBAR_Paint (hwnd, wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
7018

7019 7020 7021
	case WM_SETFOCUS:
	    return TOOLBAR_SetFocus (hwnd, wParam);

7022 7023 7024
	case WM_SETFONT:
            return TOOLBAR_SetFont(hwnd, wParam, lParam);

7025 7026 7027
	case WM_SETREDRAW:
	    return TOOLBAR_SetRedraw (hwnd, wParam, lParam);

Alexandre Julliard's avatar
Alexandre Julliard committed
7028
	case WM_SIZE:
7029
	    return TOOLBAR_Size (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
7030 7031

	case WM_STYLECHANGED:
7032
	    return TOOLBAR_StyleChanged (hwnd, (INT)wParam, (LPSTYLESTRUCT)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
7033

7034 7035
	case WM_SYSCOLORCHANGE:
	    return TOOLBAR_SysColorChange (hwnd);
7036 7037 7038
            
        case WM_THEMECHANGED:
            return theme_changed (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
7039

7040
/*	case WM_WININICHANGE: */
Alexandre Julliard's avatar
Alexandre Julliard committed
7041 7042 7043 7044 7045 7046

	case WM_CHARTOITEM:
	case WM_COMMAND:
	case WM_DRAWITEM:
	case WM_MEASUREITEM:
	case WM_VKEYTOITEM:
7047
            return SendMessageW (infoPtr->hwndNotify, uMsg, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
7048

7049 7050
	/* We see this in Outlook Express 5.x and just does DefWindowProc */
        case PGM_FORWARDMOUSE:
7051
	    return DefWindowProcW (hwnd, uMsg, wParam, lParam);
7052

Alexandre Julliard's avatar
Alexandre Julliard committed
7053
	default:
7054
	    if ((uMsg >= WM_USER) && (uMsg < WM_APP))
7055
		ERR("unknown msg %04x wp=%08lx lp=%08lx\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
7056
		     uMsg, wParam, lParam);
7057
	    return DefWindowProcW (hwnd, uMsg, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
7058 7059 7060 7061
    }
}


7062
VOID
Patrik Stridvall's avatar
Patrik Stridvall committed
7063
TOOLBAR_Register (void)
Alexandre Julliard's avatar
Alexandre Julliard committed
7064
{
7065
    WNDCLASSW wndClass;
Alexandre Julliard's avatar
Alexandre Julliard committed
7066

7067
    ZeroMemory (&wndClass, sizeof(WNDCLASSW));
Alexandre Julliard's avatar
Alexandre Julliard committed
7068
    wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS;
7069
    wndClass.lpfnWndProc   = ToolbarWindowProc;
Alexandre Julliard's avatar
Alexandre Julliard committed
7070 7071
    wndClass.cbClsExtra    = 0;
    wndClass.cbWndExtra    = sizeof(TOOLBAR_INFO *);
7072 7073 7074
    wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
    wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
    wndClass.lpszClassName = TOOLBARCLASSNAMEW;
7075

7076
    RegisterClassW (&wndClass);
Alexandre Julliard's avatar
Alexandre Julliard committed
7077
}
7078 7079 7080


VOID
Patrik Stridvall's avatar
Patrik Stridvall committed
7081
TOOLBAR_Unregister (void)
7082
{
7083
    UnregisterClassW (TOOLBARCLASSNAMEW, NULL);
7084
}
7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098

static HIMAGELIST TOOLBAR_InsertImageList(PIMLENTRY **pies, INT *cies, HIMAGELIST himl, INT id)
{
    HIMAGELIST himlold;
    PIMLENTRY c = NULL;

    /* Check if the entry already exists */
    c = TOOLBAR_GetImageListEntry(*pies, *cies, id);

    /* If this is a new entry we must create it and insert into the array */
    if (!c)
    {
        PIMLENTRY *pnies;

7099
	c = (PIMLENTRY) Alloc(sizeof(IMLENTRY));
7100 7101
	c->id = id;

7102
	pnies = Alloc((*cies + 1) * sizeof(PIMLENTRY));
7103 7104 7105 7106
	memcpy(pnies, *pies, ((*cies) * sizeof(PIMLENTRY)));
	pnies[*cies] = c;
	(*cies)++;

7107
	Free(*pies);
7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122
	*pies = pnies;
    }

    himlold = c->himl;
    c->himl = himl;

    return himlold;
}


static VOID TOOLBAR_DeleteImageList(PIMLENTRY **pies, INT *cies)
{
    int i;

    for (i = 0; i < *cies; i++)
7123
	Free((*pies)[i]);
7124

7125
    Free(*pies);
7126 7127 7128 7129 7130 7131

    *cies = 0;
    *pies = NULL;
}


7132
static PIMLENTRY TOOLBAR_GetImageListEntry(const PIMLENTRY *pies, INT cies, INT id)
7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153
{
    PIMLENTRY c = NULL;

    if (pies != NULL)
    {
	int i;

        for (i = 0; i < cies; i++)
        {
            if (pies[i]->id == id)
            {
                c = pies[i];
                break;
            }
        }
    }

    return c;
}


7154
static HIMAGELIST TOOLBAR_GetImageList(const PIMLENTRY *pies, INT cies, INT id)
7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165
{
    HIMAGELIST himlDef = 0;
    PIMLENTRY pie = TOOLBAR_GetImageListEntry(pies, cies, id);

    if (pie)
        himlDef = pie->himl;

    return himlDef;
}


7166
static BOOL TOOLBAR_GetButtonInfo(const TOOLBAR_INFO *infoPtr, NMTOOLBARW *nmtb)
7167 7168
{
    if (infoPtr->bUnicode)
7169
        return TOOLBAR_SendNotify(&nmtb->hdr, infoPtr, TBN_GETBUTTONINFOW);
7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180
    else
    {
        CHAR Buffer[256];
        NMTOOLBARA nmtba;
        BOOL bRet = FALSE;

        nmtba.iItem = nmtb->iItem;
        nmtba.pszText = Buffer;
        nmtba.cchText = 256;
        ZeroMemory(nmtba.pszText, nmtba.cchText);

7181
        if (TOOLBAR_SendNotify(&nmtba.hdr, infoPtr, TBN_GETBUTTONINFOA))
7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196
        {
            int ccht = strlen(nmtba.pszText);
            if (ccht)
               MultiByteToWideChar(CP_ACP, 0, (LPCSTR)nmtba.pszText, -1, 
                  nmtb->pszText, nmtb->cchText);

            memcpy(&nmtb->tbButton, &nmtba.tbButton, sizeof(TBBUTTON));
            bRet = TRUE;
        }

        return bRet;
    }
}


7197
static BOOL TOOLBAR_IsButtonRemovable(const TOOLBAR_INFO *infoPtr, int iItem, PCUSTOMBUTTON btnInfo)
7198
{
7199
    NMTOOLBARW nmtb;
7200

7201 7202
    /* MSDN states that iItem is the index of the button, rather than the
     * command ID as used by every other NMTOOLBAR notification */
7203 7204 7205
    nmtb.iItem = iItem;
    memcpy(&nmtb.tbButton, &btnInfo->btn, sizeof(TBBUTTON));

7206
    return TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_QUERYDELETE);
7207
}