button.c 34.2 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/* File: button.c -- Button type widgets
Alexandre Julliard's avatar
Alexandre Julliard committed
2
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
3 4
 * Copyright (C) 1993 Johannes Ruscheinski
 * Copyright (C) 1993 David Metcalfe
Alexandre Julliard's avatar
Alexandre Julliard committed
5
 * Copyright (C) 1994 Alexandre Julliard
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * 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
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
 *
 * NOTES
 *
 * This code was audited for completeness against the documented features
 * of Comctl32.dll version 6.0 on Oct. 3, 2004, by Dimitrie O. Paun.
 * 
 * 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.
 * 
 * TODO
 *  Styles
 *  - BS_NOTIFY: is it complete?
 *  - BS_RIGHTBUTTON: same as BS_LEFTTEXT
 *  - BS_TYPEMASK
 *
 *  Messages
 *  - WM_CHAR: Checks a (manual or automatic) check box on '+' or '=', clears it on '-' key.
 *  - WM_SETFOCUS: For (manual or automatic) radio buttons, send the parent window BN_CLICKED
 *  - WM_NCCREATE: Turns any BS_OWNERDRAW button into a BS_PUSHBUTTON button.
 *  - WM_SYSKEYUP
 *  - BCM_GETIDEALSIZE
 *  - BCM_GETIMAGELIST
 *  - BCM_GETTEXTMARGIN
 *  - BCM_SETIMAGELIST
 *  - BCM_SETTEXTMARGIN
 *  
 *  Notifications
 *  - BCN_HOTITEMCHANGE
 *  - BN_DISABLE
 *  - BN_PUSHED/BN_HILITE
51
 *  + BN_KILLFOCUS: is it OK?
52
 *  - BN_PAINT
53
 *  + BN_SETFOCUS: is it OK?
54 55 56 57 58 59 60 61 62 63 64
 *  - BN_UNPUSHED/BN_UNHILITE
 *  - NM_CUSTOMDRAW
 *
 *  Structures/Macros/Definitions
 *  - BUTTON_IMAGELIST
 *  - NMBCHOTITEM
 *  - Button_GetIdealSize
 *  - Button_GetImageList
 *  - Button_GetTextMargin
 *  - Button_SetImageList
 *  - Button_SetTextMargin
Alexandre Julliard's avatar
Alexandre Julliard committed
65
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
66

67
#include <stdarg.h>
68
#include <string.h>
69 70
#include <stdlib.h>

71 72
#define OEMRESOURCE

73
#include "windef.h"
74
#include "winbase.h"
75
#include "wingdi.h"
76
#include "wine/winuser16.h"
77
#include "controls.h"
78
#include "user_private.h"
79 80 81
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(button);
Alexandre Julliard's avatar
Alexandre Julliard committed
82

83 84 85
/* GetWindowLong offsets for window extra information */
#define STATE_GWL_OFFSET  0
#define HFONT_GWL_OFFSET  (sizeof(LONG))
86 87
#define HIMAGE_GWL_OFFSET (HFONT_GWL_OFFSET+sizeof(HFONT))
#define NB_EXTRA_BYTES    (HIMAGE_GWL_OFFSET+sizeof(HANDLE))
88 89 90 91 92 93 94 95 96 97 98 99 100

  /* Button state values */
#define BUTTON_UNCHECKED       0x00
#define BUTTON_CHECKED         0x01
#define BUTTON_3STATE          0x02
#define BUTTON_HIGHLIGHTED     0x04
#define BUTTON_HASFOCUS        0x08
#define BUTTON_NSTATES         0x0F
  /* undocumented flags */
#define BUTTON_BTNPRESSED      0x40
#define BUTTON_UNKNOWN2        0x20
#define BUTTON_UNKNOWN3        0x10

101 102 103 104 105 106 107 108
#define BUTTON_NOTIFY_PARENT(hWnd, code) \
    do { /* Notify parent which has created this button control */ \
        TRACE("notification " #code " sent to hwnd=%p\n", GetParent(hWnd)); \
        SendMessageW(GetParent(hWnd), WM_COMMAND, \
                     MAKEWPARAM(GetWindowLongPtrW((hWnd),GWLP_ID), (code)), \
                     (LPARAM)(hWnd)); \
    } while(0)

109
static UINT BUTTON_CalcLabelRect( HWND hwnd, HDC hdc, RECT *rc );
110 111 112 113 114 115
static void PB_Paint( HWND hwnd, HDC hDC, UINT action );
static void CB_Paint( HWND hwnd, HDC hDC, UINT action );
static void GB_Paint( HWND hwnd, HDC hDC, UINT action );
static void UB_Paint( HWND hwnd, HDC hDC, UINT action );
static void OB_Paint( HWND hwnd, HDC hDC, UINT action );
static void BUTTON_CheckAutoRadioButton( HWND hwnd );
116 117
static LRESULT WINAPI ButtonWndProcA( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
static LRESULT WINAPI ButtonWndProcW( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
118

Alexandre Julliard's avatar
Alexandre Julliard committed
119
#define MAX_BTN_TYPE  12
Alexandre Julliard's avatar
Alexandre Julliard committed
120

Alexandre Julliard's avatar
Alexandre Julliard committed
121
static const WORD maxCheckState[MAX_BTN_TYPE] =
Alexandre Julliard's avatar
Alexandre Julliard committed
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
{
    BUTTON_UNCHECKED,   /* BS_PUSHBUTTON */
    BUTTON_UNCHECKED,   /* BS_DEFPUSHBUTTON */
    BUTTON_CHECKED,     /* BS_CHECKBOX */
    BUTTON_CHECKED,     /* BS_AUTOCHECKBOX */
    BUTTON_CHECKED,     /* BS_RADIOBUTTON */
    BUTTON_3STATE,      /* BS_3STATE */
    BUTTON_3STATE,      /* BS_AUTO3STATE */
    BUTTON_UNCHECKED,   /* BS_GROUPBOX */
    BUTTON_UNCHECKED,   /* BS_USERBUTTON */
    BUTTON_CHECKED,     /* BS_AUTORADIOBUTTON */
    BUTTON_UNCHECKED,   /* Not defined */
    BUTTON_UNCHECKED    /* BS_OWNERDRAW */
};

137
typedef void (*pfPaint)( HWND hwnd, HDC hdc, UINT action );
Alexandre Julliard's avatar
Alexandre Julliard committed
138

Alexandre Julliard's avatar
Alexandre Julliard committed
139
static const pfPaint btnPaintFunc[MAX_BTN_TYPE] =
Alexandre Julliard's avatar
Alexandre Julliard committed
140 141 142 143 144 145 146 147 148 149 150 151 152
{
    PB_Paint,    /* BS_PUSHBUTTON */
    PB_Paint,    /* BS_DEFPUSHBUTTON */
    CB_Paint,    /* BS_CHECKBOX */
    CB_Paint,    /* BS_AUTOCHECKBOX */
    CB_Paint,    /* BS_RADIOBUTTON */
    CB_Paint,    /* BS_3STATE */
    CB_Paint,    /* BS_AUTO3STATE */
    GB_Paint,    /* BS_GROUPBOX */
    UB_Paint,    /* BS_USERBUTTON */
    CB_Paint,    /* BS_AUTORADIOBUTTON */
    NULL,        /* Not defined */
    OB_Paint     /* BS_OWNERDRAW */
Alexandre Julliard's avatar
Alexandre Julliard committed
153 154
};

155
static HBITMAP hbitmapCheckBoxes = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
156 157
static WORD checkBoxWidth = 0, checkBoxHeight = 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
158

159 160 161 162 163 164
/*********************************************************************
 * button class descriptor
 */
const struct builtin_class_descr BUTTON_builtin_class =
{
    "Button",            /* name */
165
    CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC, /* style  */
166 167
    ButtonWndProcA,      /* procA */
    ButtonWndProcW,      /* procW */
168
    NB_EXTRA_BYTES,      /* extra */
169
    IDC_ARROW,           /* cursor */
170 171 172 173
    0                    /* brush */
};


174 175
inline static LONG get_button_state( HWND hwnd )
{
176
    return GetWindowLongW( hwnd, STATE_GWL_OFFSET );
177 178 179 180
}

inline static void set_button_state( HWND hwnd, LONG state )
{
181
    SetWindowLongW( hwnd, STATE_GWL_OFFSET, state );
182 183 184 185
}

inline static HFONT get_button_font( HWND hwnd )
{
186
    return (HFONT)GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET );
187 188 189 190
}

inline static void set_button_font( HWND hwnd, HFONT font )
{
191
    SetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET, (LONG_PTR)font );
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
}

inline static UINT get_button_type( LONG window_style )
{
    return (window_style & 0x0f);
}

/* paint a button of any type */
inline static void paint_button( HWND hwnd, LONG style, UINT action )
{
    if (btnPaintFunc[style] && IsWindowVisible(hwnd))
    {
        HDC hdc = GetDC( hwnd );
        btnPaintFunc[style]( hwnd, hdc, action );
        ReleaseDC( hwnd, hdc );
    }
}

/* retrieve the button text; returned buffer must be freed by caller */
inline static WCHAR *get_button_text( HWND hwnd )
{
213
    INT len = 512;
214
    WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
215
    if (buffer) InternalGetWindowText( hwnd, buffer, len + 1 );
216 217 218
    return buffer;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
219
/***********************************************************************
220
 *           ButtonWndProc_common
Alexandre Julliard's avatar
Alexandre Julliard committed
221
 */
222 223
static LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
                                           WPARAM wParam, LPARAM lParam, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
224
{
225
    RECT rect;
226
    POINT pt;
227
    LONG style = GetWindowLongW( hWnd, GWL_STYLE );
228 229
    UINT btn_type = get_button_type( style );
    LONG state;
230
    HANDLE oldHbitmap;
Alexandre Julliard's avatar
Alexandre Julliard committed
231

232 233
    pt.x = (short)LOWORD(lParam);
    pt.y = (short)HIWORD(lParam);
234

Alexandre Julliard's avatar
Alexandre Julliard committed
235 236 237
    switch (uMsg)
    {
    case WM_GETDLGCODE:
238
        switch(btn_type)
Alexandre Julliard's avatar
Alexandre Julliard committed
239
        {
240 241 242 243 244
        case BS_USERBUTTON:
        case BS_PUSHBUTTON:      return DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON;
        case BS_DEFPUSHBUTTON:   return DLGC_BUTTON | DLGC_DEFPUSHBUTTON;
        case BS_RADIOBUTTON:
        case BS_AUTORADIOBUTTON: return DLGC_BUTTON | DLGC_RADIOBUTTON;
245
        case BS_GROUPBOX:        return DLGC_STATIC;
Alexandre Julliard's avatar
Alexandre Julliard committed
246 247
        default:                 return DLGC_BUTTON;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
248

Alexandre Julliard's avatar
Alexandre Julliard committed
249
    case WM_ENABLE:
250
        paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
Alexandre Julliard's avatar
Alexandre Julliard committed
251
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
252

Alexandre Julliard's avatar
Alexandre Julliard committed
253 254 255
    case WM_CREATE:
        if (!hbitmapCheckBoxes)
        {
256
            BITMAP bmp;
257 258
            hbitmapCheckBoxes = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CHECKBOXES));
            GetObjectW( hbitmapCheckBoxes, sizeof(bmp), &bmp );
Alexandre Julliard's avatar
Alexandre Julliard committed
259 260 261
            checkBoxWidth  = bmp.bmWidth / 4;
            checkBoxHeight = bmp.bmHeight / 3;
        }
262
        if (btn_type >= MAX_BTN_TYPE)
263
            return -1; /* abort */
264
        set_button_state( hWnd, BUTTON_UNCHECKED );
Alexandre Julliard's avatar
Alexandre Julliard committed
265
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
266

Alexandre Julliard's avatar
Alexandre Julliard committed
267
    case WM_ERASEBKGND:
268 269 270 271
        if (btn_type == BS_OWNERDRAW)
        {
            HDC hdc = (HDC)wParam;
            RECT rc;
272 273 274 275
            HBRUSH hBrush;
            HWND parent = GetParent(hWnd);
            if (!parent) parent = hWnd;
            hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)hWnd);
276
            if (!hBrush) /* did the app forget to call defwindowproc ? */
277
                hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN,
278 279 280 281
                                                (WPARAM)hdc, (LPARAM)hWnd);
            GetClientRect(hWnd, &rc);
            FillRect(hdc, &rc, hBrush);
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
282
        return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
283

284
    case WM_PRINTCLIENT:
Alexandre Julliard's avatar
Alexandre Julliard committed
285
    case WM_PAINT:
286
        if (btnPaintFunc[btn_type])
Alexandre Julliard's avatar
Alexandre Julliard committed
287
        {
288 289
            PAINTSTRUCT ps;
            HDC hdc = wParam ? (HDC)wParam : BeginPaint( hWnd, &ps );
290
            int nOldMode = SetBkMode( hdc, OPAQUE );
291
            (btnPaintFunc[btn_type])( hWnd, hdc, ODA_DRAWENTIRE );
292
            SetBkMode(hdc, nOldMode); /*  reset painting mode */
293
            if( !wParam ) EndPaint( hWnd, &ps );
Alexandre Julliard's avatar
Alexandre Julliard committed
294 295 296
        }
        break;

297 298 299
    case WM_KEYDOWN:
	if (wParam == VK_SPACE)
	{
300
	    SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
301
            set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
302 303
	}
	break;
304

305
    case WM_LBUTTONDBLCLK:
306 307 308 309 310
        if(style & BS_NOTIFY ||
           btn_type == BS_RADIOBUTTON ||
           btn_type == BS_USERBUTTON ||
           btn_type == BS_OWNERDRAW)
        {
311
            BUTTON_NOTIFY_PARENT(hWnd, BN_DOUBLECLICKED);
312 313 314
            break;
        }
        /* fall through */
315
    case WM_LBUTTONDOWN:
316
        SetCapture( hWnd );
317
        SetFocus( hWnd );
318
        set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
319
        SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
320 321
        break;

322 323 324 325
    case WM_KEYUP:
	if (wParam != VK_SPACE)
	    break;
	/* fall through */
Alexandre Julliard's avatar
Alexandre Julliard committed
326
    case WM_LBUTTONUP:
327 328 329 330 331 332
        state = get_button_state( hWnd );
        if (!(state & BUTTON_BTNPRESSED)) break;
        state &= BUTTON_NSTATES;
        set_button_state( hWnd, state );
        if (!(state & BUTTON_HIGHLIGHTED))
        {
333 334 335
            ReleaseCapture();
            break;
        }
336
        SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
337
        ReleaseCapture();
338
        GetClientRect( hWnd, &rect );
339
	if (uMsg == WM_KEYUP || PtInRect( &rect, pt ))
Alexandre Julliard's avatar
Alexandre Julliard committed
340
        {
341 342
            state = get_button_state( hWnd );
            switch(btn_type)
Alexandre Julliard's avatar
Alexandre Julliard committed
343 344
            {
            case BS_AUTOCHECKBOX:
345
                SendMessageW( hWnd, BM_SETCHECK, !(state & BUTTON_CHECKED), 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
346
                break;
Alexandre Julliard's avatar
Alexandre Julliard committed
347
            case BS_AUTORADIOBUTTON:
348
                SendMessageW( hWnd, BM_SETCHECK, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
349 350
                break;
            case BS_AUTO3STATE:
351
                SendMessageW( hWnd, BM_SETCHECK,
352
                                (state & BUTTON_3STATE) ? 0 : ((state & 3) + 1), 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
353
                break;
Alexandre Julliard's avatar
Alexandre Julliard committed
354
            }
355
            BUTTON_NOTIFY_PARENT(hWnd, BN_CLICKED);
Alexandre Julliard's avatar
Alexandre Julliard committed
356 357 358
        }
        break;

359
    case WM_CAPTURECHANGED:
360
        TRACE("WM_CAPTURECHANGED %p\n", hWnd);
361 362 363 364 365 366
        state = get_button_state( hWnd );
        if (state & BUTTON_BTNPRESSED)
        {
            state &= BUTTON_NSTATES;
            set_button_state( hWnd, state );
            if (state & BUTTON_HIGHLIGHTED) SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
367 368 369
        }
        break;

Alexandre Julliard's avatar
Alexandre Julliard committed
370
    case WM_MOUSEMOVE:
371
        if ((wParam & MK_LBUTTON) && GetCapture() == hWnd)
Alexandre Julliard's avatar
Alexandre Julliard committed
372
        {
373
            GetClientRect( hWnd, &rect );
374
            SendMessageW( hWnd, BM_SETSTATE, PtInRect(&rect, pt), 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
375 376 377 378
        }
        break;

    case WM_SETTEXT:
379 380 381 382 383
    {
        /* Clear an old text here as Windows does */
        HDC hdc = GetDC(hWnd);
        HBRUSH hbrush;
        RECT client, rc;
384
        HWND parent = GetParent(hWnd);
385

386 387
        if (!parent) parent = hWnd;
        hbrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC,
388
				      (WPARAM)hdc, (LPARAM)hWnd);
389
        if (!hbrush) /* did the app forget to call DefWindowProc ? */
390
            hbrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC,
391
					    (WPARAM)hdc, (LPARAM)hWnd);
392 393 394 395 396 397 398 399 400 401

        GetClientRect(hWnd, &client);
        rc = client;
        BUTTON_CalcLabelRect(hWnd, hdc, &rc);
        /* Clip by client rect bounds */
        if (rc.right > client.right) rc.right = client.right;
        if (rc.bottom > client.bottom) rc.bottom = client.bottom;
        FillRect(hdc, &rc, hbrush);
        ReleaseDC(hWnd, hdc);

402 403
        if (unicode) DefWindowProcW( hWnd, WM_SETTEXT, wParam, lParam );
        else DefWindowProcA( hWnd, WM_SETTEXT, wParam, lParam );
404 405 406 407
        if (btn_type == BS_GROUPBOX) /* Yes, only for BS_GROUPBOX */
            InvalidateRect( hWnd, NULL, TRUE );
        else
            paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
408
        return 1; /* success. FIXME: check text length */
409
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
410 411

    case WM_SETFONT:
412
        set_button_font( hWnd, (HFONT)wParam );
413
        if (lParam) paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
Alexandre Julliard's avatar
Alexandre Julliard committed
414 415 416
        break;

    case WM_GETFONT:
417
        return (LRESULT)get_button_font( hWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
418 419

    case WM_SETFOCUS:
420
        TRACE("WM_SETFOCUS %p\n",hWnd);
421 422
        set_button_state( hWnd, get_button_state(hWnd) | BUTTON_HASFOCUS );
        paint_button( hWnd, btn_type, ODA_FOCUS );
423 424
        if (style & BS_NOTIFY)
            BUTTON_NOTIFY_PARENT(hWnd, BN_SETFOCUS);
Alexandre Julliard's avatar
Alexandre Julliard committed
425 426 427
        break;

    case WM_KILLFOCUS:
428
        TRACE("WM_KILLFOCUS %p\n",hWnd);
429 430
        state = get_button_state( hWnd );
        set_button_state( hWnd, state & ~BUTTON_HASFOCUS );
431
	paint_button( hWnd, btn_type, ODA_FOCUS );
432 433 434

        if ((state & BUTTON_BTNPRESSED) && GetCapture() == hWnd)
            ReleaseCapture();
435 436 437
        if (style & BS_NOTIFY)
            BUTTON_NOTIFY_PARENT(hWnd, BN_KILLFOCUS);

Alexandre Julliard's avatar
Alexandre Julliard committed
438 439 440
        break;

    case WM_SYSCOLORCHANGE:
441
        InvalidateRect( hWnd, NULL, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
442 443 444
        break;

    case BM_SETSTYLE16:
445
    case BM_SETSTYLE:
Alexandre Julliard's avatar
Alexandre Julliard committed
446
        if ((wParam & 0x0f) >= MAX_BTN_TYPE) break;
447 448
        btn_type = wParam & 0x0f;
        style = (style & ~0x0f) | btn_type;
449
        SetWindowLongW( hWnd, GWL_STYLE, style );
450 451 452

        /* Only redraw if lParam flag is set.*/
        if (lParam)
453
           paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
454

Alexandre Julliard's avatar
Alexandre Julliard committed
455 456
        break;

457
    case BM_CLICK:
458 459
	SendMessageW( hWnd, WM_LBUTTONDOWN, 0, 0 );
	SendMessageW( hWnd, WM_LBUTTONUP, 0, 0 );
460 461
	break;

462
    case BM_SETIMAGE:
463 464 465 466 467 468 469 470 471 472 473 474
        /* Check that image format matches button style */
        switch (style & (BS_BITMAP|BS_ICON))
        {
        case BS_BITMAP:
            if (wParam != IMAGE_BITMAP) return 0;
            break;
        case BS_ICON:
            if (wParam != IMAGE_ICON) return 0;
            break;
        default:
            return 0;
        }
475
        oldHbitmap = (HBITMAP)SetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET, lParam );
476
	InvalidateRect( hWnd, NULL, FALSE );
477
	return (LRESULT)oldHbitmap;
478 479

    case BM_GETIMAGE:
480
        return GetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET );
481

Alexandre Julliard's avatar
Alexandre Julliard committed
482
    case BM_GETCHECK16:
483
    case BM_GETCHECK:
484
        return get_button_state( hWnd ) & 3;
Alexandre Julliard's avatar
Alexandre Julliard committed
485 486

    case BM_SETCHECK16:
487
    case BM_SETCHECK:
488 489
        if (wParam > maxCheckState[btn_type]) wParam = maxCheckState[btn_type];
        state = get_button_state( hWnd );
490 491 492 493
        if ((btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON))
        {
            if (wParam) style |= WS_TABSTOP;
            else style &= ~WS_TABSTOP;
494
            SetWindowLongW( hWnd, GWL_STYLE, style );
495
        }
496
        if ((state & 3) != wParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
497
        {
498 499
            set_button_state( hWnd, (state & ~3) | wParam );
            paint_button( hWnd, btn_type, ODA_SELECT );
Alexandre Julliard's avatar
Alexandre Julliard committed
500
        }
501 502
        if ((btn_type == BS_AUTORADIOBUTTON) && (wParam == BUTTON_CHECKED) && (style & WS_CHILD))
            BUTTON_CheckAutoRadioButton( hWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
503
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
504

Alexandre Julliard's avatar
Alexandre Julliard committed
505
    case BM_GETSTATE16:
506
    case BM_GETSTATE:
507
        return get_button_state( hWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
508

Alexandre Julliard's avatar
Alexandre Julliard committed
509
    case BM_SETSTATE16:
510
    case BM_SETSTATE:
511
        state = get_button_state( hWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
512 513
        if (wParam)
        {
514 515
            if (state & BUTTON_HIGHLIGHTED) break;
            set_button_state( hWnd, state | BUTTON_HIGHLIGHTED );
Alexandre Julliard's avatar
Alexandre Julliard committed
516 517 518
        }
        else
        {
519 520
            if (!(state & BUTTON_HIGHLIGHTED)) break;
            set_button_state( hWnd, state & ~BUTTON_HIGHLIGHTED );
Alexandre Julliard's avatar
Alexandre Julliard committed
521
        }
522
        paint_button( hWnd, btn_type, ODA_SELECT );
Alexandre Julliard's avatar
Alexandre Julliard committed
523 524
        break;

525
    case WM_NCHITTEST:
526
        if(btn_type == BS_GROUPBOX) return HTTRANSPARENT;
527
        /* fall through */
Alexandre Julliard's avatar
Alexandre Julliard committed
528
    default:
529 530
        return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) :
                         DefWindowProcA(hWnd, uMsg, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
531 532
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
533 534
}

535
/***********************************************************************
536
 *           ButtonWndProcW
537 538 539 540
 * The button window procedure. This is just a wrapper which locks
 * the passed HWND and calls the real window procedure (with a WND*
 * pointer pointing to the locked windowstructure).
 */
541
static LRESULT WINAPI ButtonWndProcW( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
542
{
543 544
    if (!IsWindow( hWnd )) return 0;
    return ButtonWndProc_common( hWnd, uMsg, wParam, lParam, TRUE );
545
}
Alexandre Julliard's avatar
Alexandre Julliard committed
546

547 548 549 550

/***********************************************************************
 *           ButtonWndProcA
 */
551
static LRESULT WINAPI ButtonWndProcA( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
552
{
553 554
    if (!IsWindow( hWnd )) return 0;
    return ButtonWndProc_common( hWnd, uMsg, wParam, lParam, FALSE );
555 556 557
}


558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
/**********************************************************************
 * Convert button styles to flags used by DrawText.
 * TODO: handle WS_EX_RIGHT extended style.
 */
static UINT BUTTON_BStoDT(DWORD style)
{
   UINT dtStyle = DT_NOCLIP;  /* We use SelectClipRgn to limit output */

   /* "Convert" pushlike buttons to pushbuttons */
   if (style & BS_PUSHLIKE)
      style &= ~0x0F;

   if (!(style & BS_MULTILINE))
      dtStyle |= DT_SINGLELINE;
   else
      dtStyle |= DT_WORDBREAK;

   switch (style & BS_CENTER)
   {
      case BS_LEFT:   /* DT_LEFT is 0 */    break;
      case BS_RIGHT:  dtStyle |= DT_RIGHT;  break;
      case BS_CENTER: dtStyle |= DT_CENTER; break;
      default:
         /* Pushbutton's text is centered by default */
582
         if (get_button_type(style) <= BS_DEFPUSHBUTTON) dtStyle |= DT_CENTER;
583 584 585 586 587 588
         /* all other flavours have left aligned text */
   }

   /* DrawText ignores vertical alignment for multiline text,
    * but we use these flags to align label manualy.
    */
589
   if (get_button_type(style) != BS_GROUPBOX)
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
   {
      switch (style & BS_VCENTER)
      {
         case BS_TOP:     /* DT_TOP is 0 */      break;
         case BS_BOTTOM:  dtStyle |= DT_BOTTOM;  break;
         case BS_VCENTER: /* fall through */
         default:         dtStyle |= DT_VCENTER; break;
      }
   }
   else
      /* GroupBox's text is always single line and is top aligned. */
      dtStyle |= DT_SINGLELINE;

   return dtStyle;
}

/**********************************************************************
 *       BUTTON_CalcLabelRect
 *
 *   Calculates label's rectangle depending on button style.
 *
 * Returns flags to be passed to DrawText.
 * Calculated rectangle doesn't take into account button state
 * (pushed, etc.). If there is nothing to draw (no text/image) output
 * rectangle is empty, and return value is (UINT)-1.
 */
616
static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc)
617
{
618
   LONG style = GetWindowLongW( hwnd, GWL_STYLE );
619
   WCHAR *text;
620 621
   ICONINFO    iconInfo;
   BITMAP      bm;
622
   UINT        dtStyle = BUTTON_BStoDT(style);
623 624 625 626
   RECT        r = *rc;
   INT         n;

   /* Calculate label rectangle according to label type */
627
   switch (style & (BS_ICON|BS_BITMAP))
628 629
   {
      case BS_TEXT:
630 631 632 633 634 635 636 637 638
          if (!(text = get_button_text( hwnd ))) goto empty_rect;
          if (!text[0])
          {
              HeapFree( GetProcessHeap(), 0, text );
              goto empty_rect;
          }
          DrawTextW(hdc, text, -1, &r, dtStyle | DT_CALCRECT);
          HeapFree( GetProcessHeap(), 0, text );
          break;
639 640

      case BS_ICON:
641
         if (!GetIconInfo((HICON)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), &iconInfo))
642 643
            goto empty_rect;

644
         GetObjectW (iconInfo.hbmColor, sizeof(BITMAP), &bm);
645 646 647 648 649 650 651 652 653

         r.right  = r.left + bm.bmWidth;
         r.bottom = r.top  + bm.bmHeight;

         DeleteObject(iconInfo.hbmColor);
         DeleteObject(iconInfo.hbmMask);
         break;

      case BS_BITMAP:
654
         if (!GetObjectW( (HANDLE)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), sizeof(BITMAP), &bm))
655 656 657 658 659 660 661
            goto empty_rect;

         r.right  = r.left + bm.bmWidth;
         r.bottom = r.top  + bm.bmHeight;
         break;

      default:
662
      empty_rect:
Felix Nawothnig's avatar
Felix Nawothnig committed
663 664
         rc->right = r.left;
         rc->bottom = r.top;
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
         return (UINT)(LONG)-1;
   }

   /* Position label inside bounding rectangle according to
    * alignment flags. (calculated rect is always left-top aligned).
    * If label is aligned to any side - shift label in opposite
    * direction to leave extra space for focus rectangle.
    */
   switch (dtStyle & (DT_CENTER|DT_RIGHT))
   {
      case DT_LEFT:    r.left++;  r.right++;  break;
      case DT_CENTER:  n = r.right - r.left;
                       r.left   = rc->left + ((rc->right - rc->left) - n) / 2;
                       r.right  = r.left + n; break;
      case DT_RIGHT:   n = r.right - r.left;
                       r.right  = rc->right - 1;
                       r.left   = r.right - n;
                       break;
   }

   switch (dtStyle & (DT_VCENTER|DT_BOTTOM))
   {
      case DT_TOP:     r.top++;  r.bottom++;  break;
      case DT_VCENTER: n = r.bottom - r.top;
                       r.top    = rc->top + ((rc->bottom - rc->top) - n) / 2;
                       r.bottom = r.top + n;  break;
      case DT_BOTTOM:  n = r.bottom - r.top;
                       r.bottom = rc->bottom - 1;
                       r.top    = r.bottom - n;
                       break;
   }

   *rc = r;
   return dtStyle;
}


/**********************************************************************
 *       BUTTON_DrawTextCallback
 *
705
 *   Callback function used by DrawStateW function.
706 707 708
 */
static BOOL CALLBACK BUTTON_DrawTextCallback(HDC hdc, LPARAM lp, WPARAM wp, int cx, int cy)
{
709 710 711 712 713
   RECT rc;
   rc.left = 0;
   rc.top = 0;
   rc.right = cx;
   rc.bottom = cy;
714 715 716 717 718 719 720 721 722 723 724

   DrawTextW(hdc, (LPCWSTR)lp, -1, &rc, (UINT)wp);
   return TRUE;
}


/**********************************************************************
 *       BUTTON_DrawLabel
 *
 *   Common function for drawing button label.
 */
725
static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, RECT *rc)
726 727 728 729 730
{
   DRAWSTATEPROC lpOutputProc = NULL;
   LPARAM lp;
   WPARAM wp = 0;
   HBRUSH hbr = 0;
731 732
   UINT flags = IsWindowEnabled(hwnd) ? DSS_NORMAL : DSS_DISABLED;
   LONG state = get_button_state( hwnd );
733
   LONG style = GetWindowLongW( hwnd, GWL_STYLE );
734
   WCHAR *text = NULL;
735

736
   /* FIXME: To draw disabled label in Win31 look-and-feel, we probably
737 738 739 740
    * must use DSS_MONO flag and COLOR_GRAYTEXT brush (or maybe DSS_UNION).
    * I don't have Win31 on hand to verify that, so I leave it as is.
    */

741
   if ((style & BS_PUSHLIKE) && (state & BUTTON_3STATE))
742 743 744 745 746
   {
      hbr = GetSysColorBrush(COLOR_GRAYTEXT);
      flags |= DSS_MONO;
   }

747
   switch (style & (BS_ICON|BS_BITMAP))
748 749 750 751
   {
      case BS_TEXT:
         /* DST_COMPLEX -- is 0 */
         lpOutputProc = BUTTON_DrawTextCallback;
752 753
         if (!(text = get_button_text( hwnd ))) return;
         lp = (LPARAM)text;
754 755 756 757 758
         wp = (WPARAM)dtFlags;
         break;

      case BS_ICON:
         flags |= DST_ICON;
759
         lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET );
760 761 762 763
         break;

      case BS_BITMAP:
         flags |= DST_BITMAP;
764
         lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET );
765 766 767 768 769 770 771 772
         break;

      default:
         return;
   }

   DrawStateW(hdc, hbr, lpOutputProc, lp, wp, rc->left, rc->top,
              rc->right - rc->left, rc->bottom - rc->top, flags);
773
   HeapFree( GetProcessHeap(), 0, text );
774 775
}

776
/**********************************************************************
777
 *       Push Button Functions
778
 */
779
static void PB_Paint( HWND hwnd, HDC hDC, UINT action )
Alexandre Julliard's avatar
Alexandre Julliard committed
780
{
781
    RECT     rc, focus_rect, r;
782
    UINT     dtFlags, uState;
783 784 785 786
    HPEN     hOldPen;
    HBRUSH   hOldBrush;
    INT      oldBkMode;
    COLORREF oldTxtColor;
787 788
    HFONT hFont;
    LONG state = get_button_state( hwnd );
789
    LONG style = GetWindowLongW( hwnd, GWL_STYLE );
790
    BOOL pushedState = (state & BUTTON_HIGHLIGHTED);
791
    HWND parent;
Alexandre Julliard's avatar
Alexandre Julliard committed
792

793
    GetClientRect( hwnd, &rc );
Alexandre Julliard's avatar
Alexandre Julliard committed
794

795
    /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */
796
    if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
797 798 799
    parent = GetParent(hwnd);
    if (!parent) parent = hwnd;
    SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd );
800
    hOldPen = (HPEN)SelectObject(hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME));
801
    hOldBrush =(HBRUSH)SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE));
802
    oldBkMode = SetBkMode(hDC, TRANSPARENT);
803

804
    if (get_button_type(style) == BS_DEFPUSHBUTTON)
Alexandre Julliard's avatar
Alexandre Julliard committed
805
    {
806
        Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
807
	InflateRect( &rc, -1, -1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
808 809
    }

810 811 812 813 814
    uState = DFCS_BUTTONPUSH | DFCS_ADJUSTRECT;

    if (style & BS_FLAT)
        uState |= DFCS_MONO;
    else if (pushedState)
Alexandre Julliard's avatar
Alexandre Julliard committed
815
    {
816 817 818 819
	if (get_button_type(style) == BS_DEFPUSHBUTTON )
	    uState |= DFCS_FLAT;
	else
	    uState |= DFCS_PUSHED;
820 821
    }

822 823
    if (state & (BUTTON_CHECKED | BUTTON_3STATE))
        uState |= DFCS_CHECKED;
824

825
    DrawFrameControl( hDC, &rc, DFC_BUTTON, uState );
826

827
    focus_rect = rc;
828

829 830
    /* draw button label */
    r = rc;
831
    dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &r);
832

833 834
    if (dtFlags == (UINT)-1L)
       goto cleanup;
835

836 837
    if (pushedState)
       OffsetRect(&r, 1, 1);
838

839
    IntersectClipRect(hDC, rc.left, rc.top, rc.right, rc.bottom);
840

841
    oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) );
842

843
    BUTTON_DrawLabel(hwnd, hDC, dtFlags, &r);
844

845
    SetTextColor( hDC, oldTxtColor );
846

847
    if (state & BUTTON_HASFOCUS)
848 849
    {
        InflateRect( &focus_rect, -1, -1 );
850
        IntersectRect(&focus_rect, &focus_rect, &rc);
851 852 853
        DrawFocusRect( hDC, &focus_rect );
    }

854
 cleanup:
855 856
    SelectObject( hDC, hOldPen );
    SelectObject( hDC, hOldBrush );
857
    SetBkMode(hDC, oldBkMode);
Alexandre Julliard's avatar
Alexandre Julliard committed
858 859 860
}

/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
861
 *       Check Box & Radio Button Functions
Alexandre Julliard's avatar
Alexandre Julliard committed
862
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
863

864
static void CB_Paint( HWND hwnd, HDC hDC, UINT action )
Alexandre Julliard's avatar
Alexandre Julliard committed
865
{
866 867
    RECT rbox, rtext, client;
    HBRUSH hBrush;
868 869
    int delta;
    UINT dtFlags;
870 871
    HFONT hFont;
    LONG state = get_button_state( hwnd );
872
    LONG style = GetWindowLongW( hwnd, GWL_STYLE );
873
    HWND parent;
Alexandre Julliard's avatar
Alexandre Julliard committed
874

875
    if (style & BS_PUSHLIKE)
876
    {
877
        PB_Paint( hwnd, hDC, action );
878
	return;
879 880
    }

881
    GetClientRect(hwnd, &client);
Alexandre Julliard's avatar
Alexandre Julliard committed
882
    rbox = rtext = client;
Alexandre Julliard's avatar
Alexandre Julliard committed
883

884
    if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
Alexandre Julliard's avatar
Alexandre Julliard committed
885

886 887 888
    parent = GetParent(hwnd);
    if (!parent) parent = hwnd;
    hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC,
889
				  (WPARAM)hDC, (LPARAM)hwnd);
890
    if (!hBrush) /* did the app forget to call defwindowproc ? */
891
        hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC,
892
					(WPARAM)hDC, (LPARAM)hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
893

894
    if (style & BS_LEFTTEXT)
Alexandre Julliard's avatar
Alexandre Julliard committed
895 896
    {
	/* magic +4 is what CTL3D expects */
Alexandre Julliard's avatar
Alexandre Julliard committed
897

Alexandre Julliard's avatar
Alexandre Julliard committed
898 899 900
        rtext.right -= checkBoxWidth + 4;
        rbox.left = rbox.right - checkBoxWidth;
    }
901
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
902 903 904 905
    {
        rtext.left += checkBoxWidth + 4;
        rbox.right = checkBoxWidth;
    }
906 907 908 909
 
    /* Since WM_ERASEBKGND does nothing, first prepare background */
    if (action == ODA_SELECT) FillRect( hDC, &rbox, hBrush );
    if (action == ODA_DRAWENTIRE) FillRect( hDC, &client, hBrush );
Alexandre Julliard's avatar
Alexandre Julliard committed
910

911 912 913 914
    /* Draw label */
    client = rtext;
    dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rtext);
    
915 916 917 918 919 920 921
    /* Only adjust rbox when rtext is valid */
    if (dtFlags != (UINT)-1L)
    {
	rbox.top = rtext.top;
	rbox.bottom = rtext.bottom;
    }

922
    /* Draw the check-box bitmap */
Alexandre Julliard's avatar
Alexandre Julliard committed
923
    if (action == ODA_DRAWENTIRE || action == ODA_SELECT)
924
    {
925
	UINT flags;
926

927 928 929 930
	if ((get_button_type(style) == BS_RADIOBUTTON) ||
	    (get_button_type(style) == BS_AUTORADIOBUTTON)) flags = DFCS_BUTTONRADIO;
	else if (state & BUTTON_3STATE) flags = DFCS_BUTTON3STATE;
	else flags = DFCS_BUTTONCHECK;
931

932 933
	if (state & (BUTTON_CHECKED | BUTTON_3STATE)) flags |= DFCS_CHECKED;
	if (state & BUTTON_HIGHLIGHTED) flags |= DFCS_PUSHED;
934

935
	if (style & WS_DISABLED) flags |= DFCS_INACTIVE;
936

937 938 939 940 941
	/* rbox must have the correct height */
	delta = rbox.bottom - rbox.top - checkBoxHeight;
	
	if (style & BS_TOP) {
	    if (delta > 0) {
942
		rbox.bottom = rbox.top + checkBoxHeight;
943
	    } else { 
944
		rbox.top -= -delta/2 + 1;
945
		rbox.bottom = rbox.top + checkBoxHeight;
946 947 948
	    }
	} else if (style & BS_BOTTOM) {
	    if (delta > 0) {
949
		rbox.top = rbox.bottom - checkBoxHeight;
950
	    } else {
951
		rbox.bottom += -delta/2 + 1;
952
		rbox.top = rbox.bottom - checkBoxHeight;
953
	    }
954 955 956 957 958 959 960 961 962 963 964
	} else { /* Default */
	    if (delta > 0) {
		int ofs = (delta / 2);
		rbox.bottom -= ofs + 1;
		rbox.top = rbox.bottom - checkBoxHeight;
	    } else if (delta < 0) {
		int ofs = (-delta / 2);
		rbox.top -= ofs + 1;
		rbox.bottom = rbox.top + checkBoxHeight;
	    }
	}
965

966
	DrawFrameControl( hDC, &rbox, DFC_BUTTON, flags );
Alexandre Julliard's avatar
Alexandre Julliard committed
967
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
968

969 970
    if (dtFlags == (UINT)-1L) /* Noting to draw */
	return;
971 972

    IntersectClipRect(hDC, client.left, client.top, client.right, client.bottom);
973 974

    if (action == ODA_DRAWENTIRE)
975
	BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rtext);
976 977

    /* ... and focus */
Alexandre Julliard's avatar
Alexandre Julliard committed
978
    if ((action == ODA_FOCUS) ||
979
        ((action == ODA_DRAWENTIRE) && (state & BUTTON_HASFOCUS)))
Alexandre Julliard's avatar
Alexandre Julliard committed
980
    {
981 982 983 984
	rtext.left--;
	rtext.right++;
	IntersectRect(&rtext, &rtext, &client);
	DrawFocusRect( hDC, &rtext );
Alexandre Julliard's avatar
Alexandre Julliard committed
985 986 987 988
    }
}


Alexandre Julliard's avatar
Alexandre Julliard committed
989 990 991
/**********************************************************************
 *       BUTTON_CheckAutoRadioButton
 *
992
 * hwnd is checked, uncheck every other auto radio button in group
Alexandre Julliard's avatar
Alexandre Julliard committed
993
 */
994
static void BUTTON_CheckAutoRadioButton( HWND hwnd )
Alexandre Julliard's avatar
Alexandre Julliard committed
995
{
996
    HWND parent, sibling, start;
997 998 999 1000

    parent = GetParent(hwnd);
    /* make sure that starting control is not disabled or invisible */
    start = sibling = GetNextDlgGroupItem( parent, hwnd, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1001 1002 1003
    do
    {
        if (!sibling) break;
1004
        if ((hwnd != sibling) &&
1005
            ((GetWindowLongW( sibling, GWL_STYLE) & 0x0f) == BS_AUTORADIOBUTTON))
1006
            SendMessageW( sibling, BM_SETCHECK, BUTTON_UNCHECKED, 0 );
1007
        sibling = GetNextDlgGroupItem( parent, sibling, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1008
    } while (sibling != start);
Alexandre Julliard's avatar
Alexandre Julliard committed
1009 1010 1011
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1012 1013 1014 1015
/**********************************************************************
 *       Group Box Functions
 */

1016
static void GB_Paint( HWND hwnd, HDC hDC, UINT action )
Alexandre Julliard's avatar
Alexandre Julliard committed
1017
{
1018
    RECT rc, rcFrame;
1019
    HBRUSH hbr;
1020
    HFONT hFont;
1021
    UINT dtFlags;
1022
    TEXTMETRICW tm;
1023
    LONG style = GetWindowLongW( hwnd, GWL_STYLE );
1024
    HWND parent;
Alexandre Julliard's avatar
Alexandre Julliard committed
1025

1026
    if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1027
    /* GroupBox acts like static control, so it sends CTLCOLORSTATIC */
1028 1029 1030
    parent = GetParent(hwnd);
    if (!parent) parent = hwnd;
    hbr = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)hwnd);
1031
    if (!hbr) /* did the app forget to call defwindowproc ? */
1032
        hbr = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC,
1033
				     (WPARAM)hDC, (LPARAM)hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
1034

1035
    GetClientRect( hwnd, &rc);
1036 1037 1038 1039 1040
    rcFrame = rc;

    GetTextMetricsW (hDC, &tm);
    rcFrame.top += (tm.tmHeight / 2) - 1;
    DrawEdge (hDC, &rcFrame, EDGE_ETCHED, BF_RECT | ((style & BS_FLAT) ? BF_FLAT : 0));
Alexandre Julliard's avatar
Alexandre Julliard committed
1041

1042
    InflateRect(&rc, -7, 1);
1043
    dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rc);
1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057

    if (dtFlags == (UINT)-1L)
       return;

    /* Because buttons have CS_PARENTDC class style, there is a chance
     * that label will be drawn out of client rect.
     * But Windows doesn't clip label's rect, so do I.
     */

    /* There is 1-pixel marging at the left, right, and bottom */
    rc.left--; rc.right++; rc.bottom++;
    FillRect(hDC, &rc, hbr);
    rc.left++; rc.right--; rc.bottom--;

1058
    BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rc);
Alexandre Julliard's avatar
Alexandre Julliard committed
1059 1060 1061 1062 1063 1064 1065
}


/**********************************************************************
 *       User Button Functions
 */

1066
static void UB_Paint( HWND hwnd, HDC hDC, UINT action )
Alexandre Julliard's avatar
Alexandre Julliard committed
1067
{
1068 1069
    RECT rc;
    HBRUSH hBrush;
1070 1071
    HFONT hFont;
    LONG state = get_button_state( hwnd );
1072
    HWND parent;
Alexandre Julliard's avatar
Alexandre Julliard committed
1073 1074

    if (action == ODA_SELECT) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1075

1076
    GetClientRect( hwnd, &rc);
Alexandre Julliard's avatar
Alexandre Julliard committed
1077

1078
    if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1079

1080 1081 1082
    parent = GetParent(hwnd);
    if (!parent) parent = hwnd;
    hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd);
1083
    if (!hBrush) /* did the app forget to call defwindowproc ? */
1084
        hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN,
1085
					(WPARAM)hDC, (LPARAM)hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
1086

1087
    FillRect( hDC, &rc, hBrush );
Alexandre Julliard's avatar
Alexandre Julliard committed
1088
    if ((action == ODA_FOCUS) ||
1089
        ((action == ODA_DRAWENTIRE) && (state & BUTTON_HASFOCUS)))
1090
        DrawFocusRect( hDC, &rc );
Alexandre Julliard's avatar
Alexandre Julliard committed
1091
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1092

Alexandre Julliard's avatar
Alexandre Julliard committed
1093 1094 1095 1096 1097

/**********************************************************************
 *       Ownerdrawn Button Functions
 */

1098
static void OB_Paint( HWND hwnd, HDC hDC, UINT action )
Alexandre Julliard's avatar
Alexandre Julliard committed
1099
{
1100
    LONG state = get_button_state( hwnd );
1101
    DRAWITEMSTRUCT dis;
1102 1103
    HRGN clipRegion;
    RECT clipRect;
1104
    LONG_PTR id = GetWindowLongPtrW( hwnd, GWLP_ID );
1105
    HWND parent;
1106
    HFONT hFont, hPrevFont = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1107

Alexandre Julliard's avatar
Alexandre Julliard committed
1108
    dis.CtlType    = ODT_BUTTON;
1109
    dis.CtlID      = id;
Alexandre Julliard's avatar
Alexandre Julliard committed
1110 1111
    dis.itemID     = 0;
    dis.itemAction = action;
1112 1113 1114 1115
    dis.itemState  = ((state & BUTTON_HASFOCUS) ? ODS_FOCUS : 0) |
                     ((state & BUTTON_HIGHLIGHTED) ? ODS_SELECTED : 0) |
                     (IsWindowEnabled(hwnd) ? 0: ODS_DISABLED);
    dis.hwndItem   = hwnd;
Alexandre Julliard's avatar
Alexandre Julliard committed
1116 1117
    dis.hDC        = hDC;
    dis.itemData   = 0;
1118
    GetClientRect( hwnd, &dis.rcItem );
1119

1120
    clipRegion = CreateRectRgnIndirect(&dis.rcItem);
1121 1122 1123
    if (GetClipRgn(hDC, clipRegion) != 1)
    {
	DeleteObject(clipRegion);
1124
	clipRegion=NULL;
1125 1126
    }
    clipRect = dis.rcItem;
1127
    DPtoLP(hDC, (LPPOINT) &clipRect, 2);
1128 1129
    IntersectClipRect(hDC, clipRect.left,  clipRect.top, clipRect.right, clipRect.bottom);

1130
    if ((hFont = get_button_font( hwnd ))) hPrevFont = SelectObject( hDC, hFont );
1131 1132 1133
    parent = GetParent(hwnd);
    if (!parent) parent = hwnd;
    SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd );
1134
    SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis );
1135
    if (hPrevFont) SelectObject(hDC, hPrevFont);
1136
    SelectClipRgn(hDC, clipRegion);
Alexandre Julliard's avatar
Alexandre Julliard committed
1137
}